13

The accepted wisdom is that using a construct like Dim dict As New Dictionary is poorer in performance than Dim dict As Dictionary / Set dict = New Dictionary.

The explanation is that the former example - auto-instantiation - defers instantiation until the first usage of the variable dict. And thus, every time dict is referenced, the compiled code must first check whether dict is equal to Nothing.

But it occurs to me that compiled code does this anyway. You will get an error any time you try to make use of an object reference that is Nothing.

So, in tribute to science, I ran some tests. And the results suggest there is no performance difference between the two approaches. (Run on Excel 2007)

Call "create dictionary & add 2 items" 100,000 times.

  • Explicit: 16,891ms / Auto: 16,797ms (Auto 94ms faster)
  • Explicit: 16,797ms / Auto: 16,781ms (Auto 16ms faster)

Reverse the order of test calls:

  • Auto: 16,766ms / Explicit: 16,812ms (Auto 46ms faster)
  • Auto: 16,828ms / Explicit: 16,813ms (Explicit 15ms faster)

Call "create dictionary & add 6 items" 100,000 times.

  • Auto: 17,437ms / Explicit: 17,407ms (Explicit 30ms faster)
  • Auto: 17,343ms / Explicit: 17,360ms (Auto 17ms faster)

Create dictionary and add 100,000 items.

  • Auto: 391ms / Explicit: 391ms (Same)

Create dictionary and add 1,000,000 items.

  • Auto: 57,609ms / Explicit: 58,172ms (Auto 563ms faster)
  • Explicit: 57,343ms / Auto: 57,422ms (Explicit 79ms faster)

I see nothing to indicate that auto-instantiation is a poor performing relation to explicit instantiation. (To be clear, for other reasons, I would avoid auto-instantiation but I'm just interested in the performance angle here.)

So is this a myth?

UPDATE

Let me lay out why the performance argument doesn't make sense to me. It is said that

x.Add("Key", "Item")

in an auto-instantiated object is equivalent to the following:

If x is Nothing then
    Set x = New Dictionary
End If
x.Add("Key", "Item")

which makes it look like "frightening overhead" if you're calling this thousands of times. But in the explicit instantiation case, it's exactly the form of logic generated in the compiled version of the code:

If x is Nothing Then
    Err.Raise "Object variable not set..."
End If
x.Add("Key", "Item")

It doesn't necessarily follow that auto is longer, which is why I'm asking whether there was any truth to this. I wonder if I've identified another one of the many untested performance myths.

Community
  • 1
  • 1
Joel Goodwin
  • 5,026
  • 27
  • 30
  • It really is only the different "Nothing" semantics that sets `As New` slightly apart from `Dim/Set`. As you say the so-called overhead involving a runtime type check is irrelevant given that unless the checked auto-instanced object actually *is* `Nothing`, no more happens than if it were a `Dim/Set` reference. – Alex K. Dec 13 '11 at 14:09
  • @Alex K, this age-old performance guideline says *every* reference to the variable will invoke an "If X Is Nothing" check in the auto case, which a Dim/Set variable will not. So you have to test if there is a performance hit whenever you access the object after declaration, rather than test the declaration itself. – Joel Goodwin Dec 13 '11 at 14:41
  • @JP Not true- I Googled this after another SO question I was looking at today. Example: http://www.cpearson.com/excel/classes.aspx Another example: http://www.bettersolutions.com/vba/VUA113/LI912711911.htm – Joel Goodwin Dec 13 '11 at 15:41
  • Gotcha, thanks for the links! – JimmyPena Dec 13 '11 at 15:49
  • Curious (since I don't know) but how do you know that the compiled code equates to If x is Nothing Then Err.raise...? – Gaijinhunter Dec 13 '11 at 16:00
  • It was Mr. Wolfe's answer on http://stackoverflow.com/questions/8472907/is-nothing-in-vba that set me off exploring this. I know it's an interview question that comes up sometimes -- Dim/Set versus Dim As New -- and really wanted to test this one out. – Joel Goodwin Dec 13 '11 at 16:03
  • @Issun - VBA definitely has a null pointer check. Trying to use an object reference that isn't set to anything will generate a runtime error. My "If/Err.Raise" is a representation of that. Just as the "If Nothing/Set" is a representation of the auto case. The same kind of runtime logic exists in both situations. – Joel Goodwin Dec 13 '11 at 16:06
  • I think I see what you mean now. Hrm, interesting - so you are wondering if the If Nothing check that the auto-initiating puts all over your code is the same that the runtime code will do anyway. – Gaijinhunter Dec 13 '11 at 16:10
  • @Issun - that's precisely it! I'm not disproving the point at all - I'm just highlighting that the only performance-hike explanation I've seen is insufficiently convincing. – Joel Goodwin Dec 13 '11 at 16:14
  • It's possible that all the error checking the dim as new adds to the runtime code is what is pre-handled during compile time when you dim and set. – Gaijinhunter Dec 13 '11 at 16:23
  • @Issun. It is certainly possible, but I was hoping for something concrete because testing wasn't revealing any difference. – Joel Goodwin Dec 13 '11 at 16:27
  • Nice Analysis. Both auto/explicit have advantages/disadvantages. Nice to know the performance is equal. Thanks – Fink Dec 13 '11 at 22:00

2 Answers2

6

I know there is the holy way over if it's okay or not to dim as new, but I've never heard of it being said to generate poor performance. The short answer is Not Really. Yes it does litter your code with unnessesary checks to see if it's not Nothing, but you wouldn't notice a speed difference thanks to today's machines. It's like saying "for looping over 10000 characters is faster than 10001. To start seeing any difference, you need to be looping your tests in higher terms, like millions and tens of millions.

That being said Dim as New is frowned upon but not for performance reasons.

  • You lose the ability to control when it's initialized
  • You lose the ability to check if an object is Nothing
  • Speed difference or not, it does litter your code with unnessesary checking

Granted if you are just using VBA to automate some worksheet stuff or manipulate data, you probably won't care about these, but the moment you look at more sophisticated code, losing the ability to test if an object is Nothing and not controlling when it's initialized is huge and can generate unexpected behavior, not to mention make testing a pain in the butt. All that for saving a few lines of code.

Then there are the micro-optimizers who will argue that adding anything to your code that is not needed makes for poor performance. While they are right in some ways, you'll most likely saving 0.000000001 seconds in this case.

JMax
  • 26,109
  • 12
  • 69
  • 88
Gaijinhunter
  • 14,587
  • 4
  • 51
  • 57
  • I'd agree you lose the ability to control when it's initialised; and I think it's a more serious problem that it is practically IMPOSSIBLE to set the object to Nothing. But the question is about the "unnecessary checking" - is this true? After all, if I write Dim x as Dictionary then x.Add(1, "m") -- I'm going to get an error that "Object variable not set" which means compiled VBA is checking it anyway. – Joel Goodwin Dec 13 '11 at 15:25
  • 1
    Checking during compile and checking during runtime are very different. Trust me, checking at compile time is a GOOD thing and leaving it to runtime is not. :) And when an error is throw, it's not becuase it checked and realized it could not do it, it's that it TRIED to do something that it could not do. – Gaijinhunter Dec 13 '11 at 15:33
  • +1 "losing the ability to test if an object is Nothing and not controlling when it's initialized" -- a programmer should be in control, not the code. The performance benefit from not having an implicit "Is Nothing" check when a variable is declared is far outweighed by this consideration. – JimmyPena Dec 13 '11 at 15:34
  • But the example I just quoted is a runtime check. i.e. the "If x is Nothing" is there in the compiled code anyway; the only difference is is in one case it errors, in the other it runs "Set x = New" behind the scenes. – Joel Goodwin Dec 13 '11 at 15:36
  • JP, I totally agree. Even if there were a performance benifit (the opposite of now) I'd still choose to be in control of something so fundamental. – Gaijinhunter Dec 13 '11 at 15:36
  • Joel, I could be mistaken, but an error (again) is the case of it trying to do something it cannot, not checking and letting you know it cannot. There is a difference. Either way, errors should be thrown and dealt with in the case of unexpected behavior (like trying to add to a dictionary that does not exist). – Gaijinhunter Dec 13 '11 at 15:38
  • @Joel You've saved a few milliseconds of time in exchange for control over how your objects are instantiated. If you are OK with that, then by all means write code that way. – JimmyPena Dec 13 '11 at 15:41
  • JP, quite backwards. You save a few milliseconds (if not less) by being explicit. I suppose you do save a second or two of code writing time, though. – Gaijinhunter Dec 13 '11 at 15:44
  • @JP My question is not whether we should use this construct - I wouldn't. I'm trying to determine whether there ever was any truth to the performance argument, which struck me as dodgy when I saw it. "Is the poor performance of Excel VBA auto-instancing a myth?" – Joel Goodwin Dec 13 '11 at 15:46
  • OK, I'm a little slow this morning. – JimmyPena Dec 13 '11 at 15:49
  • 1
    Joel, I think it's a valid question and I did attempt to answer it. Yes it does create extra work that could be considered 'slower performance' but we are talking micro micro micro seconds, so in reality, no, it does not affect speed wither way you choose to do it. – Gaijinhunter Dec 13 '11 at 15:51
  • I've added a bit to the question to highlight why I'm not convinced there is a performance problem at all. I can't find evidence of it. I can't find any voice of authority stating it online. – Joel Goodwin Dec 13 '11 at 15:57
1

Reading this a decade later, I then came across the following in a book 14 years older, speaking of what the compiler does with auto-instancing:

You don’t have to write all that extra code to check for Nothing; Visual Basic writes it for you. But you do have to execute it. Why can’t the compiler see that you’ve already created the object variable after the first statement and quit checking? Because it’s a compiler. Consider this statement:

Dim thing as New CThing
If fHellFrozenOver Then thing.Title = "The latest thing"

The compiler can’t tell at compile time whether hell is frozen over. That won’t be known until run time. Thus it won’t know whether to create a new CThing object without checking. Theoretically, you could write an optimizing compiler that would analyze conditionals and eliminate redundant checks in cases where there was only one possible code path. But that compiler couldn’t work on p-code, where every statement has to stand on its own. You’re better off just using the Set statement to create the objects yourself.

Now, before you throw New out of your declarations toolbox, take a look at the results of the Performance sidebar on page 133. The real-world penalty for using New in declarations for compiled code just isn’t worth worrying about. It’s all but free.

That is from Bruce McKinney's Hard Core Visual Basic, second edition, pg. 122. He then notes that auto-instancing can be used to help with memory management, in the sense that you can set a variable to nothing when you're done with it and know it will be recreated later if needed. This is also kind of a twist on the view that references to uninitialized variables should be used only to highlight bugs.

Now, he doesn't directly address the point raised in the question that a logical test would need to be run in either case. Clearly the author agrees with the point that performance is not a significant concern.

Mark E.
  • 373
  • 2
  • 10