nescius
nescius

Reputation: 73

Which of these is more efficient to choose a function?

So lets say I have 2 functions to choose from based on whether number is even or odd. I came up with this:

(void (*[])()){f1 ,f2}[n%2]();.

Is it more or less efficient than simply:

n%2 ? f2() : f1();

Upvotes: 3

Views: 106

Answers (2)

Joshua
Joshua

Reputation: 43327

Profile it; most likely too small to measure.

Assuming a naive compiler, the second will be a lot shorter to execute. But the first is written terribly; could be squashed down to

((n1&1) ? f1 : f2)();

now it's pretty much a toss-up. the first generates something like

test al, 1
jz +3
call f1
jmp +1
call f2

and the second something like

test al, 1
jmp +3
lea  rcx, [f1]
jmp +1
lea  rcx, [f2]
call rcx

but a good optimizer could flatten that down to

lea  rcx, [f1]
test al, 1
cmovcc rcx, f2
call rcx

while all this is true; the initial statement applies. It's most likely too small to measure.

Additional question in comments involving "easier to expand"; well yes. After a surprisingly short number of functions, the array lookup becomes faster. I'm skeptical of things and would not write the array inline but somebody could come along and prove me wrong. If there's more than two I would write

static  void (*dispatch_table[])() = {f1 ,f2};
dispatch_table[n % (sizeof(dispatch_table) / sizeof(dispatch_table[0]))]();

This complex mod expression compiles to a compile-time constant; which lets the compiler optimize out the % to something more performant; it's written as so so that adding more entries doesn't require changing the second argument to % when adding to the array.

As is being pointed out in comments I didn't handle n negative. Most RNG sources don't generate negative numbers.

Upvotes: 3

selbie
selbie

Reputation: 104589

Godbolt to the rescue (64-bit Clang 11.0 with -O3 optimizations): https://godbolt.org/z/MWjPnn

First implementation:

void impl1(int n)
{
    (void (*[])()){evenfunc ,oddfunc}[n%2]();
}

    mov     qword ptr [rsp - 16], offset evenfunc()
    mov     qword ptr [rsp - 8], offset oddfunc()
    mov     eax, edi
    shr     eax, 31
    add     eax, edi
    and     eax, -2
    sub     edi, eax
    movsxd  rax, edi
    jmp     qword ptr [rsp + 8*rax - 16]    # TAILCALL

Second implementation:

void impl2(int n)
{
    n%2 ? oddfunc() : evenfunc();
}

        test    dil, 1
        jne     .LBB1_1
        jmp     evenfunc()                    # TAILCALL
.LBB1_1:
        jmp     oddfunc()                     # TAILCALL

Upvotes: 1

Related Questions