gfortran openmp segmentation fault occurs on basic do loop

前端 未结 2 1852
时光说笑
时光说笑 2020-12-06 23:40

I have a program which distributes particles into a cloud-in-cell mesh. Simply loops over the total number of particles (Ntot) and populates a 256^3 mesh (i.e. each particle

相关标签:
2条回答
  • 2020-12-07 00:30

    See https://computing.llnl.gov/tutorials/openMP/#Clauses for a description of how to make variables shared and private.

    It looks like all your variables should be shared, except the loop variable i which must be private. This would suggest using the following line:

    !$omp parallel do default(shared) private(i)
    

    This should fix your segmentation fault (assuming I got all the variables correct

    However, there is the risk that different threads will attempt to overwrite the same parts of dense simultaneously, resulting in incorrect totals. To protect against this case, you will need to wrap each assignment to dense within something like an !$omp atomic or !$omp critical section.

    However, you may find that such critical sections will cause threads to spend most of their time waiting, so you may not see any improvement over purely serial code.

    In principal you could solve this problem by declaring dense with the reduction keyword, but unfortunately it cannot be used for arrays.

    0 讨论(0)
  • 2020-12-07 00:35

    This problem, as well as the one in your other question, comes from the fact that automatic heap arrays are disabled when OpenMP is enabled. This means that without -fopenmp, big arrays are automatically placed in the static storage (known as the .bss segment) while small arrays are allocated on the stack. When you switch OpenMP support on, no automatic static allocation is used and your dense arrays gets allocated on the stack of the routine. The default stack limits on OS X are very restrictive, hence the segmentation fault.

    You have several options here. The first option is to make dense have static allocation by giving it the SAVE attribute. The other option is to explicitly allocate it on the heap by making it ALLOCATABLE and then using the ALLOCATE statement, e.g.:

    REAL, DIMENSION(:,:,:), ALLOCATABLE :: dense
    
    ALLOCATE(dense(256,256,256))
    
    ! Computations, computations, computations
    
    DEALLOCATE(dense)
    

    Newer Fortran versions support automatic deallocation of arrays without the SAVE attribute when they go out of scope.

    Note that your OpenMP directive is just fine and no additional data sharing clauses are necessary. You do not need to declare i in a PRIVATE clause since loop counters have predetermined private data-sharing class. You do not need to put the other variables in SHARED clause as they are implicitly shared. Yet the operations that you do on dense should either be synchronised with ATOMIC UPDATE (or simply ATOMIC on older OpenMP implementations) or you should use REDUCTION(+:dense). Atomic updates are translated to locked additions and should not incur much of a slowdown, compared to the huge slowdown from having conditionals inside the loop:

    INTEGER :: xi, yi, zi
    
    !$OMP PARALLEL DO PRIVATE(xi,yi,zi)
    ...
    if (x1(i).gt.0.and.y1(i).gt.0.and.z1(i).gt.0) then
      xi = int(x1(i))
      yi = int(y1(i))
      zi = int(z1(i))
      !$OMP ATOMIC UPDATE
      dense(xi,yi,zi) = dense(xi,yi,zi) &
                      + dx1(i) * dy1(i) * dz1(i) * mpart
    end if
    ...
    

    Replicate the code with the proper changes for the other cases. If your compiler complains about the UPDATE clause in the ATOMIC construct, simply delete it.

    REDUCTION(+:dense) would create one copy of dense in each thread, which would consume a lot of memory and the reduction applied in the end would grow slower and slower with the size of dense. For small arrays it would work better than atomic updates.

    0 讨论(0)
提交回复
热议问题