首页 > 解决方案 > C++ 函数中的条件操作不会降低速度或代码重复:使用宏、内联函数、模板还是其他方式?

问题描述

我正在开发一些高性能的统计函数,并试图让它们根据一些函数参数执行不同的操作。目前的问题是开发一个通用代码,可以根据函数参数灵活地执行差分、部分/广义差分、增长率和对数差分。举一个基本的例子:

// General code to first-difference a vector:
std::vector<double> diff(std::vector<double> x, int ret = 1, double rho = 1, ...) { // ... = more arguments               int l = x.size();
                         std::vector<double> res(l);
                         for (int i = 1; i < l; ++i) res[i] = FUN(x[i], x[i - 1]); 
                         // rest of code ...
}

现在FUN(y, x)是我想根据 和 的参数有效地改变的ret东西rho。对于简单的差异y-x,对于广义差异y-rho*x,对于对数差异log(y/x),对于增长率(y-x)*(100/x),并且可以添加更多选项。该代码适用于大型数据集并且需要快速,因此最佳情况下我会使用类似条件创建的宏的FUN东西,例如:

std::vector<double> diff(std::vector<double> x, int ret = 1, double rho = 1, ...) {
                         int l = x.size();
                         std::vector<double> res(l);
                         #define FUN(y, x) (ret==1 && rho==1) ? ((y)-(x)) : \
                                           (ret==1) ? ((y)-rho*(x)) :      \
                                           (ret==2) ? (log((y)/(x))) :     \
                                           (ret==3) ? ((y)-(x))*(100/(x))                           
                         for (int i = 1; i < l; ++i) res[i] = FUN(x[i], x[i - 1]); 
}

这行得通,但是从降低的代码速度来看,在我看来,我不是有条件地创建宏,而只是一个宏,并且每次FUN调用时,都会评估所有条件以执行正确的操作。我对这些预处理器命令(#if、#elif、#else、#endif、#define 和 #undef)进行了一些研究,但在我看来,您不能使用它们有条件地基于函数参数创建宏。第二种方法可能是使用内联函数,即:

inline double do_diff(double y, double x, double r) {
  return y-x; 
}
inline double do_gdiff(double y, double x, double r) {
  return y-r*x;
}
inline double do_logdiff(double y, double x, double r) {
  return log(y/x); 
}
inline double do_growth(double y, double x, double r) {
  return (y-x)*(100/x); 
}

std::vector<double> diff(std::vector<double> x, int ret = 1, double rho = 1, ...) {
                         int l = x.size();
                         std::vector<double> res(l);
                         auto FUN = (ret==1 && rho==1) ? do_diff : 
                                    (ret==1) ? do_gdiff :       
                                    (ret==2) ? do_logdiff :      
                                    (ret==3) ? do_growth;
                         for (int i = 1; i < l; ++i) res[i] = FUN(x[i], x[i - 1], rho); 
}

这里的问题只是它将代码速度降低了大约 1.5 倍。鉴于这些都是非常简单的操作,并且这段代码应该尽可能快,我宁愿避免这种情况。所以我的问题是:有什么方法可以改变执行的操作,而FUN性能成本可以忽略不计?

注意:这里的代码重复是不可取的,因为我正在处理的实际代码要复杂得多,即它可以对无序的面板数据等执行迭代差异,大约 700 行在FUN多个地方输入。

标签: c++rcpp

解决方案


模板似乎更合适:

template <typename F>
std::vector<double> diff(std::vector<double> x, F f, double rho = 1, ...) {
     int l = x.size();
     std::vector<double> res(l);
     for (int i = 1; i < l; ++i) res[i] = f(x[i], x[i - 1], rho); 
     // ...
}

std::vector<double> diff(const std::vector<double>& x, ret = 1, double rho = 1, ...) {
    switch (ret)
    {
        case 1: return (rho == 1) ?
                       diff(x, []{double y, double x, double r) { return y-x; }, rho) :
                       diff(x, []{double y, double x, double r) { return y-r*x; }, rho);
        case 2: return diff(x, []{double y, double x, double r) { return log(y/x); }, rho);
        case 3: return diff(x, []{double y, double x, double r) { return (y-x)*(100/x); }, rho);
    }
    throw std::runtime_error("Invalid argument");
}

似乎rho只能由(一个)lambda 捕获,从而允许摆脱一个参数。


推荐阅读