The Analyst's Path

Speed up VBA code

Speeding up VBA code means making a macro touch the worksheet less often. Every time code reads or writes a cell, Excel may redraw the screen and recalculate the workbook. The first fix is the speed trio, three Application settings you switch off for the heavy work and restore afterwards. The second is to do the work in an array in memory, with one read from the sheet and one write back.

3 min readPart of VBA III: UserForms, Files & SpeedOpen in course mode

Advertisement

Why do macros crawl? VBA itself is almost never the reason. A slow macro is slow because it talks to the worksheet too often. Every time code reads or writes a cell, Excel may redraw the screen and recalculate the workbook. It may fire events too, and it does all that per cell. A loop that writes 10,000 cells one at a time can trigger tens of thousands of redraws and recalcs. The fix comes in two parts.

Part one is the speed trio. Wrap any bulk operation in it:

Application.ScreenUpdating = False      ' stop repainting until done
Application.Calculation = xlManual       ' stop recalcing on every write
Application.EnableEvents = False         ' stop firing Worksheet_Change etc.
'   ... the heavy work ...
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
Application.EnableEvents = True

These three lines routinely cut a macro's run time by 5 to 20×. They cost you nothing except remembering to restore them in an error handler, because a crash with ScreenUpdating still off leaves the user staring at a frozen screen. ScreenUpdating is the big visual win. Calculation = xlManual is the big win on a formula-heavy model, as long as you set it back to automatic and force one Application.Calculate at the end. EnableEvents = False speeds things up as well, and it stops your writes from tripping the very Worksheet_Change handlers you wrote in VBA II.

Advertisement

Why are arrays faster than looping over cells?

Part two, arrays over cells, is the bigger prize. Even with the trio off, touching cells one at a time crosses the boundary between VBA and Excel's grid on every access, and that crossing is the expensive part. Professionals read the whole range into a Variant array in one hit, compute entirely in memory, and write it back in one assignment:

Dim data As Variant, i As Long
data = ws.Range("A2:A10001").Value        ' ONE read: 10,000 cells -> a 2-D array
For i = 1 To UBound(data, 1)
    data(i, 1) = data(i, 1) * 1.05        ' 5% uplift, in memory only
Next i
ws.Range("A2:A10001").Value = data        ' ONE write: array -> 10,000 cells

Now count the boundary crossings. This version touches the worksheet twice, once for the bulk read and once for the bulk write. The cell-by-cell loop touches it 20,000 times, since every cell gets a read and a write. On a 10,000-row column, that one change is the difference between "instant" and "watch the progress bar." Remember the array's shape from VBA II. Range.Value always yields a 1-based, two-dimensional array (data(row, col)), even for a single column, hence data(i, 1) and UBound(data, 1). Put a number on it. A base of $2,200 uplifted 5% becomes 2200 × 1.05 = $2,310.00, computed in the array and written back with the other 9,999 rows in one stroke.

Advertisement

Part of the course

This page is part of VBA III: UserForms, Files & Speed (EX6.03), in Phase 6: Investing philosophy, mental models and behavioural edge. The module's flashcards and its quiz are in the app.