避免切换语句

时间:2013-07-15 23:31:09

标签: c++ switch-statement

考虑以下代码:

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中tag的值,但是用户想要从输入文件调用该函数而不从makefile进行更改。)< / p>

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

由于

7 个答案:

答案 0 :(得分: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;
    }
}

答案 1 :(得分: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);

答案 2 :(得分:2)

使用&#34;未知&#34;开启choice值比计算1.0/R快4到10倍,比exp(R);log(R);快10到100倍

如果choice始终相同,则分支预测将启动并决定&#34;哦,它通常在这里2,所以让我们预加载并开始执行选择2&#34;。

使用函数指针可能不值得付出努力,因为你最终会调用而不是可预测的条件跳转 - 所以不太可能给你任何好处。

如果choice是调用代码中的硬编码常量,则将此函数放在头文件中,让编译器内联它将删除整个开关。但如果它是从文件读取的变量,那么它就是一个不同的故事。

最后,也许对代码进行不同的拆分会有所帮助 - 例如而不是传递单个double,传递const vector<double>& R, vector<double> K并从K值计算{,1}}值的10,100或100000000值。

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

答案 3 :(得分:1)

首先,关于剖析和东西的常见做法。

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

请注意,所有基于存储函数指针或函数对象的解决方案都是跳转表,它们只是非本地的,编译器优化起来要困难得多 - &gt;性能较慢。

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

答案 4 :(得分:0)

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

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

当您正在读取输入时,确定要调用哪个函数(可能通过switch语句),然后将指针存储到该函数。

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

在这种情况下,你只会经历一次switch语句,所以这是一个改进。

答案 5 :(得分:0)

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

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

进行。

答案 6 :(得分:-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};