运行时一次高效的功能选择器

问题描述

我需要并行执行多次操作。例如,在点云处插入图像。对于此操作,我有一些变体。例如,不同的插值函数(考虑线性,二次,三次等)。问题是,如何在运行时有效地选择一次操作。我想避免对操作的每次调用进行分支。

通常,我将为此使用模板实例化。但是,我正在通过Matlab Mex API调用该函数。这意味着,我在编译时不知道“选择”操作。

现在,我正在考虑使用函数指针,但是我没有使用它们的经验。选择特定操作的一种变体以使后续调用将直接转移到正确版本的有效方法。

最小示例:

class Image
{
public:
    size_t siz[3] = { 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)
    }

}

解决方法

如果我正确理解了您的问题,则可以在输入代码部分之前一遍又一遍地选择操作。

double interp_v1(std::size_t offset)
{
    //implementation here
}

double interp_v2(std::size_t offset)
{
    //implementation here
}

int main()
{
    double (*interp_func)(std::size_t);

    if ( /* some condition */ ) {
        interp_func = interp_v1;
    }
    else if ( /* some other condition */ ) {
        interp_func = interp_v2;
    }

    //Loop that does the heavy lifting
    for (int counter = 0; counter != 1000000; ++counter) {
        auto some_variable = interp_func(offset);
    }
}
,

可能两个选项应该大致相同。如果性能真的很重要,则只需评估您的代码即可。我做了一个基准测试,并且ifs和指针一样快。

请记住,如果使用函数指针,则将具有间接性,并且使用“ if”语句,分支应该不会成为一个大问题,因为分支预测每次(理论上)都会在某些时候开始正确猜测电话。因此,您可能应该只选择一种感觉更清晰,更容易理解的方法。就我而言,if语句使这一点更清晰,并且可能使内联更加可行。

我尝试过并且似乎同样快(在某些情况下甚至更快)的另一个选项是动态多态。万一有人要添加新方法,此方法也更易于维护。这是一个具有动态多态性的示例。

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..
    }
};

您在这里有我的基准的链接:https://quick-bench.com/q/1mR0EyYrqvzunpEGbrHBw_U7eEY

,

我相信手动设置函数指针与虚拟函数与一小组开关/案例之间的差异与真正的基础操作相比不会有太大差异。

在谈论图片和大量像素时,您应该考虑最佳算法,而不要考虑使用函数或vtable指针的一两个间接作用。

此外,我希望代码的大小无关紧要,您可以使用该函数将循环模板化为,并在循环本身内部获得零运行时间开销,因为成员指针是一个编译时间常数。

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

        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 0; // insert code to do interpolation method 1
        }

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

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

        template < auto which_func >
            double loop( size_t offset )
            {
                //Loop that does the heavy lifting
                for (int counter = 0; counter != 1000000; ++counter) {
                    auto some_variable = (this->*which_func)(offset);
                }

                return 0;
            }

};


int main()
{
    double f[3];
    Image img{ f,1,1 };

    int what = 1;

    switch ( what )
    {
        case 0:
            img.loop<&Image::interp_v1>(0);
            break;

        case 1:
            img.loop<&Image::interp_v2>(0);
            break;

        case 0:
            img.loop<&Image::interp_v3>(0);
            break;
    }

}

我不知道您拥有的完整数据结构。但是,如果您具有静态函数或可以修改为静态函数,则也可以删除“ this-> *”。如果确实存在问题,这可以避免取消引用this指针。

所有性能问题的提示:衡量,衡量,衡量。通常情况下,编译器通过优化循环外的东西来达到最佳效果。因此,您可能已经在没有任何“编译器提示”的情况下使指针取消了对循环的引用。我相信您的算法更重要,内存布局和调用顺序也更重要。高速缓存行未命中,这种昂贵的问题比单指针的使用更为重要!

,

如果您只想使用函数指针的示例,则这里是使用函数指针数组的方法。设置了代码,以便您可以在运行时更改正在使用的函数指针。

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

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

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

// Array of function pointers with the specified signature
double (*interp_array[])(size_t offset) =
{ interp_v1,interp_v2,interp_v3 };

// Function pointer with the specified signature
double (*interp_ptr)(size_t offset) = interp_array[0];

// Set the function pointer
void interp_set(int i) {
    if( i > 0 && i <= sizeof(interp_array)/sizeof(interp_array[0]) ) {
        interp_ptr = interp_array[i-1];
    }
}

// Function interp uses whatever funtion interp_ptr is pointing to
double interp(size_t offset) {
return interp_ptr(offset);
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...