• Function Pointers...

    From Mike Sanders@21:1/5 to All on Sat Feb 17 21:16:30 2024
    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
    case 1: outputTXT(p, nodes, c); break;
    case 2: outputCSV(p, nodes, c); break;
    case 3: outputSQL(p, nodes, c); break;
    case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect call
    is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Scott Lurndal on Sat Feb 17 21:48:54 2024
    Scott Lurndal <scott@slp53.sl.home> wrote:

    Not generally. In both cases, the function call machine instruction
    needs the address of function. Loading a register with
    the function pointer (if it is not already available,
    for example as an offset from already loaded base register)
    may or may not add to the instruction count; depending
    on what relative branch size(s) the function call instruction
    supports even a direct call may require the compiler to
    load a function pointer into a register anyway.

    In your example, it depends on how the compiler implements
    the switch statement - it could indeed just index into
    a four element array containing the function pointers
    when compiled with the appropriate optimization flags.

    Thanks Scott.

    I keep reading otherwise (comfused about it all really):

    <https://www.google.com/search?q=do+function+pointers+incur+overhead>

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Mike Sanders on Sat Feb 17 21:23:54 2024
    porkchop@invalid.foo (Mike Sanders) writes:
    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
    case 1: outputTXT(p, nodes, c); break;
    case 2: outputCSV(p, nodes, c); break;
    case 3: outputSQL(p, nodes, c); break;
    case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect call
    is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?

    Not generally. In both cases, the function call machine instruction
    needs the address of function. Loading a register with
    the function pointer (if it is not already available,
    for example as an offset from already loaded base register)
    may or may not add to the instruction count; depending
    on what relative branch size(s) the function call instruction
    supports even a direct call may require the compiler to
    load a function pointer into a register anyway.

    In your example, it depends on how the compiler implements
    the switch statement - it could indeed just index into
    a four element array containing the function pointers
    when compiled with the appropriate optimization flags.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Mike Sanders on Sat Feb 17 21:32:00 2024
    On 17/02/2024 21:16, Mike Sanders wrote:
    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
    case 1: outputTXT(p, nodes, c); break;
    case 2: outputCSV(p, nodes, c); break;
    case 3: outputSQL(p, nodes, c); break;
    case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect call
    is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?


    With a switch, sometimes the function call can be inlined. That can
    speed up something like bytecode dispatching in an interpreter where you
    might be doing 100s of millions of calls per second.

    But judging from the names of your functions, each one looks to be
    writing stuff out to a file. Then the function call overhead will be insignificant.

    Note that switch will also check that the ops.export index is in the
    correct range; if less than 1 or greater than 4, nothing is called, but
    the function table version will go wrong.

    BTW you seem to have mixed up 1-based and 0-based here: 1 means TXT in
    the switch version, but CSV in the function pointer version since arrays
    are 0-based.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to bart on Sat Feb 17 21:55:51 2024
    bart <bc@freeuk.com> wrote:

    But judging from the names of your functions, each one looks to be
    writing stuff out to a file. Then the function call overhead will be insignificant.

    Hi bart.

    So Scott seems to imply as well, I'll likly use the function pointer.

    Note that switch will also check that the ops.export index is in the
    correct range; if less than 1 or greater than 4, nothing is called, but
    the function table version will go wrong.

    BTW you seem to have mixed up 1-based and 0-based here: 1 means TXT in
    the switch version, but CSV in the function pointer version since arrays
    are 0-based.

    ops.export is always 1 to 4, so:

    output[ops.export -1](p, nodes, c);

    keeps it within bounds of zero indexing (neverteless, good catch).

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Mike Sanders on Sat Feb 17 21:59:40 2024
    On Sat, 17 Feb 2024 21:16:30 -0000 (UTC), Mike Sanders wrote:

    Now here's where I'm unsure: Perhaps the overhead of an indirect call is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?

    “Premature optimization is the root of all evil.”
    -- variously attributed to Tony Hoare or Donald Knuth

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Mike Sanders on Sat Feb 17 22:07:50 2024
    Mike Sanders <porkchop@invalid.foo> wrote:

    ops.export is always 1 to 4, so:

    output[ops.export -1](p, nodes, c);

    keeps it within bounds of zero indexing (neverteless, good catch).

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };
    // 0 1 2 3

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Mike Sanders on Sat Feb 17 22:26:14 2024
    porkchop@invalid.foo (Mike Sanders) writes:
    Scott Lurndal <scott@slp53.sl.home> wrote:

    Not generally. In both cases, the function call machine instruction
    needs the address of function. Loading a register with
    the function pointer (if it is not already available,
    for example as an offset from already loaded base register)
    may or may not add to the instruction count; depending
    on what relative branch size(s) the function call instruction
    supports even a direct call may require the compiler to
    load a function pointer into a register anyway.

    In your example, it depends on how the compiler implements
    the switch statement - it could indeed just index into
    a four element array containing the function pointers
    when compiled with the appropriate optimization flags.

    Thanks Scott.

    I keep reading otherwise (comfused about it all really):

    The only place where function pointers (which generalize
    to functions in this context) have a performance impact
    is when using indirection into a shared object via a procedure
    linkage table. That overhead is independent upon weather
    you use a function or a function pointer.

    You example would only be affected by that performance
    impact if the function being called were in a different
    shared object or dynamically linked library.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Malcolm McLean on Sat Feb 17 23:36:31 2024
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:

    Compilers will often inline a direct call and perform aggressive
    holistic optimsation of the resulting subroutine whilst they won't do so
    with an indirect call, even if the value is passed as a constant. But of course it depends on the compiler. Whether a direct call is in itself
    faster than an indirect call or not depends on the architecture. Many architectures don't really support direct calls and they are faked up by loading a constant into a register, and so there is no difference in execution speed. But sometimes the indirect call is a separate
    instruction and so there can be a minuscule overhead.

    Thanks Malcom.

    Just plain ol' gcc or clang here, & I see no difference in speed.

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Scott Lurndal on Sat Feb 17 23:41:59 2024
    Scott Lurndal <scott@slp53.sl.home> wrote:

    The only place where function pointers (which generalize
    to functions in this context) have a performance impact
    is when using indirection into a shared object via a procedure
    linkage table. That overhead is independent upon weather
    you use a function or a function pointer.

    You example would only be affected by that performance
    impact if the function being called were in a different
    shared object or dynamically linked library.

    Well, a couple of things in favor of using the function pointer
    (in this context), its only called once per program invocation
    & its not called within a loop. Must study more knuckehead that
    I am.

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Lawrence D'Oliveiro on Sat Feb 17 23:38:20 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    ???Premature optimization is the root of all evil.???
    -- variously attributed to Tony Hoare or Donald Knuth

    Aye, that's what I want to avoid, cute/crafty code might
    cause issues...

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Mike Sanders on Sat Feb 17 23:59:16 2024
    On Sat, 17 Feb 2024 23:38:20 -0000 (UTC), Mike Sanders wrote:

    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    “Premature optimization is the root of all evil.”
    -- variously attributed to Tony Hoare or Donald Knuth

    Aye, that's what I want to avoid, cute/crafty code might cause issues...

    Write it in the straightforward, obvious way, then.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Mike Sanders on Sat Feb 17 16:47:37 2024
    porkchop@invalid.foo (Mike Sanders) writes:

    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
    case 1: outputTXT(p, nodes, c); break;
    case 2: outputCSV(p, nodes, c); break;
    case 3: outputSQL(p, nodes, c); break;
    case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect
    call is miniscule, even negligible. But there *IS* additional
    overhead when using function pointers correct?

    Let me give the conclusion first and the explanation second.

    Conclusion: if you think the array-of-function-pointers approach
    looks better or improves program structure then by all means use
    it.

    Explanation: I got interested in this question some years ago,
    in a similar scenario. Calling through a pointer-to-function can
    and sometimes does incur a performance penalty compared to
    calling a function directly. What I was interested in though is
    a comparison like the one you are interested in: should I use
    a switch() with direct calls, or indirect calls using an array of
    function pointers. So I did some measurements. The results were
    very nearly the same for the two different schemes. Choosing one
    approach over the other was a wash, performance-wise. I didn't
    do an exhaustive study or anything like that, but the results I
    did get showed that, at least to first order, there is no speed
    preference for either choice. So pick the one that gives a more
    nicely structured program, and don't worry about which one might
    be faster.

    (Later on you might want to do some measurements and actually see
    which approach runs faster, but the evidence suggests there is no
    reason to worry about that now.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mike Sanders@21:1/5 to Tim Rentsch on Sun Feb 18 11:26:40 2024
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Let me give the conclusion first and the explanation second.

    Conclusion: if you think the array-of-function-pointers approach
    looks better or improves program structure then by all means use
    it.

    Explanation: I got interested in this question some years ago,
    in a similar scenario. Calling through a pointer-to-function can
    and sometimes does incur a performance penalty compared to
    calling a function directly. What I was interested in though is
    a comparison like the one you are interested in: should I use
    a switch() with direct calls, or indirect calls using an array of
    function pointers. So I did some measurements. The results were
    very nearly the same for the two different schemes. Choosing one
    approach over the other was a wash, performance-wise. I didn't
    do an exhaustive study or anything like that, but the results I
    did get showed that, at least to first order, there is no speed
    preference for either choice. So pick the one that gives a more
    nicely structured program, and don't worry about which one might
    be faster.

    (Later on you might want to do some measurements and actually see
    which approach runs faster, but the evidence suggests there is no
    reason to worry about that now.)

    Excellent reply Tim, its right where I'm at. And my love/hate
    relationship with C continues =)

    --
    :wq
    Mike Sanders

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Malcolm McLean on Sun Feb 18 12:59:37 2024
    On 18/02/2024 11:52, Malcolm McLean wrote:
    On 18/02/2024 11:26, Mike Sanders wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Let me give the conclusion first and the explanation second.

    Conclusion:  if you think the array-of-function-pointers approach
    looks better or improves program structure then by all means use
    it.

    Explanation:  I got interested in this question some years ago,
    in a similar scenario.  Calling through a pointer-to-function can
    and sometimes does incur a performance penalty compared to
    calling a function directly.  What I was interested in though is
    a comparison like the one you are interested in:  should I use
    a switch() with direct calls, or indirect calls using an array of
    function pointers.  So I did some measurements.  The results were
    very nearly the same for the two different schemes.  Choosing one
    approach over the other was a wash, performance-wise.  I didn't
    do an exhaustive study or anything like that, but the results I
    did get showed that, at least to first order, there is no speed
    preference for either choice.  So pick the one that gives a more
    nicely structured program, and don't worry about which one might
    be faster.

    (Later on you might want to do some measurements and actually see
    which approach runs faster, but the evidence suggests there is no
    reason to worry about that now.)

    Excellent reply Tim, its right where I'm at. And my love/hate
    relationship with C continues =)

    If you are writing a bytecode interpeter often there is a big switch in
    the innermost loop, and so it is very important to optimise it.

    In my main interpreter, bytecode dispatch is usually done with a simple
    loop invoking a function from a table of function pointers.

    But there is also a now unused module where it is done with a big switch
    (where each case just calls the corresponding function).

    I dusted off that module, transpiled the program to C, compiled with gcc
    -O3, and got these results on the first 3 programs I tried:

    With two of them, using the switch-based method was 8% faster. With the
    third, it was 20% faster.

    It would probably be faster still if, instead of function calls, I
    manually inlined some of the switch cases.

    (However I don't normally bother with that dispatch method, since for
    speed, I use another method (involving inline assembly and threaded
    code) that can give me speedups of 2-3x even over gcc-O3 using switch,
    and that's not even using C or optimisation.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Mike Sanders on Sun Feb 18 14:41:04 2024
    On 17/02/2024 22:16, Mike Sanders wrote:
    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
    case 1: outputTXT(p, nodes, c); break;
    case 2: outputCSV(p, nodes, c); break;
    case 3: outputSQL(p, nodes, c); break;
    case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
    { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect call
    is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?


    First off, only concern yourself about the overhead if the overhead is
    relevant - not all code has to be at top efficiency. But all code
    benefits from being written in a clear and maintainable manner, with
    minimal risk of errors. So keep that in mind when making such design decisions.


    I personally would not use function pointers here. You can have
    different balances and opinions, and the rest of your code can influence things, but I will list my justifications here.

    1. You can get function pointers wrong. You can, of course, get all
    kinds of things wrong - but when a function pointer goes wrong, it can
    be extremely difficult to figure out what is happening and debug the code.

    2. Switches have range-checking built in, array lookups here do not.

    3. Switches can work directly with enumerated types which are almost
    certainly a much better choice here for the type of "ops.export". Drop
    the "magic numbers" and use names from an enumerated type. This makes
    the switch clearer, and easier to maintain. It lets you use
    "-Wswitch-enums" in gcc/clang to warn if the switch does not handle all
    the cases.

    4. Your ordering in the switch can be whatever suits your preferences
    and for maintenance, not the numerical ordering needed for array initialisation.

    5. Now that you have an order free from numbers, there are no
    complications or risks if you want to remove one of the actions.

    6. The actions are run from within the switch, not via pointers. That
    means the compiler can optimise far better, and - often more importantly
    - can do more static analysis to find potential problems at compile
    time. It also means you don't have to squeeze everything into one set
    of parameters - different actions can have different parameters.

    7. Tools that deal with call tracing work much better with the switch
    version. That includes tools to check stack depth (rarely important on
    PC's, but useful in small embedded systems), code coverage,
    documentation, etc., can do a better job. If you are debugging and need
    to work backwards to find how you got to a particular point in the
    program, it is vastly easier if there are no function pointers in the way.

    There are certainly situations where function pointers make code better
    - in particular, using callbacks avoids dependencies between different
    parts of the code. But for my own use, I rarely find them the best choice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Sun Feb 18 14:29:12 2024
    On 18/02/2024 13:41, David Brown wrote:
    On 17/02/2024 22:16, Mike Sanders wrote:
    For a good while I've been wanting to use function pointers
    & I finally found an opportunity to do so...

    Currently I have:

    switch(ops.export) {
         case 1: outputTXT(p, nodes, c); break;
         case 2: outputCSV(p, nodes, c); break;
         case 3: outputSQL(p, nodes, c); break;
         case 4: outputHTM(p, nodes, c); break;
    }

    But instead I could use:

    void (*output[])(FILE*, BLOCK[], int) =
         { outputTXT, outputCSV, outputSQL, outputHTM };

    // do stuff

    output[ops.export -1](p, nodes, c);

    Now here's where I'm unsure: Perhaps the overhead of an indirect call
    is miniscule, even negligible. But there *IS* additional overhead when
    using function pointers correct?


    First off, only concern yourself about the overhead if the overhead is relevant - not all code has to be at top efficiency.  But all code
    benefits from being written in a clear and maintainable manner, with
    minimal risk of errors.  So keep that in mind when making such design decisions.


    I personally would not use function pointers here.  You can have
    different balances and opinions, and the rest of your code can influence things, but I will list my justifications here.

    1. You can get function pointers wrong.  You can, of course, get all
    kinds of things wrong - but when a function pointer goes wrong, it can
    be extremely difficult to figure out what is happening and debug the code.

    2. Switches have range-checking built in, array lookups here do not.

    3. Switches can work directly with enumerated types which are almost certainly a much better choice here for the type of "ops.export".  Drop
    the "magic numbers" and use names from an enumerated type.  This makes
    the switch clearer, and easier to maintain.  It lets you use "-Wswitch-enums" in gcc/clang to warn if the switch does not handle all
    the cases.

    4. Your ordering in the switch can be whatever suits your preferences
    and for maintenance, not the numerical ordering needed for array initialisation.

    5. Now that you have an order free from numbers, there are no
    complications or risks if you want to remove one of the actions.

    6. The actions are run from within the switch, not via pointers.  That
    means the compiler can optimise far better, and - often more importantly
    - can do more static analysis to find potential problems at compile
    time.  It also means you don't have to squeeze everything into one set
    of parameters - different actions can have different parameters.

    7. Tools that deal with call tracing work much better with the switch version.  That includes tools to check stack depth (rarely important on PC's, but useful in small embedded systems), code coverage,
    documentation, etc., can do a better job.  If you are debugging and need
    to work backwards to find how you got to a particular point in the
    program, it is vastly easier if there are no function pointers in the way.

    There are certainly situations where function pointers make code better
    - in particular, using callbacks avoids dependencies between different
    parts of the code.  But for my own use, I rarely find them the best choice.


    In the example posted, there is a clear pattern where you have four near-identical lines; only the function name changes. Making it more table-driven makes sense.

    But with only four cases, there is little to choose from between those
    two options.

    The actual performance of function pointers vs. switch is irrelevant
    here. (I think the OP said this is only ever executed once, but even a
    million times per second wouldn't matter.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)