c++函数静态局部,性能好
C++ Function static local, performance?
我有一个函数,它必须根据代表操作符类型的成员枚举的值返回真或假。
我想知道以下选项中最快的是什么,因为我不确定编译器会做什么隐式优化,如果有的话。
inline bool isBinaryOper( void ) const // Fastest i assume.
{
static const bool arr[] =
{
true, // E_PLUS
true, // E_MINUS
true, // E_MULTIPLY
true, // E_DIVIDE
false, // E_LPARENT
false, // E_RPARENT
false, // E_COMMA
false // E_SEMICOLON
};
return arr[ size_t( this->_eType ) ]; // Assuming values are valid indexes.
}
或:
inline bool isBinaryOper( void ) const
{
switch( this->_eType )
{
case E_PLUS : return true;
case E_MINUS : return true;
case E_MULTIPLY : return true;
case E_DIVIDE : return true;
case E_LPARENT : return false;
case E_RPARENT : return false;
case E_COMMA : return false;
case E_SEMICOLON : return false;
default : ...
};
}
或者,我猜这和上一个很像:
inline bool isBinaryOper( void ) const
{
if ( this->_eType == E_PLUS ) return true;
else if ( this->_eType == E_MINUS ) return true;
// etc...
}
哪个是最快的,为什么?
这个问题让我觉得是一个过早优化的例子,但就其价值而言,我还是选择switch
语句,尽管它可能会稍微慢一些,因为:
-
你不会注意到减速。
-
假设您填写了
default:
的情况下,开关实现保护您免受无效数据或更改枚举定义,这将简化调试。 -
gcc和clang(可能还有其他好的编译器)都将优化转换为二进制搜索或跳转表,这取决于替代方案的排序方式和目标平台的精确特征。在这两种情况下,它都不会简单地对每个可能的值进行线性序列检查,比如
if ... else if ... else if ...
选项,这几乎肯定是最慢的。这使您不必考虑如何对备选项排序,特别是当您可能需要具有不同排序的各种布尔函数时。除非您是计算机体系结构方面的专家,否则您可以合理地假设您的编译器更了解它。
使用该值作为数组的索引要比switch
语句快得多。
你的第二个和第三个代码块的表现差不多。但是第一个函数可以快速获取索引并使用它来访问所需的数组元素。这是我的偏好;但是,您可能还需要添加错误检查,以确保参数在预期范围内。
如果对枚举进行了分区,以便所有返回true的值都在返回false的值之前,那么您可以这样做:
inline bool isBinaryOper() const
{
return this->_eType < E_LPARENT;
}
我想说,数组查找很可能是最有效的事情。它根本没有"脂肪"供优化编译器删减。
当然,这个表很可能会被放在其他段()中。Rdata而不是.text),因此表将占用更多的缓存行。然而,你遇到任何负面影响的机会是可以忽略不计的。
当然,编译器可能会在表查找中实现具有密集大小写值的switch
。这将给'naïve'级联if实现带来巨大的改进。然而,不能保证这将以最直接的方式完成。
一个非常简单的快速实验证实了我的推理:
#include <stdio.h>
#include <time.h>
enum E
{
E0,
E1,
E2,
E3,
E4,
E5,
E6,
E7,
};
bool f1(E x)
{
if (x > E7 || x < E0)
throw "ohbadbad";
static const bool t[] =
{
true,
true,
true,
true,
false,
false,
false,
false,
};
return t[x];
}
bool f2(E x)
{
switch (x)
{
case E0: return true;
case E1: return true;
case E2: return true;
case E3: return true;
case E4: return false;
case E5: return false;
case E6: return false;
case E7: return false;
default: throw "ohbadbad";
}
}
int main(int argc, char* argv[])
{
bool (*f)(E) = (argc > 1 && argv[1][0] == 's')
? f2
: f1;
clock_t t = clock();
int r = 0;
for (int i = 0; i < 10000; ++i)
for (int j = 0; j < 100000; ++j)
r += f((E)(j & E7));
printf("%d %I64dn", r, __int64(clock() - t));
return 0;
}
使用msvc++ 16在x86和x64上编译(带-O2选项),f1
给出的时钟比f2
好3倍以上。
分析目标代码,很容易看出原因:switch
确实是使用表实现的——但它是一个标签表。代码从表中获取一个地址,然后跳转到该地址。一个分支有效地执行return 0
,另一个执行return 1
。这不仅是一个不必要的步骤,而且还会导致频繁的分支错误预测。
- 如何比较两个函数的速度和性能
- 调用不在基类中的派生类函数而不进行动态强制转换,以最大程度地提高性能
- 函数局部静态变量:从性能角度来看的优点/缺点
- C++ 将函数指针与最佳性能相结合
- 为什么使用默认构造函数"{}"而不是"= default"存在性能变化?
- 通过默认复制构造函数比较 C++ 字符串是否会影响性能,原因为何?
- 隐式转换函数的返回对象时是否会影响性能?
- 虚拟函数调用的性能作为 for 循环中的上限
- 如何提高对 std::函数侦听器的分发性能?
- 性能函数调用与乘以 1
- std::函数有性能问题,如何避免?
- isspace 函数的性能警告,从 int 转换为布尔值
- C++函数名称对于性能来说太长
- 性能 - 使用字符串构造函数与使用串联
- if-else 与三元函数调用性能
- 加快 R 性能或将 R 函数转换为C++函数
- 递归函数和性能在C++
- 性能:否则如果 vs if 在已经返回的函数中
- C++构造函数性能
- 每帧的性能函数调用