6

在我正在分析的应用程序中,我发现在某些情况下,此函数能够占用总执行时间的 10% 以上。

正在使用 MSVC++ 2008 编译器,以供参考……我不记得 modf 是否映射到单个指令,或者是否有任何方法可以使其更快。

有关 sqrt 函数的类似问题,请参见此处

与 sqrt 不同,我真的不知道 modf 是如何工作的。有组装操作吗?例如你可以这样做:

modf(float input,int &intPart, float &floatPart)
{
 intPart= (int)input;
 floatPart= input - intPart;
}

但我认为这会导致强制转换/转换等的惩罚。快速实现如何工作?

4

6 回答 6

2

一个好的实现modf可以非常快(在当前硬件上大约需要 10 个周期)。一个糟糕的实现可能会非常慢(大约 100 个周期)。可以想象,一个构思非常糟糕的实现可能需要 1000 个周期。我不知道 Microsoft 的实现情况如何,但是您可能会查看各种开源 C 库中的许多好的实现。

您提出的实现采用了一些捷径,并且不符合 C 标准;input特别是在太大而无法成功转换为整数的情况下,它将表现得相当严重。在某些情况下,它也会得到零错误的符号,但你可能不在乎。

另请注意,您最好使用支持 C99 标准的编译器/C 库,因为这样您就可以利用该modff函数并避免在双精度之间进行转换的开销。我知道英特尔的数学库(随他们的编译器一起提供)具有出色的modf实现modff。GCC 还支持 C99 单精度变体。

FWIW,我对您提出的实现进行了基准测试,并且(假设编译器代码生成出色),它比英特尔库快约 50% modff(然而,英特尔的实现为所有输入提供了正确的结果)。我测试过的最快的正确实现仅比您的实现慢 15%(但同样,为所有输入提供正确的结果,甚至正确设置浮点状态标志以启动)。

于 2010-04-14T18:30:54.027 回答
2

你在这里得到了很好的答案,但其他 90% 的时间都去哪儿了?

不要查看每个例程的独占时间

查看每行代码包含的时间百分比,如果可能的话,让它包括阻塞时间,而不仅仅是 CPU 时间。

这样,你很可能会发现那部分时间,你甚至不需要modf函数或其他函数中。

获取该信息的一种简单方法是这种技术

补充:当你发现你可以做的优化时,预计总执行时间会减少,但不要指望百分比必然会下降。例如,如果您摆脱其他东西,您在modf和/或sqrt中的时间百分比实际上可能会上升,或者如果您发现可以记住它们(因此减少它们的调用次数),它们可能会下降。

在这种优化方法中,您可以将程序的执行历史视为一个大调用树,而您正在寻找的是可以修剪掉的整个分支。更重要的是,由于一行代码可以出现在调用树的多个分支中,因此将其修剪为一个即可。

于 2010-04-14T17:57:28.837 回答
1

modf确实应该是一个非常快的函数,所以问题可能是它仍然是一个函数(即,没有被内联)。您可以尝试使用库中完全相同的代码,但在标头中的内联静态函数中允许编译器内联它。

当函数被内联时,如果你总是只使用尾数/指数之一,编译器应该足够聪明,只发出代码来计算那部分,进一步加快速度。

如果您仍然对自己滚动感兴趣,请查看wikipedia on the floating point format

于 2010-04-14T13:57:19.310 回答
0

您的实现可能是 x86 上最快的。尽管请记住,您将支持的输入范围限制为int!

您可能希望将编译器设置为使用 SSE(2) 进行浮点数学运算,因为这消除了(可能很慢)用于截断的控制字更改。

于 2010-04-14T18:32:04.117 回答
0

我还看到在 DLL 中调用小例程时(如 CRT 的情况)在进出 DLL 时会受到胶水代码的影响。在这种情况下,即使实现本身是相同的,滚动你自己的并改变它的编译方式(例如,内联它)可以给你带来性能提升。YMMV、POITROAE 等

于 2010-04-14T18:08:06.193 回答
0

请注意,该库必须提供适用于所有极端情况的最快解决方案——对于这样的情况,这会增加相当多的复杂性。

如果您的带有强制转换的版本适用于您的程序,这意味着您没有任何超出 int 范围的浮点数,并且您已经确认它对于负数可以正常工作,或者您没有关心他们,那么它可能会快一点。

于 2010-04-14T17:46:52.337 回答