是否有一个快速fabsf更换为C“浮动” ++?(Is there a fast fabsf rep

2019-10-20 02:32发布

我只是在做一些基准测试,结果发现, fabsf()往往喜欢比慢10倍fabs() 所以我拆开它和原来的double版本使用fabs指令, float版本是没有的。 这能提高? 这是更快,但没有这么多,我担心它可能无法正常工作,这是一个有点过于低级:

float mabs(float i)
{
    (*reinterpret_cast<MUINT32*>(&i)) &= 0x7fffffff;
    return i;
}

编辑:对不起忘了编译器 - 我还是用好老VS2005,没有特殊的库。

Answer 1:

您可以使用轻松地测试不同的可能性, 下面的代码 。 它本质上考验你对天真的模板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";
}

哦,并回答您的实际问题:如果编译器不能生成更高效的代码,我怀疑有一种更快的方式保存为微型优化的汇编,特别是对于像这样的基本操作。



Answer 2:

还有,在这里打球很多东西。 首先,在的x87协处理器被弃用,取而代之的SSE / AVX的,所以我很惊讶地看了你的编译器仍然使用fabs指令。 这很可能是谁张贴在这个问题上的基准答案他人使用支持SSE的平台。 您的结果可能会完全不同。

我不知道为什么你的编译器使用了不同的逻辑fabsfabsf 。 这是完全有可能加载一个float到x87堆栈,并使用fabs很容易就可以了指令。 与自己复制本,没有编译器支持的问题,就是你不能操作集成到编译器的正常优化管道:如果你说“加载此浮动,使用fabs指令,这个浮动回到记忆”,那么,编译器会做到这些......它可能涉及将回内存已经准备好要处理的浮动,在加载回使用fabs指令,把它背到内存中,并加载它再次x87堆栈恢复正常的,优化的管道。 这是因为只需要做四浪费负载存储操作fabs

总之,你不可能击败了浮点运算的综合编译器的支持。 如果没有这种支持,内联汇编可能只是使事情更慢于他们想必已经是。 为你做的跑得最快的家伙甚至可能是使用的fabs功能,而不是fabsf你的花车功能。

作为参考,现代编译器和现代的平台上使用SSE指令andps (为浮动),并andpd (双打),以进出位标志,非常喜欢你这样做你自己,而是躲着所有语言的语义问题。 他们都一样快。 现代编译器也可检测状图案x < 0 ? -x : x x < 0 ? -x : x和产生最佳andps / andpd指令,而不需要编译器本征的。



Answer 3:

你有没有尝试std::abs过载float ? 这将是规范的C ++的方式。

另外顺便说一句,我要指出,你的位,修改版本确实违反了严格走样规则(除到较基本假设intfloat具有相同的尺寸),因此将是不确定的行为。



文章来源: Is there a fast fabsf replacement for “float” in C++?