为简洁/正确而编辑:滚动到 TL/DR 的末尾
我运行了一个相当长的宏,它从 Workspace/Sharepoint 打开一堆文件,将内容复制到我的 Excel 工作表,将数据加载到数组中,比较数组条目,将数据写入工作表等。它使用 PowerPivot 和 Power Query 并读取/写入几张工作簿中几张纸中的东西。
在寻找代码优化选项的过程中,我发现使用以下行激活工作表使我的代码运行速度提高了 3 倍。我想弄清楚原因和方法,以避免将来出现这种放缓。
ThisWorkbook.Sheets("Dashboard").Activate
我还将“仪表板”更改为另一个新添加的完全空的工作表(工作表(15)),这对运行时间没有影响,这意味着它不会快 3 倍。我认为,在活动工作表上工作有一些效果。但是不,激活新的、空的和未使用的工作表并没有使代码更快。我使用以下宏增强功能:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Run Code Online (Sandbox Code Playgroud)
我不在代码中的任何其他地方使用 .activate。(我 cntrl+f 专门说明了这一点)。.activate 就在一个冗长的 For-Loop 之前,它调用了其他 3 个 subs。在下图中,您可以看到 For 循环的运行时间。
图中开始是指分配一些变量等所花费的时间。工具、序列号和其他是循环期间运行的子程序。这些数字是循环中所有步骤的总和,循环大部分有大约 300 步,可能需要从 0 到 0.3 秒。
以下是部分代码。我将发布 For-Loop 和“check_others”子程序以及它调用的子程序。那是最短的 sub,但正如您在上图中所看到的,即使该 sub 也受到影响,因此发生的事情似乎并不仅仅影响一个 sub。为了便于阅读,我还去掉了循环计时的代码。
'Go from first row of the Prisma report to the last row. (Reference WITHIN the pivot table. e.g: 1 is absolut row 6)
RowCount = ThisWorkbook.Sheets("Prisma").PivotTables("OperationData").DataBodyRange.Rows.Count
PrismaArr = ThisWorkbook.Sheets("Prisma").Range("H6:U" & RowCount + 5)
'It is unclear why and how but without this line, the code takes 3x longer to execute.
ThisWorkbook.Sheets("Dashboard").Activate
For Prismarow = 1 To RowCount
'Set some of the most used variables and progres bar. (Indicator, Comment, Instruction)
progress 13 + (Prismarow / RowCount) * 82, "Analizing"
IndicatorSkipFurtherAnalyses = 0
CommentToBeAnalysed = PrismaArr(Prismarow, 5)
InspectionInstruction = PrismaArr(Prismarow, 6)
'#7 G - Check Tools
Call Check_Tools(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction)
'#8 H - Check serial numbers
If Not IndicatorSkipFurtherAnalyses Like "1" Then _
Call Check_Serial_Numbers(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction)
'#9 I - Check for others (everything else)
If Not IndicatorSkipFurtherAnalyses Like "1" Then _
Call Check_Others(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction)
SkipThisEntry:
Next
Run Code Online (Sandbox Code Playgroud)
调用 Check_Others(PrismaArr、Prismarow、CommentToBeAnalysed、InspectionInstruction):
Sub Check_Others(ByVal PrismaArr, ByVal Prismarow As Long, ByVal CommentToBeAnalysed As String, ByVal InspectionInstruction As String)
'Check if the entry was already deleted, ignore it otherwise
If Not PrismaArr(Prismarow, 7) Like "E D" Then
'Check that all entries on the PDF which are not tools or serial numbers are empty
If PrismaArr(Prismarow, 13) Like "2" And PrismaArr(Prismarow, 10) Like "FALSCH" And PrismaArr(Prismarow, 11) Like "FALSCH" Then
If Not CommentToBeAnalysed Like "" Then
Call Fill_Out_Others_Analysis(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction, 5)
End If
End If
'Check if there should have been a serial number here
If PrismaArr(Prismarow, 10) Like "WAHR" And CommentToBeAnalysed Like "" Then
Call Fill_Out_Others_Analysis(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction, 19)
End If
'Check if there should have been a tool number here
If PrismaArr(Prismarow, 11) Like "WAHR" And CommentToBeAnalysed Like "" Then
Call Fill_Out_Others_Analysis(PrismaArr, Prismarow, CommentToBeAnalysed, InspectionInstruction, 18)
End If
End If
End Sub
Run Code Online (Sandbox Code Playgroud)
调用 Fill_Out_Others_Analysis(PrismaArr、Prismarow、CommentToBeAnalysed、InspectionInstruction、5):
Sub Fill_Out_Others_Analysis(ByVal PrismaArr, _
ByVal Prismarow As Long, _
ByVal CommentToBeAnalysed As String, _
ByVal InspectionInstruction As String, _
ByVal Error_Code As String)
'Write the given information into the analysis sheet
With ThisWorkbook.Sheets("Analysis").ListObjects("Analysis_Others").ListRows.Add
.Range.ClearFormats
.Range(1, 1) = PrismaArr(Prismarow, 1)
.Range(1, 2) = Prismarow + 5 & " (" & PrismaArr(Prismarow, 2) & ")"
.Range(1, 3) = CommentToBeAnalysed
.Range(1, 4) = InspectionInstruction
.Range(1, 6) = "----"
.Range(1, 7) = "----"
.Range(1, 8) = "----"
.Range(1, 9) = "----"
.Range(1, 10) = "----"
.Range(1, 11) = "----"
'Assign errors, including coloring and statistical error assignment
Call AssignError(Error_Code, .Range)
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
此时它调用“AssignError”,这太长而无法真正发布。但即使我注释掉那个 Call,宏仍然可以通过使用 .activate 来加速。
此外,如果我启动宏,然后在打开和关闭 Excel 工作表以收集数据时,但在 For-Loop 开始之前,单击退出 excel,我也可以类似地加速宏。我只需单击 Windows 按钮并让开始菜单保持打开状态,同时我看到我的进度条填充速度比平时快 3 倍。事实上,这具有更好的性能,即使我使用 .activate 额外约 20% (16s->13s) 也能加快程序速度。这并不让我感到惊讶。但是对于 .activate 行为,我根本没有解释。
有人可以理解这一点吗?
TL/DR:有没有人知道在宏中间的 worksheet.activate 如何可能将执行速度提高 3 倍。即添加 worksheet.activate 使我的代码在 1/3 的时间内完成运行,否则需要花费的时间.
谢谢丹尼斯
我有几个想法,不过,我也在钓鱼。
1)图形更新仍在进行。 即使您已将 Application.ScreenUpdating 设置为 False,在更新工作表 Analysis 上的子 Fill_Out_Others_Analysis 中的单元格时,它是否仍在更新/跟踪图形?活动开始菜单上的更快速度也表明了这一点。检查这一点的方法是比较 VBA 在不写入值的情况下运行时的时间(启用 .activate 行与禁用 .activate 行)(禁用该块 With ThisWorkbook.Sheets("Analysis").ListObjects("Analysis_Others").ListRows .Add),因为工作表上的值(以及图形内存中的值)不会一直改变。我在这里假设当您禁用 .Activate 行时,分析表是活动表 - 但是您提到当您有一个空表处于活动状态时也存在相同的差异,所以我认为这可能不会产生影响。
2)较短的数据参考。 是否有通过仪表板表运行的任何参考?正如在指向其他工作表的公式或表格中,或者从仪表板工作表获取数据的数据透视表中一样:在 VBA 期间通过 [thisworkbook --> 当前活动工作表] 获取数据可能比 [thisworkbook --> 查找工作表 --> 更快> 仪表板表中的数据]。起初,我以为你的数据透视表在仪表板表上,但再次阅读它在另一张表上。所以也不太可能。
3)导致差异的不是Dashboard工作表本身,而是Dashboard工作表的当前工作表索引。 我不知道为什么,但我记得读过一两次,如果工作簿包含大量带有公式的单元格/当计算链位于工作簿的背面(在工作簿的最后一张工作表上),工作簿会更快。这表明 excel-speed 与工作表索引相关,因此速度差异不是因为仪表板工作表本身,而是因为仪表板工作表现在(例如)是工作簿的第一张工作表。尝试将仪表板工作表移至当前所在的另一端(如果现在是工作表 (1),请将其移至工作表 (15)),激活工作表 1 并再次运行速度测试。如果 VBA 现在在另一张工作表处于活动状态时速度更快,它仍然无法回答您的原因问题,但至少有一个领先。
此外,为了追求更快的 VBA: 正如名称所示,仪表板表显示“分析”选项卡中的内容。随着“分析”选项卡中的表在 VBA 期间反复更新,对该表的引用也会更新(仪表板选项卡也是如此)。首先将分析写入内存(写入数组),然后在 VBA 结束时将其写入表可能会更快:这样表大小及其所有引用只需更改一次,而不是每次都更改添加 1 行。
| 归档时间: |
|
| 查看次数: |
378 次 |
| 最近记录: |