我只是在做一些基准测试,结果发现, fabsf()
往往喜欢比慢10倍fabs()
所以我拆开它和原来的double
版本使用fabs
指令, float
版本是没有的。 这能提高? 这是更快,但没有这么多,我担心它可能无法正常工作,这是一个有点过于低级:
float mabs(float i)
{
(*reinterpret_cast<MUINT32*>(&i)) &= 0x7fffffff;
return i;
}
编辑:对不起忘了编译器 - 我还是用好老VS2005,没有特殊的库。
您可以使用轻松地测试不同的可能性, 下面的代码 。 它本质上考验你对天真的模板ABS和bitfiddling std::abs
。 毫不奇怪,天真模板腹肌胜。 嗯,有点意外的是获胜。 我期望std::abs
是同样快。 需要注意的是-O3
实际上使事情变得更慢(至少在coliru)。
Coliru的主机系统显示这些时间:
random number generation: 4240 ms
naive template abs: 190 ms
ugly bitfiddling abs: 241 ms
std::abs: 204 ms
::fabsf: 202 ms
而这些时间的VirtualBox的VM上的酷睿i7运行拱与GCC 4.9:
random number generation: 1453 ms
naive template abs: 73 ms
ugly bitfiddling abs: 97 ms
std::abs: 57 ms
::fabsf: 80 ms
而这些时间上MSVS2013(Windows 7的64位):
random number generation: 671 ms
naive template abs: 59 ms
ugly bitfiddling abs: 129 ms
std::abs: 109 ms
::fabsf: 109 ms
如果我没有做这个基准测试代码中的一些公然明显的错误(不要拍我了吧,我在约2分钟写了这件事),我会说只是使用std::abs
,或模板版本如果原来是你稍快。
编码:
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
#include <math.h>
using Clock = std::chrono::high_resolution_clock;
using milliseconds = std::chrono::milliseconds;
template<typename T>
T abs_template(T t)
{
return t>0 ? t : -t;
}
float abs_ugly(float f)
{
(*reinterpret_cast<std::uint32_t*>(&f)) &= 0x7fffffff;
return f;
}
int main()
{
std::random_device rd;
std::mt19937 mersenne(rd());
std::uniform_real_distribution<> dist(-std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
std::vector<float> v(100000000);
Clock::time_point t0 = Clock::now();
std::generate(std::begin(v), std::end(v), [&dist, &mersenne]() { return dist(mersenne); });
Clock::time_point trand = Clock::now();
volatile float temp;
for (float f : v)
temp = abs_template(f);
Clock::time_point ttemplate = Clock::now();
for (float f : v)
temp = abs_ugly(f);
Clock::time_point tugly = Clock::now();
for (float f : v)
temp = std::abs(f);
Clock::time_point tstd = Clock::now();
for (float f : v)
temp = ::fabsf(f);
Clock::time_point tfabsf = Clock::now();
milliseconds random_time = std::chrono::duration_cast<milliseconds>(trand - t0);
milliseconds template_time = std::chrono::duration_cast<milliseconds>(ttemplate - trand);
milliseconds ugly_time = std::chrono::duration_cast<milliseconds>(tugly - ttemplate);
milliseconds std_time = std::chrono::duration_cast<milliseconds>(tstd - tugly);
milliseconds c_time = std::chrono::duration_cast<milliseconds>(tfabsf - tstd);
std::cout << "random number generation: " << random_time.count() << " ms\n"
<< "naive template abs: " << template_time.count() << " ms\n"
<< "ugly bitfiddling abs: " << ugly_time.count() << " ms\n"
<< "std::abs: " << std_time.count() << " ms\n"
<< "::fabsf: " << c_time.count() << " ms\n";
}
哦,并回答您的实际问题:如果编译器不能生成更高效的代码,我怀疑有一种更快的方式保存为微型优化的汇编,特别是对于像这样的基本操作。
还有,在这里打球很多东西。 首先,在的x87协处理器被弃用,取而代之的SSE / AVX的,所以我很惊讶地看了你的编译器仍然使用fabs
指令。 这很可能是谁张贴在这个问题上的基准答案他人使用支持SSE的平台。 您的结果可能会完全不同。
我不知道为什么你的编译器使用了不同的逻辑fabs
和fabsf
。 这是完全有可能加载一个float
到x87堆栈,并使用fabs
很容易就可以了指令。 与自己复制本,没有编译器支持的问题,就是你不能操作集成到编译器的正常优化管道:如果你说“加载此浮动,使用fabs
指令,这个浮动回到记忆”,那么,编译器会做到这些......它可能涉及将回内存已经准备好要处理的浮动,在加载回使用fabs
指令,把它背到内存中,并加载它再次x87堆栈恢复正常的,优化的管道。 这是因为只需要做四浪费负载存储操作fabs
。
总之,你不可能击败了浮点运算的综合编译器的支持。 如果没有这种支持,内联汇编可能只是使事情更慢于他们想必已经是。 为你做的跑得最快的家伙甚至可能是使用的fabs
功能,而不是fabsf
你的花车功能。
作为参考,现代编译器和现代的平台上使用SSE指令andps
(为浮动),并andpd
(双打),以进出位标志,非常喜欢你这样做你自己,而是躲着所有语言的语义问题。 他们都一样快。 现代编译器也可检测状图案x < 0 ? -x : x
x < 0 ? -x : x
和产生最佳andps
/ andpd
指令,而不需要编译器本征的。
你有没有尝试std::abs
过载float
? 这将是规范的C ++的方式。
另外顺便说一句,我要指出,你的位,修改版本确实违反了严格走样规则(除到较基本假设int
和float
具有相同的尺寸),因此将是不确定的行为。