I have learnt the hard way over many years ways of speeding up VBA code. Mostly these lessons have come from necessity – the old “go and have a cup of coffee while it runs” comment doesn’t always endear your code to the customer. We have in the past written thousands of lines of code that sometimes takes minutes or even 10s of minutes to run.
Some of the quicker ways to speed things up are in turning off things in excel like calculation, screen updating and the like. Others involve using arrays in code to handle slabs of data and manipulate them in code before returning the results back to the spreadsheet. Others are little things like not selecting ranges etc before using them.
Chad Rothschiller has written a great article on Excel VBA Performance Coding Best Practices going through all these items and explaining how to optimise your code for efficiency. These are beginning tasks. There are certainly lot more complex ways to deal with some of these things but for anybody who started writing VBA by using the record macro function, as I did way back in the early 90’s these tips certainly allow you to speed up some of the very inefficient code that that method creates.
I have highlighted some of them here and added some additional notes.
Here's some sample code that shows how and what to shut off while your code runs. Doing this should help improve the performance of your code:
'Get current state of various Excel settings; put this at the beginning of your code
screenUpdateState = Application.ScreenUpdating
statusBarState = Application.DisplayStatusBar
calcState = Application.Calculation
eventsState = Application.EnableEvents
displayPageBreakState = ActiveSheet.DisplayPageBreaks 'note this is a sheet-level setting
'turn off some Excel functionality so your code runs faster
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False 'note this is a sheet-level setting
'>>your code goes here<<
'after your code runs, restore state; put this at the end of your code
Application.ScreenUpdating = screenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
ActiveSheet.DisplayPageBreaks = displayPageBreaksState 'note this is a sheet-level setting
Note – these items are all good time savers. however some of them may not be very useful to you. Chad has written a description of each item and what it does on the MSDN blog site. We routinely use screenupdating as it is the single biggest time saver other than calculate here. Some of the others we do not need to use. Sometimes turning off calculate has a detrimental affect as you require calculation to to be turned on to ensure that values are changed. However if necessary you can add a calculation command such as ActiveWorkbook.Calculate or ActiveSheet.Calculate or Sheets(thissheet).Calculate when you know you need values to be updated.
Chad also writes about using arrays to manipulate sheet data by copying data from the sheet to an array and then manipulating it and copying it back. We do this all the time as it is way faster that manipulating on the sheet directly. He provides some sample code to test this as well.
Finally he writes about not selecting items directly but referencing them directly.
A previous Excel blog entry by Dany Hoter also compares these two methods, along with a selection / offset method as well: http://blogs.msdn.com/excel/archive/2008/10/03/what-is-the-fastest-way-to-scan-a-large-range-in-excel.aspx
...which leads me to my next point.
Avoid Selecting / Activating Objects
Notice that in the above-referenced blog post, the selection method of updating a range was the slowest. This next optimization minimizes how frequently Excel has to respond to the selection changing in the workbook by minimizing the selection changing as much as possible.
Range Example: Again, see the Excel blog post quoted above. It demonstrates that using selection is the slowest of the 3 methods discussed for reading and writing to ranges.
Shapes Example: Setup: I have 40 shapes on a sheet, and I want to write "Hello" in each of them.
Using the slower "selection" method, the code looks like this:
For i = 0 To ActiveSheet.Shapes.Count
Selection.Text = "Hello"
The much faster method is to avoid selection completely and directly reference the shape:
For i = 0 To ActiveSheet.Shapes.Count
ActiveSheet.Shapes(i).TextEffect.Text = "Hello"
The concepts illustrated by the examples above can also be applied to objects other than Ranges and Shapes.
There are some other best practices as well.Chad alludes to using the Option Explicit command at the top of every module, requiring you to declare and use your variables explicitly. Running the Debug Compile command from the VBE menu also helps locate potential errors.
We try and use good naming conventions as well as lots of code comments for both ourselves and other users who need to understand at a glance what is happening in the code.
I recommend you read Chads full article.