最近我正在使用代码类似于:
的应用程序for (auto x = 0; x < width - 1 - left; ++x)
{
// store / reset points
temp = hPoint = 0;
for(int channel = 0; channel < audioData.size(); channel++)
{
if (peakmode) /* fir rms of window size */
{
for (int z = 0; z < sizeFactor; z++)
{
temp += audioData[channel][x * sizeFactor + z + offset];
}
hPoint += temp / sizeFactor;
}
else /* highest sample in window */
{
for (int z = 0; z < sizeFactor; z++)
{
temp = audioData[channel][x * sizeFactor + z + offset];
if (std::fabs(temp) > std::fabs(hPoint))
hPoint = temp;
}
}
.. some other code
}
... some more code
}
这是在一个图形渲染循环中,称为50-100次/秒,多个通道中的缓冲区高达192kHz。因此,很多数据都在最里面的循环中运行,并且分析显示这是一个热点。
在我看来,可以将浮点数转换为整数并删除符号位,然后仅使用临时值将其转换回来。它看起来像这样:
if ((const float &&)(*((int *)&temp) & ~0x80000000) > (const float &&)(*((int *)&hPoint) & ~0x80000000))
hPoint = temp;
这使渲染时间减少了12倍,同时仍然产生相同的有效输出。请注意,audiodata中的所有内容都要事先清理,以便不包含nans / infs / denormals,并且只有[-1,1]的范围。
是否存在此优化会产生错误结果的极端情况 - 或者,为什么标准库函数没有像这样实现?我认为它与处理非正常值有关吗?
e:浮点模型的布局符合ieee,sizeof(float)== sizeof(int)== 4
答案 0 :(得分:4)
好吧,您将浮点模式设置为符合IEEE标准。通常,对于像--fast-math
这样的开关,编译器可以忽略像NaN,INF和非正规的IEEE极端情况。如果编译器也使用内在函数,它可能会发出相同的代码。
BTW,如果你将采用IEEE格式,那么在比较之前不需要将反转回浮动。 IEEE格式很漂亮:对于所有正有限值,a<b
当且仅当reinterpret_cast<int_type>(a) < reinterpret_cast<int_type>(b)
答案 1 :(得分:4)
在我看来,可以将浮点数转换为整数并删除符号位,然后仅使用临时值将其转换回来。
不,你不能,因为这违反了strict aliasing rule。
是否存在此优化会产生错误结果的极端情况
从技术上讲,此代码会导致未定义的行为,因此始终会产生错误的&#34;结果&#34;。从某种意义上说,绝对值的结果总是意外或不正确,但从某种意义上说,如果程序有未定义的行为,你就无法推断程序的作用。
或者,为什么标准库函数没有像这样实现?
你的怀疑是合理的,处理非正规值和其他异常值是棘手的,stdlib函数也需要考虑这些,而另一个原因仍然是未定义的行为。
如果您关心效果,则采用一种(非)解决方案:
您可以使用联合而不是强制转换和指针。 不幸的是,这只适用于C,而不是C ++。这不会导致UB,但它仍然不可移植(尽管它可能适用于大多数,如果并非所有平台都使用IEEE-754。
union {
float f;
unsigned u;
} pun = { .f = -3.14 };
pun.u &= ~0x80000000;
printf("abs(-pi) = %f\n", pun.f);
但是,如果被授予,这可能或者可能不会比调用fabs()
更快。只有一件事是肯定的:它永远不会是正确的。
答案 2 :(得分:2)
您希望fabs()
在硬件中实现。毕竟,1980年有8087指令。你不会打败硬件。
答案 3 :(得分:1)
标准库函数如何实现它是......依赖于实现。因此,您可能会发现具有不同性能的标准库的不同实现。
我想你可能在int
不是32位的平台上遇到问题。您最好使用int32_t(cstdint&gt;)
据我所知,std :: abs之前是内联的吗?或者您观察到的优化主要是由于函数调用的抑制?
答案 4 :(得分:1)
关于重构如何改善绩效的一些观察:
如上所述,x * sizeFactor + offset
可以从内循环中分解出来
peakmode
实际上是一个改变函数行为的开关 - 制作两个函数而不是在循环中测试开关。这有两个好处:
temp
除sizeFactor
之前的划分可以推迟到channel
版本的peakmode
循环之外。
abs(hPoint)
更新时,都可以预先计算 hPoint
如果audioData
是向量的向量,则可以通过在audioData[channel]
循环体的开头引用channel
来获得一些性能优势,从而减少数组索引在z
循环中向下到一维。
最后,对您认为合适的fabs
的计算应用任何特定的优化。你在这里做的任何事都会损害便携性,所以这是最后的手段。
答案 5 :(得分:0)
在VS2008中,使用以下内容跟踪hpoint
和hIsNeg
的绝对值以记住它是正面还是负面,大约是使用fabs()
的两倍:
int hIsNeg=0 ;
...
//Inside loop, replacing
// if (std::fabs(temp) > std::fabs(hPoint))
// hPoint = temp;
if( temp < 0 )
{
if( -temp > hpoint )
{
hpoint = -temp ;
hIsNeg = 1 ;
}
}
else
{
if( temp > hpoint )
{
hpoint = temp ;
hIsNeg = 0 ;
}
}
...
//After loop
if( hIsNeg )
hpoint = -hpoint ;