C 在Fortran中向大数组写入值

C 在Fortran中向大数组写入值,c,multithreading,performance,optimization,fortran,C,Multithreading,Performance,Optimization,Fortran,我正在尝试优化我的C/Fortran代码,以便使用OpenMP计算有限差分梯度。该代码输出串行和多线程情况下的正确值。但是,当我尝试将计算值存储在数组中时,代码的速度会比仅执行计算慢很多 我写了一个简单的例子来问这个问题: 我在C中分配数组: /* phi - field over which derivatives are calculated */ float *phi = (float *) calloc(SIZE, sizeof(float)); /* rhs - derivative

我正在尝试优化我的C/Fortran代码,以便使用OpenMP计算有限差分梯度。该代码输出串行和多线程情况下的正确值。但是,当我尝试将计算值存储在数组中时,代码的速度会比仅执行计算慢很多

我写了一个简单的例子来问这个问题:

我在C中分配数组:

/* phi - field over which derivatives are calculated */
float *phi = (float *) calloc(SIZE, sizeof(float));

/* rhs - derivatives in each direction are summed and
         stored in this variable 
*/
float *rhs = (float *) calloc(SIZE, sizeof(float));
我的数组大小是256^3,每端有3个单元格作为“填充”,这使得“大小”为262^3

然后,我在并行OMP区域内调用Fortran函数,在k方向将工作平均分配到不同的线程上:

 #pragma omp parallel default(none) shared(phi, rhs)
{
 /* Divide up slices in the z-direction over the available threads */
    cur_thread = omp_get_thread_num();
    num_threads = omp_get_num_threads();
    nslices = khi_fb - klo_fb + 1;

    /* Current lo and hi indices in the z-direction */
    cur_klo_fb = klo_fb + nslices * cur_thread/num_threads;
    cur_khi_fb = klo_fb + nslices * (cur_thread + 1)/num_threads - 1;

    if (cur_khi_fb > khi_fb)
        cur_khi_fb = khi_fb;

 /* Start timing the program */
    time0 = omp_get_wtime();
/* Run 100 times for better timing */
    for (i = 0; i < 100; i++)
    {
        /* Calling Fortran routine for calculating derivatives */
        CALC_DERIV(phi, rhs, 
            &(ilo_gb), &(ihi_gb), &(jlo_gb), &(jhi_gb), &(klo_gb), &(khi_gb),
            &(ilo_fb), &(ihi_fb), &(jlo_fb), &(jhi_fb), 
            &(cur_klo_fb), &(cur_khi_fb),
            &dx);
    }
   time1 = omp_get_wtime();
这里,后缀“_fb”表示填充框。因此迭代从i和j方向的3:258开始,在k方向的“cur_klo_fb”和“cur_khi_fb”上运行

我的问题是:当我运行如图所示的代码时,1个线程(串行行为)的计时是~2.17s。当我注释掉行
rhs(I,j,k)=temp
时,我的计时是3e-4s。为什么会有这么大的差异?我正在做同样数量的计算。唯一的区别是我将临时变量“temp”存储在rhs中给定的数组位置。此外,我多次读取数组“phi”,但它似乎并不影响速度。似乎将“temp”写入数组“rhs”会减慢速度

当我使用OpenMP运行时,我会得到一个加速,但它不是最优的。我想我错过了什么

我希望我能把我的问题解释清楚。我很乐意提供我正在测试的完整代码

编辑2:

因此,根据这些注释,我尝试了编译器是否完全忽略了循环

我进一步修改了帖子,加入了@jabirali和@VladimirF的建议。我也增加了时间安排

所以我修改了Fortran循环:

integer count
count = 1
c     { begin loop over grid 
      do k=klo_fb,khi_fb
        do j=jlo_fb,jhi_fb
          do i=ilo_fb,ihi_fb

            phi_x = (phi(i+1,j,k) - phi(i-1,j,k))*dx_factor
            phi_y = (phi(i,j+1,k) - phi(i,j-1,k))*dy_factor
            phi_z = (phi(i,j,k+1) - phi(i,j,k-1))*dz_factor

            temp = rhs(i,j,k) + phi_x + phi_y + phi_z
            temp2 = temp2 + temp
c           rhs(i,j,k) = temp
          enddo
        enddo
      enddo
c     } end loop over grid
以下是各种情况(1和2线程)的计时:


仍然困惑:(.

您使用的是什么编译器?以及什么编译器选项以及为什么将fortran与C混合,是否有可能使用我假设是用C编写的fortran函数的
Calc_Deriv
函数?首先想到的是用C声明一个数组,它是
行主
,并且根据
k,j,i
循环将是以
rhs[k][j][i]
的方式访问它。然而,您似乎拥有fortran
rhs(i,j,k)=temp
采用正确的列主格式,但我怀疑仅此语句除了写入该地址外,还需要一些时间来计算三维数组的地址偏移量。我正在使用gcc编译器和'-O3-fPIC-funroll循环'优化选项。现代编译器非常智能,尤其是在当一个代码的运行时间降到零时,我总是问自己,编译器是否意识到它的输出在以后的程序中没有被使用,并且只是完全优化了它们。我无法从你的帖子中判断这是否是你的代码中正在发生的事情,但一定要检查。不要期望一个最优的BSP从OpenMP升级到模板计算,它是内存有限的,而不是CPU有限的。您使用的是什么编译器?以及什么编译器选项以及为什么将fortran与C混合使用,是否可以使用我假设是用C编写的fortran函数的
Calc_Deriv
函数?首先想到的是声明arr在C中,ay是行主键,根据你的
k,j,i
循环,它将像
rhs[k][j][i]
那样访问它。然而,你似乎有你的fortran
rhs(i,j,k)=temp
采用正确的列主格式,但我怀疑仅此语句除了写入该地址外,还需要一些时间来计算三维数组的地址偏移量。我正在使用gcc编译器和'-O3-fPIC-funroll循环'优化选项。现代编译器非常智能,尤其是在当一个代码的运行时间降到零时,我总是问自己,编译器是否意识到它的输出在以后的程序中没有被使用,并且只是完全优化了它们。我无法从你的帖子中判断这是否是你的代码中正在发生的事情,但一定要检查。不要期望一个最优的BSP从OpenMP升级到模具计算,内存带宽有限,而不是CPU有限。
integer count
count = 1
c     { begin loop over grid 
      do k=klo_fb,khi_fb
        do j=jlo_fb,jhi_fb
          do i=ilo_fb,ihi_fb

            phi_x = (phi(i+1,j,k) - phi(i-1,j,k))*dx_factor
            phi_y = (phi(i,j+1,k) - phi(i,j-1,k))*dy_factor
            phi_z = (phi(i,j,k+1) - phi(i,j,k-1))*dz_factor

            temp = rhs(i,j,k) + phi_x + phi_y + phi_z
            temp2 = temp2 + temp
c           rhs(i,j,k) = temp
          enddo
        enddo
      enddo
c     } end loop over grid
Case 1: With temp2, no rhs(i,j,k), 1 thread: 3.24s
Case 2: With temp2, no rhs(i,j,k), 2 threads: 1.65s
Case 3: Without temp2, with rhs(i,j,k), 1 thread: 1.23s
Case 4: Without temp2, with rhs(i,j,k), 2 threads: 0.74s
Case 5: Without temp2, without rhs, 1 thread: 1e-6s