【问题标题】:Why does activating specific sheets affect run time of my macro为什么激活特定工作表会影响我的宏的运行时间
【发布时间】:2020-01-23 13:41:09
【问题描述】:

为简洁/正确而编辑:滚动到末尾以获取 TL/DR

我运行了一个相当长的宏,它从 Workspace/Sharepoint 打开一堆文件,将内容复制到我的 Excel 工作表中,将数据加载到数组中,比较数组条目,将数据写入工作表等。它使用 PowerPivot 和 Power Query 以及在多个工作簿的多个工作表中读取/写入内容。

在寻找代码优化选项期间,我发现使用以下行激活工作表可使我的代码运行速度提高 3 倍。我想弄清楚为什么以及如何,以避免将来出现这种减速。

ThisWorkbook.Sheets("Dashboard").Activate

我还将“仪表板”更改为另一个新添加的完全空的工作表 (sheets(15)),这对运行时间没有影响,这意味着它的速度不会快 3 倍。我认为,在活动工作表上工作会产生一些影响。但是不,激活新的、空的和未使用的工作表并没有使代码更快。我使用以下宏增强功能:

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual

我不在代码中的其他任何地方使用 .activate。 (我专门cntrl + f'ed)。 .activate 就在一个冗长的 For-Loop 之前,它调用了 3 个其他潜艇。在下图中,您可以看到 For-Loop 的运行时间。

See the image for some run times in seconds

图中的开始是指分配一些变量等所花费的时间。工具、序列号和其他是循环期间运行的子程序。这些数字是循环期间所有步骤的总和,循环主要有大约 300 步,可能需要从 0 到 0.3 秒。

以下是部分代码。我将发布 For-Loop 和“check_others”子以及它调用的子。那是最短的潜艇,但正如您在上图中看到的那样,即使那个潜艇也受到影响,所以发生的事情似乎不仅仅影响一个潜艇。为了便于阅读,我还去掉了计时循环的代码。

'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

调用 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

调用 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

此时它调用“AssignError”,它太长而无法真正发布。但即使我注释掉那个调用,宏仍然可以通过使用 .activate 来加速。

同样,如果我启动宏,然后在打开和关闭 Excel 表以收集数据时,但在 For-Loop 开始之前,单击退出 excel,我也可以类似地加速宏。我只需单击 Windows 按钮并让开始菜单保持打开状态,同时我会看到进度条以比平时快 3 倍的速度填满。事实上,这具有更好的性能,即使在我使用 .activate 时也可以将程序速度提高约 20% (16s->13s)。这并不让我感到惊讶。但是对于 .activate 行为,我根本没有解释。

有人能理解吗?

TL/DR: 有没有人知道宏中间的 worksheet.activate 如何可能将执行速度提高 3 倍。即添加 worksheet.activate 使我的代码在 1/3 的时间内完成运行。

谢谢 丹尼斯

【问题讨论】:

  • 来看看For
  • 感谢您的关注,我提供了更多信息。希望它不会太混乱。
  • 我的想法是这与加载工作表准备写入它有关,但您的代码甚至不会写入或读取仪表板。可能与在进程队列中赋予它更高的优先级有关,但老实说,这几乎只是在黑暗中刺伤。
  • 是的,这就是我尝试新的/空的工作表的原因之一。你知道另一种测试方法吗?有没有办法强制更高的优先级来测试另一个假设? (我不确定你指的是什么,我只知道windows中的优先级设置,而不是excel中的)
  • 如果您改为激活“prisma”会发生什么?

标签: excel vba


【解决方案1】:

我有几个想法,但是,我也在钓鱼。

1) 图形更新仍在进行中。 即使您已将 Application.ScreenUpdating 设置为 False,在更新工作表 Analysis 上的子 Fill_Out_Others_Analysis 中的单元格时,它是否仍在更新/跟踪图形?活动开始菜单上的更快速度也表明了这一点。检查这一点的一种方法是比较 VBA 在不写入值的情况下运行的时间(启用与禁用 .activate 行)(使用 ThisWorkbook.Sheets("Analysis").ListObjects("Analysis_Others").ListRows 禁用块) .Add)作为工作表上的值(因此在图形内存中)不会一直更改。我在这里假设当您禁用 .Activate 行时分析表是活动表 - 但是您提到当您有一个空表处于活动状态时存在相同的差异,所以我认为它可能不会有所作为。

2) 更短的数据引用。 是否有任何参考通过仪表板表运行?与指向其他工作表的公式或表格,或从仪表板工作表获取数据的数据透视表一样:在 VBA 期间通过 [thisworkbook --> current active sheet] 获取数据可能比 [thisworkbook --> find worksheet -- > 仪表板表中的数据]。起初,我以为您的数据透视表在仪表板工作表上,但再次阅读它却在另一个工作表上。所以也不太可能。

3) 造成差异的不是仪表板工作表本身,而是仪表板工作表的当前工作表索引。 我不知道为什么,但我记得读过一次或两次,如果工作表中有很多带有公式的单元格/当计算链位于工作簿的背面(在工作簿的最后一页上),工作簿会更快。这表明 excel-speed 与 sheetindexes 相关,因此速度差异不是因为 Dashboard 工作表本身,而是因为 Dashboard 工作表现在(例如)工作簿的第一个工作表。尝试将仪表板工作表移动到它现在所在的另一端(如果现在是工作表(1),请将其移动到工作表(15)),激活工作表 1 并再次运行速度测试。如果 VBA 现在在另一个工作表处于活动状态的情况下速度更快,它仍然无法回答您的问题,但至少有一个领先优势。

此外,为了追求更快的 VBA: 正如名称所述,仪表板工作表显示分析选项卡中的内容。在 VBA 期间,分析选项卡中的表被重复更新,对该表的引用也是如此(仪表板选项卡也是如此)。首先将分析写入内存(写入数组)然后在 VBA 结束时将其写入表可能会更快:这样表大小及其所有引用只需更改一次,而不是每次更改添加了 1 行。

【讨论】:

  • 非常感谢您的回答!很用心的回复。 1.)我尝试一次将一个数组写入一个范围,我也尝试不写任何东西。除了预期的提速外,3 倍的提速依然存在。 2.) Dashboard 表几乎没有参考。它只是一个漂亮的东西,让人们设置一些选项,然后单击一个按钮来启动宏。它在宏开始时被引用,但在 For 循环期间不被引用。
  • 3.) 我刚试过,我用各种方式移动床单,按名称和编号引用它们,在中间或最后引用床单,但我没有看到任何变化。但我感谢您的意见。我需要多玩一点,这似乎是一个有趣的想法。也许我错过了一些东西。另外,很抱歉回复晚了。工作交给了我,我还有其他职责要处理。如果我因为您的输入而弄清楚了,我会将您的输入标记为答案。
  • 感谢您花时间阅读您的帖子并回复它。仍然想知道它可能是什么我今天偶然发现了一篇关于通过将 ActiveSheet.DisplayPageBreaks 设置为 false 来加速 vba 的文章。这感觉就像是您正在寻找的答案,因为您的仪表板表可能有分页符。见docs.microsoft.com/en-us/office/vba/excel/concepts/…
  • 哈哈,看来你脑子里也还在想这个问题。在当天晚些时候的几次会议之后,我将有时间对此进行测试。但是我不太自信,因为我在所有工作表上都禁用了分页符(至少它们没有显示),但它们在后台可能会发生一些奇怪的事情。我会在会议结束后回复你。谢谢你坚持!我知道我还没有接受答案,但你应该得到赏金!我将在 2 月的最后一周对此进行深入研究。
  • 出于某种原因,即使我复制粘贴您的姓名,我也无法使用“(at)l3ob”。 Anywho:遗憾的是,这并没有改变宏速度。我使用:ActiveSheet.DisplayPageBreaks = False 而不是之前使用的行:ThisWorkbook.Sheets("Dashboard").Activate 并且执行时间仍然很长。
【解决方案2】:

根据您的 OP,工作表 Sheets("Dashboard") 之间的唯一(明显)区别 而Sheets(15) 是前者填充了一些数据、公式等。

如果没有关于Dashboard 内容的更多详细信息,很难说速度差异的原因是什么,但当然可以通过在代码中插入更多经过时间的检查点来设置更精细的测试。这将帮助找出罪魁祸首。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-15
    • 2019-02-10
    • 1970-01-01
    • 2017-03-23
    • 1970-01-01
    • 2015-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多