【问题标题】:Optimizing C for loops [closed]为循环优化 C [关闭]
【发布时间】:2016-06-29 17:15:02
【问题描述】:

到目前为止,我一直在尝试优化我的程序,但我的目标是让它少于 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];
        }   
    }
}

【问题讨论】:

  • 在编译时知道大小的情况下不要使用动态内存分配。
  • 代码优化是题外话。试试 codereview,也许吧。
  • @SouravGhosh:与加速程序无关,真的。那将是一个 O(1) 操作。
  • 我有义务,因为我的老师希望我们使用 double *array = calloc(ARRAY_SIZE, sizeof(double));
  • 程序过去是 22 秒,我把它缩短到 8 秒,我认为我的方向是正确的,对吧?

标签: c linux loops optimization


【解决方案1】:

删除所有无意义的过程。

#include <stdio.h>

int main(void) {
    printf("CS201 - Asgmt 4 - your name\n");
    return 0;
}

【讨论】:

  • 感谢您享受我的一天
  • 技术上这是正确的答案。由于sum从不返回或输出,所以整个过程没有意义。
  • 我知道,但是我们应该在 linux 服务器上使用时间 a04 运行这个程序,a04 是程序的名称,终端将返回添加所用的时间
  • 有什么方法可以手动优化这段代码你能帮我吗
  • 什么是“优化”?我认为其中之一是删除实际上什么都不做并浪费 CPU 时间的代码。这是多么愚蠢的任务!
【解决方案2】:

让优化器为你做优化。

我把代码粘贴到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。

【讨论】:

  • 我不能像 mikeCat 所说的那样做,因为我们应该在 linux 服务器上使用时间 a04 运行这个程序,a04 是程序的名称,终端将返回添加所用的时间
  • 是否允许在编译命令中添加优化器标志?
  • 它需要我拥有的代码,但是一些如何优化循环,我尝试优化循环购买展开循环
  • 我根本没有更改代码。我刚刚添加了一个优化器标志。
  • 大家好,感谢那些试图帮助我的人。我想使用指针,确实将它降低到 4.30,这对我来说非常好,这就是我是如何做到的 (int i = 0; i
【解决方案3】:

calloc 返回一个用零字节初始化的数组。考虑到您的机器可能使用 IEEE 754 二进制浮点数,该数组中的所有 double 值都是 0.0

现在,你所做的实际上是重复 300000000 次 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;

【讨论】:

    【解决方案4】:

    使用乘法指令怎么样?

    #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;
    }
    

    【讨论】:

      【解决方案5】:

      首先,让我们尝试更改算法,因为这通常会产生最佳结果。

      方法#1。 由于所有内存都是分配的内存,由于 calloc() 将其初始化为零,并且从未分配过其他值,因此分配的数组中的每个双精度数都将具有 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 文件)。

      方法#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
      

      希望这会有所帮助。

      【讨论】:

        【解决方案6】:

        从技术上讲,代码调用未定义的行为,因为所有位 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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-09
          • 2011-07-01
          • 1970-01-01
          • 2021-12-12
          相关资源
          最近更新 更多