【问题标题】:if-else vs Ternary function call performance [duplicate]if-else vs三元函数调用性能[重复]
【发布时间】:2019-12-21 08:08:59
【问题描述】:

在我的 C++ 代码中,我必须根据给定条件在两个采用相同参数的函数之间进行选择。我可以写:

if (condition) 
   return foo(a, b, c);
else
   return bar(a, b, c);

或者:

return (condition? foo : bar)(a, b, c);

但这两种方式哪一种更快?

编辑:

我尝试使用此代码进行测试:

#include <cmath>
#include <chrono>
#include <iostream>

using namespace std;

void foo(float x, float y, int param)
{
    pow(x+y, param);
}

void bar(float x, float y, int param)
{
    pow(x+y, param);
}

int main() {

    const int loops = 1000;

    auto t1 = chrono::high_resolution_clock::now();
    for(int i = 0; i < loops; i++)
        for(int j = 0; j < loops; j++)
            (i==j? foo : bar)(i, j, 2);

    auto t2 = chrono::high_resolution_clock::now();

    for(int i = 0; i < loops; i++)
        for(int j = 0; j < loops; j++)
            if(i==j)
                foo(i, j, 2);
            else
                bar(i, j, 2);

    auto t3 = chrono::high_resolution_clock::now();

    cout << "ternary: " << (chrono::duration_cast<chrono::microseconds>(t2-t1).count()) << "us" << endl;
    cout << "if-else: " << (chrono::duration_cast<chrono::microseconds>(t3-t2).count()) << "us" << endl;
    return 0;
}

使用更新后的测试代码:

 ternary: 70951us
 if-else: 67962us

【问题讨论】:

  • 任何体面的编译器都会为两者输出相同的程序集。这是下一级的过早优化,你不应该关心的事情。选择最易读且适合您的项目风格的那个。
  • 到目前为止你做过任何基准测试吗?为什么你会认为任何一种方法都应该优于另一种方法。您的问题不清楚。
  • @SombreroChicken 看起来gccclang 都为这两种情况产生了不同的组装,尽管我不清楚是否存在任何真正的性能差异。请注意,带有条件运算符的那个首先确定要调用哪个函数,然后在条件分支之外实际执行调用。
  • time_t 是基准测试时的钝器。它提供秒数。接下来,与写入控制台的成本相比,条件的成本微不足道。我认为您主要测量cout &lt;&lt; blah
  • @Hydren 首先,您应该在代码的定时部分中删除所有couts。打印速度相对较慢,因此您可能只是在计算打印输出所需的时间。

标签: c++ performance if-statement function-pointers ternary-operator


【解决方案1】:

从 cmets 迁移而来:

任何体面的编译器都会为两者输出相同的程序集。这是下一级的过早优化,你不应该关心的事情。选择最易读且适合您的项目风格的那个。 ——Sombrero Chicken

【讨论】:

  • cmets 是评论而不是答案可能是有原因的。
  • @πάνταῥεῖ 我认为这句话恰到好处,作为一个答案看起来不错。
  • @πάνταῥεῖ 不幸的是,很多时候,这个理由是错误的。这是其中之一!评论是为了要求澄清,并有论据:)
【解决方案2】:

性能没有问题。编译器可以为这两种情况生成相同的目标代码。

但是在应用这些结构时存在差异。

对于条件运算符,必​​须推导出其第二个和第三个操作数的公共类型。

例如如果你有以下功能

void foo( int, int, int ) {}
int bar( int, int, int ) { return 10; } 

那么您可以使用演示程序中显示的 if 语句

#include <iostream>

void foo( int, int, int ) {}
int bar( int, int, int ) { return 10; }         

int main()
{
    int x;
    std::cin >> x;

    if ( x < 10 )
    {
        foo( x, x, x );
    }
    else
    {
        bar( x, x, x );
    }
}

但是你可能不会写,因为它看起来是等效的代码

#include <iostream>

void foo( int, int, int ) {}
int bar( int, int, int ) { return 10; }         

int main()
{
    int x;
    std::cin >> x;

    x < 10 ? foo( x, x, x ) : bar( x, x, x );
}

因为编译器无法推断出调用foo( x, x, x )bar( x, x, x ) 的共同类型。第一个的类型为void,第二个的类型为int

当然你也可以写

    x < 10 ? foo( x, x, x ) : ( void )bar( x, x, x );

但有时这会在使用复合表达式时降低代码的可读性。而且有时甚至不可能推导出通用类型,因为表达式之间没有显式或隐式转换,尤其是对于用户定义的类型。

另一个例子。你可以编写一个函数让编译器推断它的返回类型。

#include <iostream>

constexpr auto factorial( unsigned long long int n )
{
    if ( n < 2 )
    {
        return n;
    }
    else
    {
        return n * factorial( n - 1 );
    }        
}

int main()
{
    int a[factorial( 5 )];

    std::cout << "The array has " << sizeof( a ) / sizeof( *a ) << " elements\n";
}

程序输出是

The array has 120 elements

另一方面,你可能不会写

#include <iostream>

constexpr auto factorial( unsigned long long int n )
{
    return n < 2 ? n : n * factorial( n - 1 );
}

int main()
{
    int a[factorial( 5 )];

    std::cout << "The array has " << sizeof( a ) / sizeof( *a ) << " elements\n";
}

编译器无法推断函数的返回类型。

所以有时甚至在使用 if 语句或条件表达式之间没有选择:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 2015-06-05
    • 1970-01-01
    • 2018-08-07
    相关资源
    最近更新 更多