Search code examples
vbaoptimizationoverhead

Function call overhead


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.


Solution

  • 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