其中哪一项计算效率更高,为什么?
A)重复数组访问:
for(i=0; i<numbers.length; i++) {
result[i] = numbers[i] * numbers[i] * numbers[i];
}
B)设置局部变量:
for(i=0; i<numbers.length; i++) {
int n = numbers[i];
result[i] = n * n * n;
}
是否必须计算重复的数组访问版本(使用指针运算),使第一个选项变慢,因为它正在执行此操作?:
for(i=0; i<numbers.length; i++) {
result[i] = *(numbers + i) * *(numbers + i) * *(numbers + i);
}
答案 0 :(得分:10)
任何足够复杂的编译器都会为所有三种解决方案生成相同的代码。我把你的三个版本变成了一个小的C程序(带有一个小调整,我将访问numbers.length
更改为宏调用,它给出了一个数组的长度):
#include <stddef.h>
size_t i;
static const int numbers[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 };
#define ARRAYLEN(x) (sizeof((x)) / sizeof(*(x)))
static int result[ARRAYLEN(numbers)];
void versionA(void)
{
for(i=0; i<ARRAYLEN(numbers); i++) {
result[i] = numbers[i] * numbers[i] * numbers[i];
}
}
void versionB(void)
{
for(i=0; i<ARRAYLEN(numbers); i++) {
int n = numbers[i];
result[i] = n * n * n;
}
}
void versionC(void)
{
for(i=0; i<ARRAYLEN(numbers); i++) {
result[i] = *(numbers + i) * *(numbers + i) * *(numbers + i);
}
}
然后我使用Visual Studio 2012使用优化(和调试符号,用于更漂亮的反汇编)编译它:
C:\Temp>cl /Zi /O2 /Wall /c so19244189.c
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
so19244189.c
最后,这是反汇编:
C:\Temp>dumpbin /disasm so19244189.obj
[..]
_versionA:
00000000: 33 C0 xor eax,eax
00000002: 8B 0C 85 00 00 00 mov ecx,dword ptr _numbers[eax*4]
00
00000009: 8B D1 mov edx,ecx
0000000B: 0F AF D1 imul edx,ecx
0000000E: 0F AF D1 imul edx,ecx
00000011: 89 14 85 00 00 00 mov dword ptr _result[eax*4],edx
00
00000018: 40 inc eax
00000019: 83 F8 09 cmp eax,9
0000001C: 72 E4 jb 00000002
0000001E: A3 00 00 00 00 mov dword ptr [_i],eax
00000023: C3 ret
_versionB:
00000000: 33 C0 xor eax,eax
00000002: 8B 0C 85 00 00 00 mov ecx,dword ptr _numbers[eax*4]
00
00000009: 8B D1 mov edx,ecx
0000000B: 0F AF D1 imul edx,ecx
0000000E: 0F AF D1 imul edx,ecx
00000011: 89 14 85 00 00 00 mov dword ptr _result[eax*4],edx
00
00000018: 40 inc eax
00000019: 83 F8 09 cmp eax,9
0000001C: 72 E4 jb 00000002
0000001E: A3 00 00 00 00 mov dword ptr [_i],eax
00000023: C3 ret
_versionC:
00000000: 33 C0 xor eax,eax
00000002: 8B 0C 85 00 00 00 mov ecx,dword ptr _numbers[eax*4]
00
00000009: 8B D1 mov edx,ecx
0000000B: 0F AF D1 imul edx,ecx
0000000E: 0F AF D1 imul edx,ecx
00000011: 89 14 85 00 00 00 mov dword ptr _result[eax*4],edx
00
00000018: 40 inc eax
00000019: 83 F8 09 cmp eax,9
0000001C: 72 E4 jb 00000002
0000001E: A3 00 00 00 00 mov dword ptr [_i],eax
00000023: C3 ret
请注意组件在所有情况下的完全相同。所以对你的问题的正确答案
该编译器的其中哪一项计算效率更高,为什么?
是: mu 。您的问题无法回答,因为它基于不正确的假设。答案都没有比其他答案更快。
答案 1 :(得分:2)
理论答案:
一个相当不错的优化编译器应该将版本A转换为版本B,并且只从内存执行一次加载。如果启用了优化,则应该没有性能差异。
如果禁用优化,版本A将会变慢,因为地址必须计算3次并且有3个内存加载(其中2个缓存并且非常快,但它仍然比重用寄存器慢)。
实际上,答案取决于您的编译器,您应该通过基准测试来检查。
答案 2 :(得分:1)
这取决于编译器,但它们都应该是相同的。
首先让我们看一下案例B
智能编译器会生成代码,只将值加载到寄存器中一次,因此无论是否使用其他变量都无关紧要,编译器会为mov
指令生成操作码并有价值登记。因此B
与A
相同。
现在让我们比较A
和C
。我们应该看一下opeators []
内联实现。 a[b]
实际上是*(a + b)
所以*(numbers + i)
与numbers[i]
相同,这意味着案例A
和C
是相同的。
所以我们总共(A==B) && (A==C)
(A==B==C)
如果你知道我的意思:)。