2

正在测试和计时一些计算(试图找到一个在与我的处理器上的所有 4 个线程并行时运行速度快 4 倍的 for 循环)当我注意到这个循环不会以 100% 的 cpu 使用率运行,尽管编译器报告它被并行化了。它只会以 25% 的 CPU 使用率运行。我的处理器上的每个核心都应该有自己的 arr4 副本,这是一个分配在堆栈上的 C 样式数组,并且每个核心都应该重复修改该堆栈数组的每个值。最后,计时器以秒为单位打印所用时间。如果并行化的时间需要 40 秒,我希望没有并行化的 for 循环的时间只需不到 4*40 秒或 160 秒。优化设置为最大速度,物理内存上的堆栈大小设置为 8 亿字节(以防止堆栈溢出)。反正,

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <malloc.h>

int main (void)
{
    clock_t begin, end;
    double time_spent;

    begin = clock();
    {
        //int j;

        #pragma loop(hint_parallel(4))
        #pragma loop(ivdep)
        for (int j=0; j < 8; ++j)
        {
            int * __restrict const arr4 = (int *) _alloca(16000000*sizeof(int));

            for (int z = 0; z < 16000000; ++z)
            {
                arr4[z] = z;
            }

            #pragma loop(no_vector)
            for (int i = 0; i < 16000000; ++i)
            {
                for (int k = 0; k < 160; ++k)
                {
                    arr4[i] -= (7 - arr4[i] * 6 % (i+77) + 5 * 4 / 3 + 3 % 2 + 1 - (i+7));
                    arr4[i] += ((77 - 2 - (i+9)/2 + arr4[i]));
                    arr4[i] *= (8 - 2 + 6 % 3 / 2 + (i+6));
                }
            }
            printf(" %i ", arr4[((j+1)*666)%16]);
        }
    }
    end = clock();
    time_spent = (double)(end - begin) / ((double)CLOCKS_PER_SEC);
    printf("Test1: time as a floating point type is %f \n", time_spent);
    return 0;
}

这个修改后的例子也产生了同样的 25% CPU 问题。

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <malloc.h>

int main (void)
{
clock_t begin, end;
double time_spent;

begin = clock();
int * __restrict const arr4 = (int *) _alloca(16000000*sizeof(int));

#pragma loop(hint_parallel(4))
#pragma loop(ivdep)
for (int j=0; j < 8; ++j)
{
    for (int i = 0; i < 16000000; ++i)
    {
       int v = i;    // eliminate initialization pass (z loop)
       for (int k = 0; k < 160; ++k)
       {
           v -= (7 - v * 6 % (i+77) + 5 * 4 / 3 + 3 % 2 + 1 - (i+7));
           v += ((77 - 2 - (i+9)/2 + v));
           v *= (8 - 2 + 6 % 3 / 2 + (i+6));
       }
       arr4[i] = v;
    }
    //printf(" %i ", arr4[((j+1)*666)%16]);
}

end = clock();
//time_spent = (double)(end - begin) / ((double)CLOCKS_PER_SEC);
time_spent = (double)(end - begin);
printf(" %i ", arr4[666]);
printf("Test1: time as a floating point type is %f \n", time_spent);
return 0;
}
4

1 回答 1

4

首先,您不应期望在添加处理器时会提高线性速度。在理想条件下,将可用内核数量加倍通常只会将执行性能提高约 1.8 倍。

从人的角度来考虑:将 10 人的开发团队增加一倍至 20 人是否会自动让您完成两倍的工作?不,因为随着参与者数量的增加,沟通和协调成为一项更大的任务。

其次,你的定时器循环中有很多非计算的东西。您在外循环中有内存分配和 printf,在您的内循环中有多个内存读取和写入。特别是,您正在从内存地址读取、写入、再次读取等,这可能会使某些寄存器变量编译器优化无效。

很可能是您的 CPU 花费大量时间等待内存读取和写入完成。

由于您对数组中数据的修改似乎对外部观察者不可见,因此您应该考虑将 arr4[i] 值拉入本地 int 变量并对该本地 int 变量执行所有操作,然后写入本地 int变量返回到 arr4[i] 内存地址。这应该将您的内存负载从 5 次读取、3 次写入减少到 1 次读取、每次内部循环迭代 1 次写入,并消除代价高昂的写入后读取管道停顿。

由于这些内存写入发生在 k 循环内,因此将初始负载和最终存储移出 k 循环将使您的内存负载从 (5+3)*160 = 1280 内存 I/O 每次迭代 i 循环i 循环的每次迭代最多 2 个内存 I/O。哦,整个初始化循环(z 循环)也可以消除,因为初始值是迭代次数。因此,我们可以将每 i 次迭代的内存 I/O 减少到 1。

像这样的东西:

for (int j=0; j < 8; ++j)
{
    int * __restrict const arr4 = (int *) _alloca(16000000*sizeof(int));

    for (int i = 0; i < 16000000; ++i)
    {
       int v = i;    // eliminate initialization pass (z loop)
       for (int k = 0; k < 160; ++k)
       {
           v -= (7 - v * 6 % (i+77) + 5 * 4 / 3 + 3 % 2 + 1 - (i+7));
           v += ((77 - 2 - (i+9)/2 + v));
           v *= (8 - 2 + 6 % 3 / 2 + (i+6));
       }
       arr4[i] = v;
    }
    printf(" %i ", arr4[((j+1)*666)%16]);
}

编译器不能总是进行这种优化,因为内存写入通常被认为是神圣的,因为它们可以被当前上下文之外的未知方观察到。如果您比编译器更了解这种情况,那么您可以编写比编译器更好的代码。

于 2013-06-06T17:04:14.373 回答