我正在做家庭作业,我必须手动优化嵌套循环(我的程序将在禁用优化的情况下编译)。分配的目标是在不到6秒的时间内运行整个程序(额外的功劳少于4.5秒)。
我只允许更改一小段代码,起点如下:
for (j=0; j < ARRAY_SIZE; j++) {
sum += array[j];
}
ARRAY_SIZE
为9973.此循环包含在另一个运行200,000次的循环中。这个特定的版本在16秒内运行。
到目前为止我所做的是更改实现以展开循环并使用指针作为我的迭代器:
(这些声明不会超过200,000次)
register int unroll_length = 16;
register int *unroll_end = array + (ARRAY_SIZE - (ARRAY_SIZE % unroll_length));
register int *end = array + (ARRAY_SIZE -1);
register int *curr_end;
curr_end = end;
while (unroll_end != curr_end) {
sum += *curr_end;
curr_end--;
}
do {
sum += *curr_end + *(curr_end-1) + *(curr_end-2) + *(curr_end-3) +
*(curr_end-4) + *(curr_end-5) + *(curr_end-6) + *(curr_end-7) +
*(curr_end-8) + *(curr_end-9) + *(curr_end-10) + *(curr_end-11) +
*(curr_end-12) + *(curr_end-13) + *(curr_end-14) + *(curr_end-15);
}
while ((curr_end -= unroll_length) != array);
sum += *curr_end;
使用这些技术,我能够将执行时间缩短到5.5秒,这将给予我充分的信任。然而;我确实想要获得额外的功劳,但我也很好奇我能做出哪些额外的优化我可能会忽略?
编辑#1(添加外部循环)
srand(time(NULL));
for(j = 0; j < ARRAY_SIZE; j++) {
x = rand() / (int)(((unsigned)RAND_MAX + 1) / 14);
array[j] = x;
checksum += x;
}
for (i = 0; i < N_TIMES; i++) {
// inner loop goes here
if (sum != checksum)
printf("Checksum error!\n");
sum = 0;
}
答案 0 :(得分:3)
您可以尝试将变量存储在CPU寄存器中:
register int *unroll_limit = array + (ARRAY_SIZE - (ARRAY_SIZE % 10));
register int *end = array + ARRAY_SIZE;
register int *curr;
尝试使用不同大小的手动循环来检查何时最大化缓存使用情况。
答案 1 :(得分:2)
我将假设您使用的是x86,如果您不是大部分仍然适用,但细节不同。
_aligned_malloc
或常规malloc
+手动对齐的16字节对齐数据。除此之外,在这种情况下您需要_mm_add_epi32
同时进行四次添加。 (不同的架构有不同的SIMD单元,所以请检查你的。)总而言之,我猜1&amp; 2是最容易和最可行的,并且将获得足够的性能(例如,Core 2 Duo上的8倍)。但是,这一切都归结为了解您的硬件和编程PIC需要完全不同的优化(例如,指令级手动流水线操作),而不是普通的PC。
答案 2 :(得分:1)
尝试在页面边界上对齐数组(即4K)
尝试使用更宽的数据类型进行计算,即64位而不是32位整数。这样您就可以一次添加2个数字。最后一步将两半加起来。
将部分数组或计算转换为浮点数,因此可以并行使用FPU和CPU
我不希望允许以下建议,但无论如何我都提到了
答案 3 :(得分:0)
如果数组值没有改变,你可以记住总和(即在第一次运行时计算它,并在后续运行中使用计算的总和)。
答案 4 :(得分:-1)
一些不错的优化技巧:
因此,如果您要使用数组,请尝试使用:
register int idx = ARRAY_SIZE - 1;
register int sum = 0;
do {
sum += array[idx];
} while (idx-- % 10 != 0);
do {
sum += array[idx] + array[idx - 1] + array[idx - 2] + array[idx - 3] + array[idx - 4] + array[idx - 5] + array[idx - 6] + array[idx - 7] + array[idx - 8] + array[idx - 9];
} while (idx -= 10);
// now we don't use a comparison and the ZERO flag will be set in FLAG
// register on which we can conditional jump. With a comparison you do VALUE - VALUE
// and then check if the ZERO flag is set or the NEGATIVE flag or whatever you are testing on