patternMinor
Increasingly Long Runtime for Macro
Viewed 0 times
runtimelongincreasinglymacrofor
Problem
My code works, but the problem is that it is taking an increasingly long time to run, with the time required to complete calculations increasing every time I use the macro. I've tried a variety of variations and modifications with the syntax, but as I'm pretty new to VBA, I haven't made a whole lot of progress.
Here's the code I'm running (Note, it is running as a subset, and
This code basically removes zero-valued results from the data by deleting an entire row. Initially, it ran in about 12 seconds, but that soon became 55 second, which has progressed into increasing long runtimes, with a 'fast' now being in the 5 minuet range. Below is a spreadsheet with the recorded runtimes and corresponding changes made:
`Runtime Changes
6:30 None
7:50 None
5:37 Manually stepped through code
7:45 Run with .cells instead of .range("B1:B" & lastRow)
5:21 Run with .Range(B:B) instead of .range("B1:B" & lastRow)
9:20 Run with application.calculation disabled/enabled, range unchanged
5:35 Run with application.enableEvents disabled/enabled, range unchanged
11:08 Run with application.enableEvents disabled/enabled, Range(B:B)
5:12 None
7:57 Run with Alternative code (old code)
5
Here's the code I'm running (Note, it is running as a subset, and
ScreenUpdate = False):Public Sub deleteRows()
Dim lastRow As Long
Dim rng As Range
With ActiveSheet
.AutoFilterMode = False
lastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
'~~> Set the range of interest, no need to include the entire data range
With .Range("B2:F" & lastRow)
.AutoFilter Field:=2, Criteria1:="=0.000", Operator:=xlFilterValues
.AutoFilter Field:=5, Criteria1:="=0.000", Operator:=xlFilterValues
End With
.Range("B1:F" & lastRow).SpecialCells(xlCellTypeVisible).EntireRow.Delete
.AutoFilterMode = False
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
End With
MsgBox Format(Time - start, "hh:mm:ss")
End SubThis code basically removes zero-valued results from the data by deleting an entire row. Initially, it ran in about 12 seconds, but that soon became 55 second, which has progressed into increasing long runtimes, with a 'fast' now being in the 5 minuet range. Below is a spreadsheet with the recorded runtimes and corresponding changes made:
`Runtime Changes
6:30 None
7:50 None
5:37 Manually stepped through code
7:45 Run with .cells instead of .range("B1:B" & lastRow)
5:21 Run with .Range(B:B) instead of .range("B1:B" & lastRow)
9:20 Run with application.calculation disabled/enabled, range unchanged
5:35 Run with application.enableEvents disabled/enabled, range unchanged
11:08 Run with application.enableEvents disabled/enabled, Range(B:B)
5:12 None
7:57 Run with Alternative code (old code)
5
Solution
This won't solve the long run times of your code if the issue is the
That being said your code should be reviewed.
Never use
As you said, this is being called as a sub function. If your project grows to include more sheets, assuming ActiveSheet is the sheet you want to manipulate won't be safe. Any Sub not meant to be an outermost Sub should not use
This variable isn't being used. Delete it.
You get the last row by going to the bottom of the sheet and iterating up until you find the bottom of your data.
Instead get the bottom of the range containing data:
Now there is the general steps that your macro takes. It operates like user interaction, which is usually not the best way to approach it. The steps you take:
It makes perfect sense in a user interface side but from a programming side is unnecessarily indirect. Rows to delete are marked by remaining visible and then deleted. What if something else has marked rows as invisible? I would suggest iterating over all rows in the range and deleting those that should be.
Pulling bits out like
Sorting Optimizations
If sorting the rows of the table is allowed and 0 is the minimum then you could sort the table and only iterate over the rows that you need to delete.
EntireRow.Delete method. I assume you still want those rows deleted and stackoverflow would be the place to get a workaround or solution.That being said your code should be reviewed.
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAboveNever use
Select unless you are making some macro to find a range for the user. Use Rows("1:1").Insert .... Making these concatenations should be priority #1 after recording a macro. Also you are not declaring the parent of Rows. ActiveSheet is implicitly the parent but Rows is already nested in a With statment. Just put the period .Rows("1:1")... to make it explicit.With ActiveSheetAs you said, this is being called as a sub function. If your project grows to include more sheets, assuming ActiveSheet is the sheet you want to manipulate won't be safe. Any Sub not meant to be an outermost Sub should not use
ActiveSheet, ActiveBook, ActiveCell, or Selection. Sheets, books, and ranges should be passed as an argument.This variable isn't being used. Delete it.
Dim rng As RangeYou get the last row by going to the bottom of the sheet and iterating up until you find the bottom of your data.
lastRow = .Cells(.Rows.Count, 2).End(xlUp).RowInstead get the bottom of the range containing data:
lastRow = .UsedRange.Rows.CountNow there is the general steps that your macro takes. It operates like user interaction, which is usually not the best way to approach it. The steps you take:
- AutoFilter the range
- only rows with columns "C" and "F" are visible
- Delete all visible rows in the range
It makes perfect sense in a user interface side but from a programming side is unnecessarily indirect. Rows to delete are marked by remaining visible and then deleted. What if something else has marked rows as invisible? I would suggest iterating over all rows in the range and deleting those that should be.
Public Sub DeleteRows()
' ActiveSheet or Range("B2:F" & lastRow) should be passed
Dim sheet as Worksheet
set sheet = ActiveSheet
Dim lastRow As Long
lastRow = sheet.UsedRange.Rows.Count
Dim table as Range
set table = sheet.Range("B2:F" & lastRow)
Dim l as Long
For l = lastRow to 1 step -1
If ShouldBeDeleted(table.rows(l)) Then
table.rows(l).EntireRow.Delete shift:=xlUp
End If
Next l
' These should be in the outside Sub
sheet.Range("1:1").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
MsgBox Format(Time - start, "hh:mm:ss")
End Sub
Function ShouldBeDeleted(row_range as Range) as Boolean
ShouldBeDeleted = (row_range.cells(1, 2) = 0 And row_range.cells(1, 5) = 0)
End FunctionPulling bits out like
ShouldBeDeleted might seem verbose but there is a better name for it. There is something special about those rows, and I would rename the function IsX where X describes what those rows are. Also, columns "C" and "F" seem to be special for your worksheet. If they are so, declare them as constantsConst IMPORTANT_COL_1 As String = "C"
Const IMPORTANT_COL_2 As String = "F"Sorting Optimizations
If sorting the rows of the table is allowed and 0 is the minimum then you could sort the table and only iterate over the rows that you need to delete.
Public Sub DeleteRows(table as Range)
Dim lastRow As Long
lastRow = table.Rows.Count
With table.Parent.Sort
.SortFields.Clear
.SortFields.Add key:=table.Range(IMPORTANT_COL_1 & ":" & IMPORTANT_COL_1)
.SortFields.Add key:=table.Range(IMPORTANT_COL_2 & ":" & IMPORTANT_COL_2)
.SetRange table
.Apply
End With
Dim botRow as Long
botRow = 1
While ShouldBeDeleted(table.rows(botRow))
botRow = botRow + 1
Wend
table.range("1:" & botRow).Delete shift:=xlUP
End SubCode Snippets
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAboveWith ActiveSheetDim rng As RangelastRow = .Cells(.Rows.Count, 2).End(xlUp).RowlastRow = .UsedRange.Rows.CountContext
StackExchange Code Review Q#59260, answer score: 6
Revisions (0)
No revisions yet.