C++ 在紧密循环中哪个更快?[swich cast,如果有,转到标签]

C++ 在紧密循环中哪个更快?[swich cast,如果有,转到标签],c++,performance,if-statement,switch-statement,goto,C++,Performance,If Statement,Switch Statement,Goto,我有一个函数:f(param),它根据输入进行单个计算(param)。这个函数应该在一个紧循环中调用大约100万(最大值): for(std::uint\u fast64\u t i=0;i

我有一个函数:
f(param)
,它根据输入进行单个计算(
param
)。这个函数应该在一个紧循环中调用大约100万(最大值):

for(std::uint\u fast64\u t i=0;i<1'000'000;++i){
f(参数);
}
第一个问题: 我的(第一个)问题是,编写
f()
函数的条件部分(根据
param
)最有效的方法是什么。我尝试了一些我知道的选项:

if-else if
静态int f_3(int const param){
if(A::u 1==param){
返回A::_1*参数;
}else如果(A::_2==param){
返回A::_2*参数;
} // ... 
}
开关箱
静态int f(int const param){
开关(参数){
案例A:_1:
返回参数*A::_1;
// ...
}
}
转到标签
静态int f_2(int const param){
void constexpr*const_表[]={
&&L1,/。。。
};
goto*_表[参数];
L1:
返回A::_1*参数;
// ...
}
我对其进行了基准测试:

  • 编译器:g++(GCC)10.2.0
  • 编译器选项:-O3-std=c++17-lboost\u计时器
  • OS:ArchLinux 5.10.8-arch1-1
intmain(intargc,char*argv[]){
使用ufast\u t=std::uint\u fast64\u t;
ufast_t constepr n=1'000'000'000ULL;
ufast_t sum=0;
{

std::cout我不认为这三种不同的方法应该有太大的不同,因为编译器可能会识别您正在做的事情并做最好的事情。最可读的变体是
开关,但它是基于意见的。它还应该明确地作为汇编程序发出一个跳转表,这应该是最快的t选项。但是只需相信编译器就可以了

最好将签出移出循环。我可以想象,编译器将能够识别param并不会更改并将签出移出循环本身,但这一点不太确定。要确保这一点,可以执行以下操作:

template<A param>
auto foo() {
    // This function will be generated once for each enumerator of A that is included
    // in the switch below. In each variant the runtime code will not contain these
    // checks against param.
    if constexpr(param == A::_1) { 
    } else if constexpr(param == A::_2) {
    } ...
}

template<A param>
auto loop() {
    for(auto i = 0ul; i < 1'000'000; ++i) {
        foo<param>();
    }
}

int main(int argc, char** argv) {
    switch(argc) {
        case A::_1: 
            loop<A::_1>();
            break; 
        ....
    }
}
模板
自动foo(){
//此函数将为包含的每个枚举数生成一次
//在下面的开关中。在每个变量中,运行时代码将不包含这些
//检查参数。
如果constexpr(param==A::1){
}如果constexpr(param==A::_2)为else{
} ...
}
模板
自动循环(){
用于(自动i=0ul;i<1'000'000;++i){
foo();
}
}
int main(int argc,字符**argv){
开关(argc){
案例A:_1:
loop();
打破
....
}
}

不幸的是,据我所知,没有很好的方法来打开argc。

这段代码不是在任何情况下都返回
param*param
(如果没有
A::X
匹配
param
)@churill,我返回
A:X*param
只是为了测试(显示只有一个简单的计算),并且再次只是为了测试,我不检查输入范围,因为在每个测试中,我在有效范围之间设置了一个随机数。
param
。所有调用的
param
是否相同?如果是,我会将检查移出循环。代码中还有更多的弱点:
sum=+f(argc)
???如果这是
sum+=…
?(如果编译器足够聪明,它会将其减少到一次迭代-因为检测到缺少的副作用。)通常,在这种情况下,我会比较编译器资源管理器上的asm输出以获得印象。(这也有助于检查在运行时真正执行的操作。编译器同时也擅长在编译时进行预计算。)比较生成的asm代码。在编译时猜测它可能是什么,而其输入值(在本例中为
param
template value参数)是在运行时指定的?
argc
在运行时被检查,但我们随后打开它并选择适当的代码路径。从这一点开始,我们将
param
作为编译时值,因此可以在编译时完成所有检查。我意识到我的注释不是很清楚,我将编辑它。