tags:

views:

395

answers:

3

in that specific case

    Const debugTime As String = "hh:mm:ss.fffffff"
    Dim i As Integer

    Debug.Print("Start " & Now.ToString(debugTime))
    For i = 0 To 4000000
        j = 5 - 4
    Next
    Debug.Print("End " & Now.ToString(debugTime))

    Debug.Print("Start " & Now.ToString(debugTime))
    For i = 0 To 4000000
        j = 5 Mod 4
    Next
    Debug.Print("End " & Now.ToString(debugTime))

result

Start 05:33:39.8281250

End 05:33:39.8437500

Start 05:33:39.8437500

End 05:33:39.8437500

* EDIT *

modified the code to make it look like that

    Const debugTime As String = "hh:mm:ss.fffffff"
    Dim i As Long, j As Integer
    Dim r As Random

    r = New Random(1)
    Debug.Print("Start " & Now.ToString(debugTime))
    For i = 0 To 400000000
        j = 5 - r.Next(1, 5)
    Next
    Debug.Print("End " & Now.ToString(debugTime))

    r = New Random(1)
    Debug.Print("Start " & Now.ToString(debugTime))
    For i = 0 To 400000000
        j = 5 Mod r.Next(1, 5)
    Next
    Debug.Print("End " & Now.ToString(debugTime))

now the minus is faster...

Start 05:49:25.0156250

End 05:49:35.7031250

Start 05:49:35.7031250

End 05:49:48.2187500

+5  A: 

The compiler will optimize both of them to an assignment. The very small difference is probably a result of another factor.

UPDATE: I wrote a benchmark on Mac OS X, Intel 64 architecture. Huge difference:

a.asm: assemble with yasm -f macho64 a.asm

SECTION .text
global _bmod, _bmin
_bmod:  push rdx
    push rbx
    mov rcx, 1000000000
    mov rdi, 5
    mov rsi, 4
.bmod:  mov rax, rdi
    mov rbx, rsi
    xor rdx, rdx
    div rbx             ; div instruction stores the mod in rdx.
    dec rcx
    jnz .bmod
    pop rbx
    pop rdx
    ret

_bmin:  push rdx
    push rbx
    mov rcx, 1000000000
    mov rdi, 5
    mov rsi, 4
.bmin:  mov rax, rdi
    mov rbx, rsi
    sub rax, rbx
    dec rcx
    jnz .bmin
    pop rbx
    pop rdx
    ret

a.c: compile with gcc -m64 a.c a.o

#include <time.h>
#include <stdio.h>

void bmod();
void bmin();

main() {
    time_t timex,timex2;
    time(&timex);
    bmod();
    time(&timex2);
    printf("Mod: %d\n", timex2 - timex);
    time(&timex);
    bmin();
    time(&timex2);
    printf("Min: %d\n", timex2 - timex);
}

Result when I ran it on my MacBook Air:

Mehrdad-Air:~ Mehrdad$ yasm -f macho64 a.asm 
Mehrdad-Air:~ Mehrdad$ gcc -m64 -O0 a.c a.o
Mehrdad-Air:~ Mehrdad$ ./a.out 
Mod: 14
Min: 2

As you can see, modulus is about an order of magnitude slower than subtraction.

Mehrdad Afshari
+7  A: 
  • Benchmarking with such tiny time intervals leaves you wide open to problems of timer granularity, which is what you're seeing here.
  • Your code doesn't actually calculate anything, as you've got a constant both times.

In fact, benchmarking operations this small is quite tricky - to make sure neither the compiler nor the JIT compiler optimise things away, you really want to make it use the result somehow, but that will affect the results quite significantly.

Jon Skeet
didn't think of the constant, thanks
Fredou
Another item to consider when profiling VB is that a VB project defaults to having integer overflow checks on. This can cause noticable performance differences in math heavy programs.
JaredPar
+3  A: 

Further to what Jon Skeet says, I usually use the System.Diagnostics.Stopwatch() for benchmarking as I've found that the high speed counter tends to be slightly more reliable than just referencing DateTime.Now

  Dim t = new System.Diagnostics.Stopwatch()
  t.Start

  ''Do Stuff...

  Debug.Print(t.Elapsed)
  t.Stop

Edit: (Or per Jon's suggestion in the comments:

  Dim t = System.Diagnostics.Stopwatch.StartNew

  ''Do Stuff...

  Debug.Print(t.Elapsed)
  t.Stop

)

BenAlabaster
Yup - although I'd argue that if your timing interval is small enough that the system clock isn't accurate enough, you're almost certainly not timing for long enough :) Agreed in general though - I tend to use Stopwatch too (although usually the StartNew static method rather than the constructor).
Jon Skeet
Even if you use a high resolution timer, you may still not be timing anything useful when the JIT realises that you're throwing away the first 3999999 calculations and only actually does the last iteration through the loop.
Greg Hewgill
@Jon: I would argue that if something has such a small interval that the accuracy isn't granular enough, then perhaps .NET is the wrong tool for benchmarking. You would need to run over many millions/billions of iterations and average to stand any chance of decent accuracy.
BenAlabaster
@Greg: In this instance yes - not only JIT, but in this case I would imagine the compiler realises that nothing's being done and optimizes out the loop altogether... I haven't checked though, so don't quote me on that.
BenAlabaster