I was given some spaghetti code to clean up, where everything basically was basically lumped together into a few hard-to-read subroutines. After putting the effort into repackaging the code into nicely documented, easily read functions, I'm finding my revised code is >3x slower than the original spaghetti. Rather depressed about this.
I suspect the problem is the overhead associated with function calls. I tried the timing the following series of nested functions, each run 3 million times.
Public Declare PtrSafe Function GetTickCount Lib "kernel32.dll" () As Long
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Function test1(X As Double)
test1 = Math.Exp(X)
End Function
Function test2(X As Double)
test2 = test1(X)
End Function
Function test3(X As Double)
test3 = test2(X)
End Function
Function test4(X As Double)
test4 = test3(X)
End Function
Sub test()
Dim t0 As Long
t0 = GetTickCount
Dim n As Long
Dim f As Double
For n = 0 To 3000000#
f = test4(2)
Next n
Debug.Print "Elapsed:", GetTickCount - t0
End Sub
Results:
test | ms |
---|---|
1 | 500 |
2 | 812 |
3 | 1109 |
4 | 1422 |
Is there anyway around this? I'm bummed that my effort to make code more maintainable has resulted in an inferior product.
If you add a function that does nothing, you can identify the overhead cost of calling the function:
Function test0()
End Function ' -> 200 ms
When specifying the return type as suggested in the comments, the overhead time is halfed:
Function test0() As Double
End Function ' -> 100 ms
And the goto equivalent as you already suspected is even faster:
Sub goto_test()
Dim foo As Double
Dim t0 As Long
t0 = GetTickCount
Dim n As Long
Dim f As Double
For n = 0 To 3000000#
GoTo test0_label
jumpback:
f = foo ' cost for this line is insignificant
Next n
Debug.Print "Elapsed:", GetTickCount - t0
End
test0_label:
GoTo jumpback
End Sub ' -> 30 ms