在C ++中快速划分的好方法?

时间:2009-05-22 20:32:48

标签: c++ performance math

有时我会看到并使用以下变体来实现C ++中具有浮点数的快速除法。

// orig loop
double y = 44100.0;
for(int i=0; i<10000; ++i) {
double z = x / y;
}

// alternative
double y = 44100;
double y_div = 1.0 / y;

for(int i=0; i<10000; ++i) {
double z = x * y_div;
}

但最近有人暗示这可能不是最准确的方式。

有什么想法吗?

11 个答案:

答案 0 :(得分:21)

在几乎每个CPU上,浮点除数是浮点乘法的几倍,因此乘以除数的倒数是一个很好的优化。缺点是在某些处理器上有可能会丢失非常小部分精度 - 例如,在现代x86处理器上,64位浮点运算实际上是在使用时使用80位进行内部计算默认的FPU模式,并将其存储在变量中将导致这些额外的精度位根据您的FPU舍入模式(默认为最接近)进行截断。只有在连接许多浮点运算并且不得不担心错误累积时,这才真正重要。

答案 1 :(得分:8)

Wikipedia同意这可以更快。链接的文章还包含一些可能感兴趣的其他快速划分算法。

我猜想任何具有工业强度的现代编译器都会为你做出优化,如果它会让你获利的话。

答案 2 :(得分:5)

你原来的

// original loop:
double y = 44100.0;

for(int i=0; i<10000; ++i) {
    double z = x / y;
}

可轻松优化

// haha:
double y = 44100.0;
double z = x / y;

并且性能非常好。 ; - )

编辑:人们继续投票,所以这里不是那么有趣的版本:

如果有一种通用的方法可以让所有情况下的分割更快,那么您认为编译器编写者现在可能不会发生这种情况吗?他们当然会这样做。此外,一些做FPU电路的人也不是很愚蠢。

因此,获得更好性能的唯一方法是了解您手头的具体情况并为此制定最佳代码。很可能这完全浪费你的时间,因为你的程序由于某些其他原因(例如在循环不变量上执行数学运算)而很慢。去找一个更好的算法。

答案 3 :(得分:3)

在使用gcc的示例中,带有选项-O3 -ffast-math的除法产生与不带-ffast-math的乘法相同的代码。 (在周围有足够挥发物的测试环境中,环路仍在那里。)

因此,如果你真的想要优化这些分歧并且不关心后果,那就是要走的路。乘法似乎快了大约15倍,顺便说一句。

答案 4 :(得分:2)

乘法比除法快,所以第二种方法更快。它可能稍微不准确,但除非你正在做核心数字,否则准确度应该绰绰有余。

答案 5 :(得分:2)

音频,嗯?如果您有五个音轨同时运行,那么这不仅仅是每秒44100个分区。毕竟,即使是简单的推子也会消耗周期。这只是一个相当简单,最小的例子 - 如果你想拥有一个eq和一个压缩器怎么办?也许有点混响?可以说,你的总数学预算会很快被吃掉。 确实在这些情况下有一点额外的性能。

Profilers很好。 Profilers是你的朋友。 Profilers值得口交和布丁。但是你已经知道主要的瓶颈在音频工作中的位置 - 它处于循环中处理样品,你做得越快,你的用户就越快乐。尽你所能!乘以倒数,尽可能移位(exp(x * y)= exp(x)* exp(y),毕竟),使用查找表,通过引用而不是值来引用变量(减少堆栈上的推/弹) ),重构术语等。 (如果你很好,你会嘲笑这些基本的优化。)

答案 6 :(得分:2)

处理音频时,我更喜欢使用定点数学。我想这取决于你需要的精度水平。但是,让我们假设16.16个定点整数可以做(意味着高16位是整数,低16是分数)。现在,所有计算都可以作为简单的整数数学来完成:

unsigned int y = 44100 << 16;
unsigned int z = x / (y >> 16);  // divisor must be the whole number portion

或者使用宏来帮助:

#define FP_INT(x) (x << 16)
#define FP_MUL(x, y) (x * (y >> 16))
#define FP_DIV(x, y) (x / (y >> 16))

unsigned int y = FP_INT(44100);
unsigned int z = FP_MUL(x, y);

答案 7 :(得分:1)

我从原始帖子中假设x不是那里显示的常量,但可能来自数组的数据,因此x [i]可能是数据的来源,类似于输出,它将被存储在内存中的某处

我建议如果循环计数确实是10,000,就像在原始帖子中那样,它会产生一些差别,因为整个循环在现代cpu上甚至不会花费几分之一毫秒。如果循环计数确实非常高,可能是1,000,000或更多,那么我预计内存访问的成本可能会使更快的操作完全无关紧要,因为无论如何它总是在等待数据。

我建议尝试使用你的代码和测试它是否真的在运行时间产生任何显着差异,如果它没有,那么只需编写简单的分区,如果这是算法需要的。

答案 8 :(得分:1)

这是用倒数做的问题,你仍然需要在你实际除以Y之前进行除法。除非你只用Y除以我想这可能是有用的。这是不太实际的,因为除法是用二进制方法用类似的算法完成的。

答案 9 :(得分:0)

我循环10,000次只是为了让代码花费足够长的时间来轻松测量时间?或者你有10000个数字除以相同的数字?如果是前者,则把“y_div = 1.0 / y;” 循环中,因为它是操作的一部分。

如果是后者,是的,浮点乘法通常比除法快。不过,请不要根据猜测将代码从明显变为神秘。基准测试首先找到慢点,然后优化它们(并在之前和之后进行测量,以确保您的想法实际上导致改进)

答案 10 :(得分:-1)

在像80286这样的旧CPU上,浮点运算非常慢,我们采用了很多技巧来加快速度。

在现代CPU上,浮点数学的速度非常快,优化编译器通常可以通过微调来实现奇迹。

几乎不值得采用这样的小微优化。

尝试使您的代码简单且具有傻瓜风格。只有你找到一个真正的瓶颈(使用分析器)你会想到浮点计算中的优化。