Speed of writing a 2D dictionary on first run slow

KillGorack

New Member
Joined
Jan 23, 2006
Messages
35
Office Version
  1. 2016
Platform
  1. Windows
Hi,

I have a macro that just grabs data from one sheet, does a little formatting, and places that data into another..

In the attachment the first column is the time it takes to read the data into a 2D dictionary, and the second column is the time it takes to place it into ThisWorkbook

the code below is the portion where it's written.

Runs slow as heck the first time, and quickly after.. I think it's a cache thing, but don't know how to get around it.

Any ideas on how to speed this up on first use?

VBA Code:
        s = Timer()
        xcursor = 2
        For Each pkey In topics.Keys
            ycursor = 1
            For Each ckey In topics(pkey)
                If ckey = 0 Then
                    ThisWorkbook.Sheets("Data").Range("A" & xcursor & ":T" & xcursor).Interior.ColorIndex = topics(pkey)(ckey)
                Else
                    ThisWorkbook.Sheets("Data").Cells(xcursor, ycursor - 1).value = topics(pkey)(ckey)
                End If
                ycursor = ycursor + 1
            Next ckey
            xcursor = xcursor + 1
        Next pkey
        timertempheight = ThisWorkbook.Sheets("Timer").Cells(Rows.Count, 2).End(xlUp).Row
        ThisWorkbook.Sheets("Timer").Cells(timertempheight + 1, 2).value = Timer() - s
 

Attachments

  • timer.PNG
    timer.PNG
    7 KB · Views: 4

Excel Facts

Does the VLOOKUP table have to be sorted?
No! when you are using an exact match, the VLOOKUP table can be in any order. Best-selling items at the top is actually the best.
I "think" we're on to something... I usually run this sub before and after each complicated task...

I added the "Application.Cursor" line after noticing when I have my cursor NOT over excel it runs faster..

So far it rings true.. very weird..

VBA Code:
Function calcEvents(state As Boolean)

    If state = True Then
    
        Application.EnableEvents = True
        Application.Calculation = xlAutomatic
        Application.ScreenUpdating = True
        Application.DisplayStatusBar = True
        ThisWorkbook.Sheets("data").DisplayPageBreaks = True
        Application.Cursor = xlDefault
        
    ElseIf state = False Then
    
        Application.EnableEvents = False
        Application.Calculation = xlManual
        Application.ScreenUpdating = False
        Application.DisplayStatusBar = False
        ThisWorkbook.Sheets("data").DisplayPageBreaks = False
        Application.Cursor = xlNorthwestArrow
        
    End If

End Function
 

Attachments

  • timer.PNG
    timer.PNG
    8.9 KB · Views: 3
Upvote 0
You code is slow becaue you are writing to the worksheet every iteration inside a double loop, this is always going to be slow. To speed it up you need to write the value out to a variant array and then write the array out at the end. Unfortunatly formatting does invovle writing to the worksheet. So you do need to do that.
What is the range of the workhseet that you are operating this on, is it constant? Something like this:
VBA Code:
xmax = 300
ymax = 40
inarr = ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax))
      s = Timer()
        xcursor = 2
        For Each pkey In topics.Keys
            ycursor = 1
            For Each ckey In topics(pkey)
                If ckey = 0 Then
                    ThisWorkbook.Sheets("Data").Range("A" & xcursor & ":T" & xcursor).Interior.ColorIndex = topics(pkey)(ckey)
                Else
'                    ThisWorkbook.Sheets("Data").Cells(xcursor, ycursor - 1).Value = topics(pkey)(ckey)
                    inarr(xcursor, ycursor - 1) = topics(pkey)(ckey)
                End If
                ycursor = ycursor + 1
            Next ckey
            xcursor = xcursor + 1
        Next pkey
     
ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax)) = inarr
        timertempheight = ThisWorkbook.Sheets("Timer").Cells(Rows.Count, 2).End(xlUp).Row
        ThisWorkbook.Sheets("Timer").Cells(timertempheight + 1, 2).Value = Timer() - s
 
Last edited:
Upvote 0
Solution
You code is slow becaue you are writing to the worksheet every iteration inside a double loop, this is always going to be slow. To speed it up you need to write the value out to a variant array and then write the array out at the end. Unfortunatly formatting does invovle writing to the worksheet. So you do need to do that.
What is the range of the workhseet that you are operating this on, is it constant? Something like this:
VBA Code:
xmax = 300
ymax = 40
inarr = ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax))
      s = Timer()
        xcursor = 2
        For Each pkey In topics.Keys
            ycursor = 1
            For Each ckey In topics(pkey)
                If ckey = 0 Then
                    ThisWorkbook.Sheets("Data").Range("A" & xcursor & ":T" & xcursor).Interior.ColorIndex = topics(pkey)(ckey)
                Else
'                    ThisWorkbook.Sheets("Data").Cells(xcursor, ycursor - 1).Value = topics(pkey)(ckey)
                    inarr(xcursor, ycursor - 1) = topics(pkey)(ckey)
                End If
                ycursor = ycursor + 1
            Next ckey
            xcursor = xcursor + 1
        Next pkey
    
ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax)) = inarr
        timertempheight = ThisWorkbook.Sheets("Timer").Cells(Rows.Count, 2).End(xlUp).Row
        ThisWorkbook.Sheets("Timer").Cells(timertempheight + 1, 2).Value = Timer() - s
with constant height not so much but I can get that size from the dictionary.. I will try this, and use it going forward with the nested arrays.. I'll report back. Thanks!
 
Upvote 0
You code is slow becaue you are writing to the worksheet every iteration inside a double loop, this is always going to be slow. To speed it up you need to write the value out to a variant array and then write the array out at the end. Unfortunatly formatting does invovle writing to the worksheet. So you do need to do that.
What is the range of the workhseet that you are operating this on, is it constant? Something like this:
VBA Code:
xmax = 300
ymax = 40
inarr = ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax))
      s = Timer()
        xcursor = 2
        For Each pkey In topics.Keys
            ycursor = 1
            For Each ckey In topics(pkey)
                If ckey = 0 Then
                    ThisWorkbook.Sheets("Data").Range("A" & xcursor & ":T" & xcursor).Interior.ColorIndex = topics(pkey)(ckey)
                Else
'                    ThisWorkbook.Sheets("Data").Cells(xcursor, ycursor - 1).Value = topics(pkey)(ckey)
                    inarr(xcursor, ycursor - 1) = topics(pkey)(ckey)
                End If
                ycursor = ycursor + 1
            Next ckey
            xcursor = xcursor + 1
        Next pkey
    
ThisWorkbook.Sheets("Data").Range(Cells(1, 1), Cells(xmax, ymax)) = inarr
        timertempheight = ThisWorkbook.Sheets("Timer").Cells(Rows.Count, 2).End(xlUp).Row
        ThisWorkbook.Sheets("Timer").Cells(timertempheight + 1, 2).Value = Timer() - s
This is something I'll add to my arsenal going forward, between the cursor graphic slowing this down, and the loop fix you've provided..

Thanks again.
 

Attachments

  • timer.PNG
    timer.PNG
    7.3 KB · Views: 3
Upvote 0
code if you're interested...

VBA Code:
        xmax = topics.Count + 1
        ymax = 20
        With ThisWorkbook.Sheets("Data")
            inarr = .Range(.Cells(1, 1), .Cells(xmax, ymax))
            s = Timer()
            xcursor = 2
            For Each pkey In topics.Keys
                ycursor = 1
                For Each ckey In topics(pkey)
                    If ckey = 0 Then
                        .Range("A" & xcursor & ":T" & xcursor).Interior.ColorIndex = topics(pkey)(ckey)
                    Else
                        inarr(xcursor, ycursor - 1) = topics(pkey)(ckey)
                    End If
                    ycursor = ycursor + 1
                Next ckey
                xcursor = xcursor + 1
            Next pkey
            .Range(.Cells(1, 1), .Cells(xmax, ymax)) = inarr
        End With
        timertempheight = ThisWorkbook.Sheets("Timer").Cells(Rows.Count, 2).End(xlUp).Row
        ThisWorkbook.Sheets("Timer").Cells(timertempheight + 1, 2).value = Timer() - s
 
Upvote 0
In my experience the sort of improvement you have got from 35 seconds to less than a second is typical of what can be achieved by using variant arrays instead of looping through cells in a range, and usually it is very easy to do!! As per your solution.
I very rarely bother about turning calculation and screenupdate off because doing it all in a varinat array it doesn't update the worksheet, so no calculations or screen updates. In your case becasue you are updating the formatting it will make a difference
 
Upvote 0

Forum statistics

Threads
1,215,029
Messages
6,122,760
Members
449,095
Latest member
m_smith_solihull

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top