启用OpenMP时,英特尔Fortran字符指针损坏

时间:2017-02-21 05:17:44

标签: arrays fortran openmp intel-fortran totalview

我正在为相当大的代码(天气预报模型)开发英特尔Fortran兼容性。在英特尔Fortran(以及英特尔Fortran)上,一些字符数据似乎会通过循环加扰并指向字符指针。我最终得到的字符只包含0和9.字符串c只有"00:00:00:16 (whitespace padding....)"作为其内容。

每当我包含任何(死的)OpenMP代码使用-fopenmp编译器标志时,我都能够重现这个问题。我在这里发生的事情有点迷失了。您可以在以下最小重现器中检测到任何编程错误吗?如果没有,我想我会提交编译错误。

复制者

minimal.f90

module foo
    implicit none

    type pointer_character
    character, pointer :: c
    end type
    type(pointer_character), allocatable :: ptr_c(:)
    integer(4) :: idx_c

    contains

    subroutine grpbcast_set_c(c)
        implicit none
        character(*), intent(in), target :: c
        integer(4) :: cloc

        allocate(ptr_c( 256 ))
        idx_c = 0

        do cloc = 1, len(c)
            idx_c = idx_c + 1
            ptr_c( idx_c )%c => c( cloc:cloc )
        end do

        print *, "testprint-2", c
        print *, "testprint-3.1", ptr_c( 1 )%c
        print *, "testprint-3.2", ptr_c( 2 )%c
        print *, "testprint-3.3", ptr_c( 3 )%c
        print *, "testprint-3.4", ptr_c( 11 )%c

    end subroutine

    ! The following subroutine is never called, but if we include it in the module foo, it will lead to the data corruption documented in
    ! http://stackoverflow.com/questions/42359258/intel-fortran-substring-access-with-convert-big-endian
    ! If the subroutine is commented it will work
    !
    ! In other words:
    ! if we comment this subroutine out, the output will be
    ! =====================================================
    ! testprint-2
    ! 00:00:00:16



    ! testprint-3.10
    ! testprint-3.20
    ! testprint-3.3:
    ! testprint-3.46
    ! =====================================================
    ! However if we include it, the output will be
    ! testprint-2
    ! 00:00:00:16



    ! testprint-3.10
    ! testprint-3.20
    ! testprint-3.30
    ! testprint-3.40
    ! =====================================================
    !
    ! tested with: ifort 17.0.1 20161005
    subroutine scatter_one_record()
     implicit none
     integer(4) :: k

!$OMP PARALLEL DO
    do k = 1, 5
    end do
!$OMP END PARALLEL DO
    end subroutine

end module foo

program main
    use foo, only: grpbcast_set_c
    implicit none

    character(len=256), target:: run_period
    run_period = "00:00:00:16"

    call grpbcast_set_c(run_period)
end program

生成文件

.PHONY: all

all: minimal

minimal.o: minimal.f90
    ifort -fopenmp -c minimal.f90 -o minimal.o

minimal: minimal.o
    ifort -fopenmp -o minimal -L./ minimal.o

ifort版本

> ifort --version
ifort (IFORT) 17.0.1 20161005

编译&安培;运行

make
./minimal

...

...

之前的分析,仅在此处了解此问题的评论链

循环问题

subroutine grpbcast_set_c(c)
 use nrtype, only : rp => rp
 implicit none
 character(*), intent(in), target :: c
 integer(4) :: cloc

 do cloc = 1, len(c)
  idx_c = idx_c + 1
  if (idx_c > max_ptr_gbcast) stop 9
  ptr_c( idx_c )%c => c( cloc:cloc )
 end do
end subroutine

模块中ptr_c的规范

type pointer_character
   character, pointer :: c
end type

type(pointer_character), private, allocatable, save :: ptr_c(:)

调试器中显示的数据(TotalView)

Data at Runtime

编译器命令

> mpif90 -O0 -no-ipo -g -convert big_endian -fopenmp -r8 -DUSE_MPI -I/usr/apps.sp3/mpi/openmpi/1.6.5/i2013.1.046/include -I/usr/apps.sp3/mpi/openmpi/1.6.5/i2013.1.046/lib -I [NUSDAS13_PATH]/src -I [NETCDF_PATH]/include -DUSE_MPI -c mpi_comm.f90 -o mpi_comm.o

> mpif90 --version
ifort (IFORT) 14.0.2 20140120
Copyright (C) 1985-2014 Intel Corporation.  All rights reserved.

更新

我刚尝试使用最新的英特尔编译器版本:

> mpif90 --version
> ifort (IFORT) 17.0.1 20161005

结果仍然错误,但不同!这次我将所有角色归零。

Newest Intel

Newest Intel - Beginning of Array

更新2

我一直试图用最小的复制器重现问题(见下文),到目前为止没有运气,即以这种方式运行时错误没有出现。然而,有一点很有趣:TotalView显示数组的第一个地址略有不同:

with working array pointer

我猜输入c一定是个问题,即使它在输入grpcast_set_c的调试器中正确显示。

module foo
    implicit none

    type pointer_character
        character, pointer :: c
    end type
    type(pointer_character), allocatable, save :: ptr_c(:)
    integer(4), save :: idx_c
contains

    subroutine parmread_bcast_a( mt, aout, chktag )
        implicit none
        integer(4), intent(in):: mt
        character(*), intent(out):: aout
        character(*), intent(in):: chktag

        call filetool_cardread_a( mt, aout, chktag )
        write(6,'("* ",a28," = ", a)') chktag, trim(aout)

        call set_ptr_c(aout)
        return
    end subroutine parmread_bcast_a

    subroutine filetool_cardread_a( imt, aout, chktag )
        implicit none
        integer(4), intent(in):: imt
        character(*), intent(out):: aout
        character(*), intent(in), optional:: chktag

        character(len=256):: tag
        character(len=256):: val
        character(len=256):: buff
        integer(4):: idxeql
        integer(4) :: is_debug = 0

        cardread: do
            read( imt, "(a)" ) buff
            if( buff(1:1) /= "*" ) then
                idxeql = index(buff, "=")
                tag = adjustl(buff(:idxeql-1))
                if (is_debug == 1) then
                write(6,*) 'buff', trim(buff)
                endif
                if ( present( chktag ) ) then
                if ( trim(chktag) /= trim(tag) ) then
                    write(6,*)"WARNING !!! the record is for ", trim(tag), &
                    & ", while you specified ", trim(chktag)
                    cycle cardread
                endif
                endif
                val = adjustl(buff(idxeql+1:))
                aout = val
                return
            end if
        end do cardread
    end subroutine filetool_cardread_a

    subroutine set_ptr_c(c)
        implicit none
        character(*), intent(in), target :: c
        integer(4) :: cloc

        do cloc = 1, len(c)
            idx_c = idx_c + 1
            if (idx_c > 10000) stop 9
            ptr_c( idx_c )%c => c( cloc:cloc )
        end do
    end subroutine
end module

program main
    use foo, only: ptr_c, idx_c, parmread_bcast_a
    implicit none

    character(len=256), save:: run_period

    allocate(ptr_c( 10000 ))
    idx_c = 0

    open( 100, file='sample.conf', form='formatted')
    call parmread_bcast_a(100, run_period, "run_period")

    print *, "run period", run_period

    print *, ptr_c( 1 )%c
    print *, ptr_c( 2 )%c
    print *, ptr_c( 3 )%c
    print *, ptr_c( 11 )%c
end program

sample.conf

  run_period               =    00:00:00:16

1 个答案:

答案 0 :(得分:0)

作为一种解决方法,将OpenMP代码从这种数据结构初始化分离为多个模块(即使在同一个文件中)也可以。在上述情况下:

module foo1
    implicit none

    type pointer_character
    character, pointer :: c
    end type
    type(pointer_character), allocatable :: ptr_c(:)
    integer(4) :: idx_c

    contains
    subroutine grpbcast_set_c(c)
        implicit none
        character(*), intent(in), target :: c
        integer(4) :: cloc

        allocate(ptr_c( 256 ))
        idx_c = 0

        do cloc = 1, len(c)
            idx_c = idx_c + 1
            ptr_c( idx_c )%c => c( cloc:cloc )
        end do

        print *, "testprint-2", c
        print *, "testprint-3.1", ptr_c( 1 )%c
        print *, "testprint-3.2", ptr_c( 2 )%c
        print *, "testprint-3.3", ptr_c( 3 )%c
        print *, "testprint-3.4", ptr_c( 11 )%c

    end subroutine
end module    

module foo2
    implicit none

    contains
    subroutine scatter_one_record()
     implicit none
     integer(4) :: k

!$OMP PARALLEL DO
    do k = 1, 5
    end do
!$OMP END PARALLEL DO
    end subroutine
end module

program main
    use foo1, only: grpbcast_set_c
    implicit none

    character(len=256), target:: run_period
    run_period = "00:00:00:16"

    call grpbcast_set_c(run_period)
end program