かなり大きなコード (天気予報モデル) の Intel Fortran 互換性に取り組んでいます。インテル Fortran (およびインテル Fortranのみ) では、一部の文字データがスクランブルされてループし、文字ポインターがそれを指しているように見えます。最終的な文字には 0 と 9 しか含まれていません。文字列の内容c
は単純"00:00:00:16 (whitespace padding....)"
です。
(デッド) OpenMP コードをインクルードし、コンパイラ フラグを使用するたびに発生する問題を再現することができました。-fopenmp
私はここで何が起こっているのか少し迷っています。次の最小再現器でプログラミング エラーを検出できますか? そうでない場合は、コンパイラのバグを報告すると思います。
再生器
ミニマル.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 --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)
コンパイラ コマンド
> 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.
アップデート
最新の Intel コンパイラ バージョンで同じことを試しました。
> mpif90 --version
> ifort (IFORT) 17.0.1 20161005
結果はまだ間違っていますが、違います! 今回はすべての文字をゼロにします。
更新 2
最小限の再現ツール (以下を参照) を使用して問題を再現しようとしましたが、これまでのところうまくいきません。つまり、この方法で実行してもエラーは表示されません。ただし、興味深い点が 1 つあります。TotalView では、配列の最初のアドレスがわずかに異なって表示されます。
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