这是我第一次在本网站发帖,希望得到一些帮助/提示。我有一个任务,我需要优化内部for循环的性能,但我不知道如何做到这一点。代码在作业中给出。我需要计算时间(我能够做到)并提高性能。
//header files
#define N_TIMES 200 //This is originally 200000 but changed it to test the program faster
#define ARRAY_SIZE 9973
int main (void) {
int *array = (int*)calloc(ARRAY_SIZE, sizeof(int));
int sum = 0;
int checksum = 0;
int i;
int j;
int x;
// Initialize the array with random values 0 to 13.
srand(time(NULL));
for (j=0; j < ARRAY_SIZE; j++) {
x = rand() / (int)(((unsigned)RAND_MAX + 1) / 14);
array[j] = x;
checksum += x;
}
//printf("Checksum is %d.\n",checksum);
for (i = 0; i < N_TIMES; i++) {
// Do not alter anything above this line.
// Need to optimize this for loop----------------------------------------
for (j=0; j < ARRAY_SIZE; j++) {
sum += array[j];
printf("Sum is now: %d\n",sum);
}
// Do not alter anything below this line.
// ---------------------------------------------------------------
// Check each iteration.
//
if (sum != checksum) {
printf("Checksum error!\n");
}
sum = 0;
}
return 0;
}
代码大约需要695秒才能运行。有关如何优化它的任何帮助吗? 非常感谢。
答案 0 :(得分:3)
该循环中的瓶颈显然是由printf
完成的IO;因为你可能在控制台上编写输出,所以输出是行缓冲的,这意味着每次迭代都会刷新stdio缓冲区,这会减慢很多事情。
如果你有做所有打印,你可以通过强制流进行块缓冲来大大提高性能:在for
添加
setvbuf(stdout, NULL, _IOFBF, 0);
另外,如果这种方法不被认为是有效的,你可以通过自己分配一个大缓冲区并自己进行缓冲来自行缓冲:使用sprintf
在缓冲区中写入,定期清空缓冲区。输出流为fwrite
。
此外,你可以使用穷人的缓冲方法 - 只需使用一个足够大的缓冲区来编写所有这些东西(你可以很容易地计算它有多大)并写入其中而不必担心它何时充满,何时清空它,... - 在循环结束时将其清空。 编辑:请参阅@ paxdiablo的答案,了解此
的示例仅应用第一个优化,time
得到的是
real 0m6.580s
user 0m0.236s
sys 0m2.400s
vs原始
real 0m8.451s
user 0m0.700s
sys 0m3.156s
因此,我们实时下降约3秒,用户时间下降半秒,系统时间下降约0.7秒。但是我们在这里看到的是user + sys和real之间的巨大差异,这意味着时间不是花在进程内的事情上,而是等待。
因此,这里的真正瓶颈不在我们的过程中,而是在虚拟终端模拟器的过程中:向控制台发送大量文本将会变慢我们在计划中做了哪些优化;换句话说,你的任务不是CPU限制的,而是IO限制的,因此以CPU为目标的优化不会有太大的好处,因为最后你必须等待你的IO设备做他慢的事情。 / p>
加速这样一个程序的真正方法会更简单:避免缓慢的IO设备(控制台),只需将数据写入文件(顺便说一下,默认情况下是块缓冲)。
matteo@teokubuntu:~/cpp/test$ time ./a.out > test
real 0m0.369s
user 0m0.240s
sys 0m0.068s
答案 1 :(得分:2)
由于基于i
(外部循环)的循环绝对没有变化,因此您不需要每次都计算它。
此外,数据的打印应该在外部内部循环,以免在计算上产生I / O成本。
考虑到这两点,一种可能性是:
static int sumCalculated = 0;
if (!sumCalculated) {
for (j=0; j < ARRAY_SIZE; j++) {
sum += array[j];
}
sumCalculated = 1;
}
printf("Sum is now: %d\n",sum);
虽然它与原版有不同的输出,这可能是一个问题(最后一行而不是每次添加一行)。
如果你做需要在循环中打印累积和,我也只是缓冲它(因为它每次都不会因i
循环而变化。 / p>
字符串Sum is now: 999999999999\n
(12位数,可能因您的int
大小而异)占用25个字节(不包括终止NUL)。乘以9973,你需要一个大约250K的缓冲区(包括终止NUL)。所以像这样:
static char buff[250000];
static int sumCalculated = 0;
if (!sumCalculated) {
int offset = 0;
for (j=0; j < ARRAY_SIZE; j++) {
sum += array[j];
offset += sprintf (buff[offset], "Sum is now: %d\n",sum);
}
sumCalculated = 1;
}
printf ("%s", buff);
现在,这种方法将外循环的整个意图作为基准工具,但循环不变的删除是一种有效的优化方法。
答案 2 :(得分:0)
将printf移到for循环之外。
// Do not alter anything above this line.
//Need to optimize this for loop----------------------------------------
for (j=0; j < ARRAY_SIZE; j++) {
sum += array[j];
}
printf("Sum is now: %d\n",sum);
// Do not alter anything below this line.
// ---------------------------------------------------------------
答案 3 :(得分:0)
循环展开可能如下所示:
for (int j = 0; j < ARRAY_SIZE; j += 2) {
sum += array[j] + array[j+1];
}
如果数组不是展开大小的精确倍数,那么你必须弄清楚要做什么(这可能就是赋值使用素数的原因)。
你必须进行试验,看看展开的数量是多少。