1

考虑以下代码:

double computeK(unsigned short choice, double R){
   switch (choice){
      case 1:
        return 1.0/R;
        break;
      case 2:
        return log(R);
        break;
      case 3:
        return exp(-R);
        break;
      case 4:
        return exp(-R*R);
        break;
      case 5:
        return exp(-R);
        break;
      default:
        return 1.0/R/R;
        break;
   }
}

R用户从他的输入文件调用的另一个类的不同值多次调用此函数,但choice每个输入文件的值保持相同。有没有办法避免这个 switch 语句?(我可以编写一个makefile,有适当的标签choice并要求用户更改makefile中标签的值,但用户想从他的输入文件中调用该函数而不从makefile中进行更改。)

如果您也能提供相关代码,我将不胜感激。

谢谢

4

7 回答 7

3

C++ 方法看起来像:

更新由于它显然不是不言自明的,这是固定的:

  • 它使开关退出循环(您说choice所有值都将保持不变)。切换只进行一次
  • 它注意允许编译器完全内联computeK调用(函数指针不一定是这种情况,尽管编译器现在很聪明,并且有时会发现这样做的能力)。当调用被实际计算包围时,这可能会带来巨大的性能优势。

代码:

#include <vector>
#include <cmath>

template <typename ComputeK>
   void RunBatch(std::vector<double> const& data, ComputeK const& computeK)
{
    for (double element : data)
    {
        double k = computeK(element); // TODO useful work
    }
}

int main()
{
    int choice = 3; // TODO input
    std::vector<double> data(10000); // Demo only, could be streamed from input
    switch (choice){
        case  0: RunBatch(data, [] (double R) { return   1.0/R;     }); break;
        case  2: RunBatch(data, [] (double R) { return   log(R);    }); break;
        case  3: RunBatch(data, [] (double R) { return   exp(-R);   }); break;
        case  4: RunBatch(data, [] (double R) { return   exp(-R*R); }); break;
        case  5: RunBatch(data, [] (double R) { return   exp(-R);   }); break;
        default: RunBatch(data, [] (double R) { return 1.0/R/R;     }); break;
    }
}
于 2013-07-15T23:50:22.857 回答
2

choice使用“未知”值打开可能比计算快 4-10 倍,比或1.0/R快 10-100 倍exp(R);log(R);

如果choice总是相同,则分支预测将启动并决定“哦,这里通常是 2,所以让我们预加载并开始执行选项 2”。

使用函数指针可能并不值得付出努力,因为您最终会进行调用而不是可预测的条件跳转——因此不太可能给您带来任何好处。

如果choice是调用代码中的硬编码常量,则将此函数放在头文件中并让编译器内联它将删除整个开关。但如果它是从文件中读取的变量,那就另当别论了。

最后,也许对代码进行不同的拆分会有所帮助 - 例如,不是传入单个double,而是传入 a并根据这些值const vector<double>& R, vector<double> K计算 10、100 或 100000000 个值。KR

和往常一样,在处理优化/性能、基准测试、比较不同的解决方案时,最好在多个平台上进行。

于 2013-07-15T23:57:34.870 回答
2

如果choice在编译时知道,您可以使用模板来选择要调用的专业化:

template<int choice>
void computeK(double R);

template<>
void computeK<1>(double R)
{
    return 1.0/R;
}

template<>
void computeK<2>(double R)
{
    return log(R);
}

你叫:

computeK<2>(R);
于 2013-07-15T23:44:40.870 回答
1

首先,关于分析和东西的常见问题。

其次,switch将尽可能高效。它是简单的连续值,编译器可以轻松地将其转换为函数局部跳转表。另外,如果您继续使用相同的方法调用,分支预测choice将使 CPU 几乎(如果不是有时绝对)在零时间内执行这个简单模式。

请注意,所有基于存储函数指针或函数对象的解决方案都是跳转表,它们只是非本地的,编译器更难以优化 -> 性能较慢。

编辑:事实上,如果您可以说服用户在 Makefile 中提供值,那么模板将是一个非常可行的选择。我把这个问题读作禁止这样做,但其他回答者没有,所以这有点模棱两可。

于 2013-07-15T23:47:01.870 回答
0

使用带有函数对象或函数指针的表驱动,例如:

typedef double (*FunPtr)(double r);
FunPtr Handle_Table[6] = {handle1Fun, handlele2Fun....};
....
return Handle_Table[choise](r);

完毕。

于 2013-07-16T03:45:29.313 回答
0

只需将您的大功能分解为几个更简单的功能,例如:

double computeK3(double R){
    return exp(-R);
}

在读取输入时,决定要调用哪个函数(可能通过 switch 语句),然后存储指向该函数的指针。

现在,您可以使用该指针调用正确的函数。

在这种情况下,您将只执行一次 switch 语句,因此这是一种改进。

于 2013-07-15T23:38:57.703 回答
-1

我会创建一个函数指针数组,每个函数代表一个不同的操作:

std::vector<double (*func)(double)> opList;

在某个地方定义每个索引(例如选择)以指向不同的函数,定义函数以便它们接受 R 值并返回结果。然后,代替 switch 语句,您访问 opList 数组的数组索引并像函数一样使用它:

double computeK(unsigned short choice, double R) {
    return opList[choice-1](R);
}

函数指针列表中的每个函数看起来像这样:

double Choice1(double R) {return return 1.0/R};
于 2013-07-15T23:41:36.870 回答