Search code examples
c++function-pointersmex

Efficient function selector once at runtime


I have an operation that I need to perform many times in parallel. For example interpolate an image at a cloud of points. For this operation I have a few variants. For example different interpolation functions (think linear, quadratic, cubic, etc.). The question is, how to efficiently select the operation once during runtime. I want to avoid branching for every call of the operation.

Normally, I would use template instantiation for this. However, I'm calling the function thru the Matlab Mex API. This means, that I don't know the "chosen" operation at compile time.

Now, I was thinking maybe about using function pointers, but I don't have experience using them. What would be an efficient method for selecting one variant of the particular operation such that subsequent calls will be transferred directly to the correct version.

Minimal example:

class Image
{
public:
    size_t siz[3] = { 0, 0, 0 }; // image size (always 3D)
    double *f; // input image
    
    Image(double *f, size_t i, size_t j, size_t k) : f(f), siz{i, j, k} {
    }

    double interp_v1(size_t offset) {
    return // insert code to do interpolation method 1
    }

    double interp_v2(size_t offset) {
    return // insert code to do interpolation method 2
    }

    double interp_v3(size_t offset) {
    return // insert code to do interpolation method 3
    }

    double (*interp)(size_t offset) {
    return interp_v1 // use interp_v1 when interp is called (can be changed at runtime)
    }

}

Solution

  • Probably both options should be roughly the same. If performance is really important, just measure your code. I did a benchmark and ifs were as fast as pointer.

    Keep in mind that if you use a function pointer, you have an indirection, and with the "if" statement branching should not be a huge problem, as branch prediction would start to guess right every time (in theory) after some calls. So you should probably just choose the one that feels clearer and easier to understand. In my case, the if statement makes this clearer and probably makes inlining more viable.

    Another option that I've tried and seems to be as fast (and in some cases even faster) is dynamic polymorphism. This one is also easier to maintain in case someone wants to add new methods. This an example with dynamic polymorphism.:

    struct Base {
        void whatever() = 0;
    };
    
    struct Method1: Base {
        void whatever() override {
    //Code for first method
        }
    };
    
    struct Method2: Base {
        void whatever() override {
    //COde for second method..
        }
    };
    

    Here you have the link to my benchmakrs: https://quick-bench.com/q/1mR0EyYrqvzunpEGbrHBw_U7eEY