整数除法还是浮点乘法?

时间:2013-09-27 23:00:30

标签: c++ optimization

如果必须计算给定int值的一小部分,请说:

int j = 78;
int i = 5* j / 4;

这比做的更快:

int i = 1.25*j; // ?

如果是,是否存在可用于决定使用哪个转化因子的转化因子,例如可以在同一时间内进行多次int除法float倍增?

编辑:我认为评论清楚地表明浮点数学会慢一点,但问题是,多少?如果我需要将每个float乘法替换为$ N $ int个分段,那么$ N $将不再值得吗?

3 个答案:

答案 0 :(得分:5)

你已经说过所有的价值都是动态的,这会产生影响。对于特定值5 * j / 4,整数操作将非常快,因为最糟糕的情况是编译器将它们优化为两个班次和一个加法,加上一些混乱以应对{ {1}}是否定的。如果CPU可以做得更好(单周期整数乘法或其他),那么编译器通常会知道它。当您编译一大类CPU(例如,生成最低公分母ARM代码)时,编译器优化此类事物的能力的限制基本上就会出现,编译器对此并不太了解硬件,因此不能总是做出好的选择。

我认为如果ja固定一段时间(但在编译时不知道),则可能会计算b一次,然后k = double(a) / b对于int(k * x)的许多不同值,对于许多不同的x值,可能比计算a * x / b更快。我不会指望它。

如果所有值每次都变化,那么计算x的浮点除法,其次是浮点乘法,似乎不会比整数乘法后跟整数更快师。但你永远不知道,测试它。

在现代处理器上为此提供简单的相对时序是不可能的,它实际上很大程度上取决于周围的代码。代码中的主要成本通常不是“实际”操作:它是“不可见”的东西,比如指令管道停止依赖,或者将寄存器溢出到堆栈或函数调用开销。是否可以内联执行此功能的功能可能比功能实际执行的功能更容易产生差异。就性能的确定性陈述而言,您基本上可以测试实际代码或关闭。但是,如果你的值以整数开头,那么对它们进行整数运算的速度要快于转换为1.25并执行类似数量的double运算。

答案 1 :(得分:1)

不可能脱离背景地回答这个问题。另外5*j/4通常不会产生与(int) (1.25*j)相同的结果,这是由于整数和浮点运算的属性,包括舍入和溢出。

如果你的程序主要进行整数运算,那么j到浮点的转换,乘以1.25,转换回整数可能是免费的,因为它使用了没有其他参与的浮点单位。

或者,在某些处理器上,操作系统可能会将浮点状态标记为无效,因此第一次进程使用它时,有一个异常,操作系统会保存浮点寄存器(包含来自另一个进程的值),恢复或初始化进程的寄存器,并从异常中返回。相对于正常的指令执行,这将花费大量时间。

答案还取决于程序正在执行的特定处理器模型的特性,以及操作系统,编译器如何将源转换为汇编,甚至可能取决于系统上正在执行的其他进程。

此外,5*j/4(int) (1.25*j)之间的性能差异通常太小而无法在程序中引起注意,除非它或类似的操作重复多次。 (并且,如果它们是,那么矢量化代码可能会带来巨大的好处,即使用许多现代处理器的单指令多数据[SIMD]功能一次执行多个操作。)

答案 2 :(得分:0)

对于您的情况,5*j/4将比1.25*j快得多,因为通过右移可以轻松操纵除以2的幂,并且5*j可以通过单个指令完成在某些体系结构上,例如x86上的LEA,或者ARM上的ADD。大多数其他指令最多需要2个指令,如j + j >> 2,但这些指令可能仍然比浮点乘法更快。此外,通过执行int i = 1.25*j,您需要在doubleint之间进行2次转换,以及2次跨域移动,这可能会非常昂贵(这就是为什么有不同的SSE或AVX指令完全执行同样的事情,只是在不同的领域)。使用int multiply / divide也允许编译器通过常量

来优化除法

对于其他情况,当分数不能用浮点表示时(如3*j/10)那么使用int multiply / divide会更正确(因为0.3在二进制浮点中不完全是0.3),并且最可能更快(因为编译器可以通过将除以乘法逆转换为乘法来优化除以)。参见

对于ij属于浮点类型的情况,乘以另一个浮点值可能会更快。因为在float和int域之间移动值需要时间,并且int和float之间的转换也需要时间,如上所述

一个重要的区别是,如果j太大,5*j/4会溢出,但1.25*j不会

也就是说,对于“更快”和“速度更快”的问题,没有一般性的答案,因为它取决于特定的架构和特定的上下文。您必须在系统上进行测量并做出决定。但是如果表达式重复执行很多值,那么就该转到SIMD了

另见