双精度运算:32 位与 64 位机器
Double-precision operations: 32-bit vs 64-bit machines
与在 32 位机器上执行相比,在 64 位机器上执行 64 位操作(例如双精度操作)时,为什么我们看不到两倍的性能?
在32位机器上,我们不是需要从内存中取两倍吗?更重要的是,我们不需要两倍的周期来执行 64 位操作吗?
“64 位机器”是一个含糊不清的术语,但通常表示处理器的通用寄存器是 64 位宽的。比较8086 and 8088,它们具有相同的指令集,在这个意义上都可以称为16位处理器。
当这个短语在这个意义上使用时,它与内存总线的宽度、CPU内部内部总线的宽度以及ALU高效运行的能力无关在 32 位或 64 位宽数据上。
您的问题还假设乘法中最难的部分是将操作数移动到处理器内部负责乘法的单元,即使操作数来自内存并且总线是32 位宽,因为延迟 != 吞吐量。此外,关于浮点乘法的数学,64 位乘法并不比 32 位乘法难两倍,大约是 (53/24)2 倍(但是,无论通用寄存器的宽度如何,晶体管都可以在那里有效地计算双精度乘法)。
In a 32-bit machine, don't we need to fetch from memory twice as much?
没有。在大多数现代 CPU 中, 内存总线宽度为 at least 64 bits. Newer microarchitectures may have wider bus. Quad-channel memory will have a minimum 256-bit bus. Many contemporary CPUs even support 6 or 8-channel memory。所以你只需要 1 次获取就可以得到双倍的。除了大部分时间该值已经在缓存中,所以加载它不会花费太多时间。 CPU 每次加载的不是单个值,而是整个缓存行
more importantly, don't we need twice as much cycles to execute a 64-bit operation?
首先你应该知道 double 中有效位的实际数量是 53,所以它不是“两倍”更难。它是 float
中的数字(24 个有效位)的两倍多。当位数增加一倍时,加法和减法的难度增加一倍,而乘法的难度增加四倍。许多其他更复杂的操作将需要更多的努力
但是尽管数学工作更难,在大多数现代架构上,float 和 double 上的非内存操作通常是相同的,因为两者都将在同一组寄存器中完成同样ALU/FPU。那些强大的 FPU 可以在一个周期内添加两个双精度数,所以很明显,即使你可以更快地添加两个浮点数,它仍然会消耗 1 个周期。在旧的 Intel x87 中,内部寄存器的长度为 80 位,单精度和双精度都必须扩展到 80 位,因此它们的性能也将相同。无法在比 80 位扩展
更窄的类型中进行数学运算
使用 SIMD support 和 SSE2/AVX/AVX-512 一样,您将能够一次处理 2/4/8 个双打 (在其他 SIMD 中甚至更多ISAs),所以你可以看到像这样添加两个双打对于现代 FPU 来说只是一个小任务。然而,与 double 相比,使用 SIMD 我们可以在寄存器中容纳两倍的浮点数,因此如果您需要并行执行大量数学运算,浮点运算会更快。在一个循环中,如果您一次可以处理 4 个双打,那么您将能够在 8 个花车上做同样的事情
另一种 float
比 double
快的情况是当你处理一个巨大的数组时,因为更多的浮点数比 double 更适合同一个缓存行。因此,当您遍历数组
时,使用 double
会导致更多缓存未命中
与在 32 位机器上执行相比,在 64 位机器上执行 64 位操作(例如双精度操作)时,为什么我们看不到两倍的性能?
在32位机器上,我们不是需要从内存中取两倍吗?更重要的是,我们不需要两倍的周期来执行 64 位操作吗?
“64 位机器”是一个含糊不清的术语,但通常表示处理器的通用寄存器是 64 位宽的。比较8086 and 8088,它们具有相同的指令集,在这个意义上都可以称为16位处理器。
当这个短语在这个意义上使用时,它与内存总线的宽度、CPU内部内部总线的宽度以及ALU高效运行的能力无关在 32 位或 64 位宽数据上。
您的问题还假设乘法中最难的部分是将操作数移动到处理器内部负责乘法的单元,即使操作数来自内存并且总线是32 位宽,因为延迟 != 吞吐量。此外,关于浮点乘法的数学,64 位乘法并不比 32 位乘法难两倍,大约是 (53/24)2 倍(但是,无论通用寄存器的宽度如何,晶体管都可以在那里有效地计算双精度乘法)。
In a 32-bit machine, don't we need to fetch from memory twice as much?
没有。在大多数现代 CPU 中, 内存总线宽度为 at least 64 bits. Newer microarchitectures may have wider bus. Quad-channel memory will have a minimum 256-bit bus. Many contemporary CPUs even support 6 or 8-channel memory。所以你只需要 1 次获取就可以得到双倍的。除了大部分时间该值已经在缓存中,所以加载它不会花费太多时间。 CPU 每次加载的不是单个值,而是整个缓存行
more importantly, don't we need twice as much cycles to execute a 64-bit operation?
首先你应该知道 double 中有效位的实际数量是 53,所以它不是“两倍”更难。它是 float
中的数字(24 个有效位)的两倍多。当位数增加一倍时,加法和减法的难度增加一倍,而乘法的难度增加四倍。许多其他更复杂的操作将需要更多的努力
但是尽管数学工作更难,在大多数现代架构上,float 和 double 上的非内存操作通常是相同的,因为两者都将在同一组寄存器中完成同样ALU/FPU。那些强大的 FPU 可以在一个周期内添加两个双精度数,所以很明显,即使你可以更快地添加两个浮点数,它仍然会消耗 1 个周期。在旧的 Intel x87 中,内部寄存器的长度为 80 位,单精度和双精度都必须扩展到 80 位,因此它们的性能也将相同。无法在比 80 位扩展
更窄的类型中进行数学运算使用 SIMD support 和 SSE2/AVX/AVX-512 一样,您将能够一次处理 2/4/8 个双打 (在其他 SIMD 中甚至更多ISAs),所以你可以看到像这样添加两个双打对于现代 FPU 来说只是一个小任务。然而,与 double 相比,使用 SIMD 我们可以在寄存器中容纳两倍的浮点数,因此如果您需要并行执行大量数学运算,浮点运算会更快。在一个循环中,如果您一次可以处理 4 个双打,那么您将能够在 8 个花车上做同样的事情
另一种 float
比 double
快的情况是当你处理一个巨大的数组时,因为更多的浮点数比 double 更适合同一个缓存行。因此,当您遍历数组
double
会导致更多缓存未命中