到目前为止,我一直在尝试优化我的程序,但是我的目标只有不到5秒。我的代码还能改变什么呢?
#include <stdio.h>
#include <stdlib.h>
#define N_TIMES 600000
#define ARRAY_SIZE 10000
int main(void) {
double *array = calloc(ARRAY_SIZE, sizeof(double));
double sum = 0;
int i, j;
printf("CS201 - Asgmt 4 - your name\n");
for (i = 0; i < N_TIMES; i++) {
for (j = 0; j < ARRAY_SIZE; j = j + 20) {
sum = sum + array[j] + array[j+1] + array[j+2] + array[j+3] +
array[j+4] + array[j+5] + array[j+6] + array[j+7] +
array[j+8] + array[j+9] + array[j+10] + array[j+11] +
array[j+12] + array[j+13] + array[j+14] + array[j+15] +
array[j+16] + array[j+17] + array[j+18] + array[j+19];
}
}
}
答案 0 :(得分:3)
删除所有无意义的过程。
#include <stdio.h>
int main(void) {
printf("CS201 - Asgmt 4 - your name\n");
return 0;
}
答案 1 :(得分:1)
让优化器为您进行优化。
我将代码粘贴到a.c
。
$ gcc a.c -o a
$ time ./a
CS201 - Asgmt 4 - your name
real 0m7.128s
user 0m7.032s
sys 0m0.004s
$ gcc -O2 a.c -o a
$ time ./a
CS201 - Asgmt 4 - your name
real 0m0.001s
user 0m0.000s
sys 0m0.000s
$
除了笑话,展开是好的。
如果数据大于缓存,则切换循环可能会有所帮助。 在我的电脑上它并没有产生任何显着的差异(数据只有80kb)。
线程可能是下一步。这样你可以使用多个核心。
如果您的服务器与我的家用电脑(具有非常好的图形卡)类似,那么如果您使用它,您将获得最佳结果。 使用显卡进行计算的流行库是CUDA。
答案 2 :(得分:1)
calloc
返回一个用零字节初始化的数组。考虑到您的计算机可能使用IEEE 754二进制浮点数,该数组中的所有double
值均为0.0
。
现在,你正在做的事情基本上是重复3亿次sum = sum + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0
。
因此如果需要sum
,我们可以优化循环:
sum = 0.0;
答案 3 :(得分:0)
使用乘法指令怎么样?
#include <stdio.h>
#include <stdlib.h>
#define N_TIMES 600000
#define ARRAY_SIZE 10000
int main (void){
double *array = calloc(ARRAY_SIZE, sizeof(double));
double sum = 0;
int i, j;
printf("CS201 - Asgmt 4 - your name\n");
for (i = 0; i < N_TIMES; i+=N_TIMES) {
for (j = 0; j < ARRAY_SIZE; j=j+20) {
sum = sum + array[j] + array[j+1] + array[j+2] + array[j+3] + array[j+4] + array[j+5] + array[j+6] + array[j+7] + array[j+8] + array[j+9] + array[j+10] + array[j+11] + array[j+12] +array[j+13] + array[j+14] + array[j+15] + array[j+16] + array[j+17] + array[j+18] + array[j+19];
}
sum *=N_TIMES;
}
return 0;
}
答案 4 :(得分:0)
首先,让我们尝试更改算法,因为这通常可以提供最佳效果。
方法#1。 由于所有内存都是由于calloc()而分配的内存被初始化为零,并且永远不会为其分配另一个值,因此分配的数组中的每个double都将具有值0.0。因此,分配的内存中所有双精度的总和始终为0.0。
方法#2。 所有元素的总和不会从一次迭代变为下一次迭代。所以,计算一次总和然后乘以N_TIMES。
其他一些选择(如果上述情况不可接受):
方法#3。 如果每次都必须遍历循环,那么让我们帮助编译器并行执行计算,以便一次添加的结果对下一次添加的影响不小。为此,在内循环中组织添加如下...
tmp[0] = array[j] + array[j+1];
tmp[1] = array[j+2] + array[j+3];
tmp[2] = array[j+4] + array[j+5];
tmp[3] = array[j+6] + array[j+7];
tmp[4] = array[j+8] + array[j+9];
tmp[5] = array[j+10] + array[j+11];
tmp[6] = array[j+12] + array[j+13];
tmp[7] = array[j+14] + array[j+15];
tmp[8] = array[j+16] + array[j+17];
tmp[9] = array[j+18] + array[j+19];
tmp[0] += tmp[1];
tmp[2] += tmp[3];
tmp[4] += tmp[5];
tmp[6] += tmp[7];
tmp[8] += tmp[9];
tmp[0] += tmp[2];
tmp[4] += tmp[6];
tmp[0] += tmp[4];
sum += (tmp[8] + tmp[0]);
在我的机器上,这改善了大约17%(6.8秒到5.6秒)(gcc file.c -o file)。
方法#4。 从方法#3微调并行化。
方法#5。 如果编译器和目标机器尚未启用,则尝试启用SSE数学运算。
gcc -mfpmath=sse file.c -o file
在我的机器上,这会进一步缩短执行时间(从5.6秒到5.4)
方法#6。 同样,如果您的编译器和目标机器都支持它,请尝试编写内部循环以利用向量化 - 这样它可以同时执行多个添加。
方法#7。 打开编译器的优化标志。
gcc -mfpmath=sse -O2 file.c -o file
希望这有帮助。
答案 5 :(得分:0)
从技术上讲,代码调用未定义的行为,因为所有位0不一定是double
类型的有效表示。结果未使用,但使用无效的double
可能会导致触发系统定义的异常。
在具有IEEE.754浮点数的计算机上,所有位0都是0.0
的表示。添加所有这些数字将产生值0.0
,重复过程N_TIMES
次没有任何区别。
如果您需要使用calloc()
分配内存,请执行此操作,但其余计算无用:
#include <stdio.h>
#include <stdlib.h>
#define N_TIMES 600000
#define ARRAY_SIZE 10000
int main(void) {
double *array = calloc(ARRAY_SIZE, sizeof(double));
printf("CS201 - Asgmt 4 - your name\n");
free(array); // you might want to free the array for good style
return 0;
}