for loop optimization c ++

时间:2013-06-11 02:51:02

标签: c++ optimization

这是我第一次在本网站发帖,希望得到一些帮助/提示。我有一个任务,我需要优化内部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秒才能运行。有关如何优化它的任何帮助吗? 非常感谢。

4 个答案:

答案 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)

  1. 让I / O脱离循环是一个很大的帮助。
  2. 根据编译器和机器,可能通过使用指针而不是索引来获得速度的微小增加(尽管在现代硬件上,它通常没有区别)。
  3. 循环展开可能有助于提高有用工作与循环开销的比率。
  4. 您可以使用向量指令(例如,SIMD)并行执行一系列计算。
  5. 您是否可以打包阵列?你能使用比int更小的类型的数组(假设所有的值都非常小)?使阵列物理上更短可以改善局部性。
  6. 循环展开可能如下所示:

    for (int j = 0; j < ARRAY_SIZE; j += 2) {
      sum += array[j] + array[j+1];
    }
    

    如果数组不是展开大小的精确倍数,那么你必须弄清楚要做什么(这可能就是赋值使用素数的原因)。

    你必须进行试验,看看展开的数量是多少。