ifort 中可能存在的错误

Possible bug in ifort

尝试在 Fortran 代码中使用分块对角矩阵类型时, 我在使用以下编译器的以下代码中偶然发现了一个令人惊讶的错误:

GNU Fortran (SUSE Linux) 7.4.0
ifort (IFORT) 18.0.5 20180823
ifort (IFORT) 16.0.1 20151021

如果我编译

gfortran -Wall -Werror --debug ifort_bug.f && valgrind ./a.out

我没有从 valgrind 报告错误。

如果我编译

ifort -warn all,error -debug -stacktrace ifort_bug.f && valgrind ./a.out

我在我的代码中遇到 ifort_18 的分段错误和 ifort_16 的 "only" 内存泄漏。

这是英特尔编译器中的错误,还是 gfortran 默默地修复了我的错误代码?

      module blockdiagonal_matrices
        implicit none
        private
        public :: t_blockdiagonal, new, delete,
     &    blocksizes, operator(.mult.), mult
        save

        integer, parameter :: dp = kind(1.d0)

        type :: t_blockdiagonal
          real(dp), allocatable :: block(:, :)
        end type

        interface new
          module procedure block_new
        end interface

        interface delete
          module procedure block_delete
        end interface

        interface operator (.mult.)
          module procedure mult_blocks
        end interface

      contains

        subroutine block_new(blocks, blocksizes)
          type(t_blockdiagonal), intent(out) :: blocks(:)
          integer, intent(in) :: blocksizes(:)
          integer :: i, L

          do i = 1, size(blocks)
            L = blocksizes(i)
            allocate(blocks(i)%block(L, L))
          end do
        end subroutine

        subroutine block_delete(blocks)
          type(t_blockdiagonal) :: blocks(:)
          integer :: i

          do i = 1, size(blocks)
            deallocate(blocks(i)%block)
          end do
        end subroutine

        function blocksizes(A) result(res)
          type(t_blockdiagonal), intent(in) :: A(:)
          integer :: res(size(A))

          integer :: i
          res = [(size(A(i)%block, 1), i = 1, size(A))]
        end function

        function mult_blocks(A, B) result(C)
          type(t_blockdiagonal), intent(in) :: A(:), B(:)
          type(t_blockdiagonal) :: C(size(A))

          integer :: i

          call new(C, blocksizes=blocksizes(A))
          do i = 1, size(A)
            C(i)%block = matmul(A(i)%block, B(i)%block)
          end do
        end function

        subroutine mult(A, B, C)
          type(t_blockdiagonal), intent(in) :: A(:), B(:)
          type(t_blockdiagonal) :: C(:)

          integer :: i

          do i = 1, size(A)
            C(i)%block = matmul(A(i)%block, B(i)%block)
          end do
        end subroutine
      end module blockdiagonal_matrices

      program time_blockdiagonal
        use blockdiagonal_matrices
        integer, parameter :: n_blocks = 2, L_block = 10**2
        type(t_blockdiagonal) :: A(n_blocks), B(n_blocks), C(n_blocks)
        integer :: i
        integer :: start, finish, rate

        call system_clock(count_rate=rate)
        call new(A, blocksizes=[(L_block, i = 1, n_blocks)])
        call new(B, blocksizes=[(L_block, i = 1, n_blocks)])
        call new(C, blocksizes=[(L_block, i = 1, n_blocks)])

        do i = 1, n_blocks
          call random_number(A(i)%block)
          call random_number(B(i)%block)
        end do


        call system_clock(start)
        C = A .mult. B
        call system_clock(finish)
        write(6,*) 'Elapsed Time in seconds:',
     &    dble(finish - start) / dble(rate)

        call system_clock(start)
        call mult(A, B, C)
        call system_clock(finish)
        write(6,*) 'Elapsed Time in seconds:',
     &    dble(finish - start) / dble(rate)

        call delete(A)
        call delete(B)
        call delete(C)


      end program time_blockdiagonal

ifort_18 段错误是:

forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
a.out              00000000004134BD  Unknown               Unknown  Unknown
libpthread-2.26.s  00007FF720EB6300  Unknown               Unknown  Unknown
a.out              000000000040ABB8  Unknown               Unknown  Unknown
a.out              000000000040B029  Unknown               Unknown  Unknown
a.out              0000000000407113  Unknown               Unknown  Unknown
a.out              0000000000402B4E  Unknown               Unknown  Unknown
libc-2.26.so       00007FF720B0AF8A  __libc_start_main     Unknown  Unknown
a.out              0000000000402A6A  Unknown               Unknown  Unknown

当我用 gdb 进入它时,结果发现,从函数 mult_blocks.

返回时会引发段错误
blockdiagonal_matrices::mult_blocks (c=..., a=..., b=...) at ifort_bug.f:63
63            do i = 1, size(A)
(gdb) s
64              C(i)%block = matmul(A(i)%block, B(i)%block)
(gdb) s
s
s
65            end do
(gdb) s
64              C(i)%block = matmul(A(i)%block, B(i)%block)
(gdb) s
65            end do
(gdb) s
66          end function
(gdb) s

Program received signal SIGSEGV, Segmentation fault.
0x000000000040abc4 in do_deallocate_all ()
(gdb) q
A debugging session is active.

即使有了这些信息,我也找不到我的代码中的错误。

编辑

我找到了一个修复程序,虽然我不明白它为什么有效。

编译修复

如果我使用 -heap-arrays 进行编译,它就可以工作。所以乍一看好像运行进了一个Whosebug。如果我这样做 ulimit -s unlimited 并没有 解决问题。

代码修复

如果我在代码中显式分配它就可以解决问题。

        subroutine new(blocks, blocksizes)
          type(t_blockdiagonal), allocatable, intent(out) :: blocks(:)
          integer, intent(in) :: blocksizes(:)
          integer :: i, L

          allocate(blocks(size(blocksizes)))
          do i = 1, size(blocks)
            L = blocksizes(i)
            allocate(blocks(i)%block(L, L))
          end do
        end subroutine

        subroutine delete(blocks)
          type(t_blockdiagonal), allocatable :: blocks(:)
          integer :: i

          do i = 1, size(blocks)
            deallocate(blocks(i)%block)
          end do
          deallocate(blocks)
        end subroutine

        function mult_blocks(A, B) result(C)
          type(t_blockdiagonal), intent(in) :: A(:), B(:)
          type(t_blockdiagonal), allocatable :: C(:)

          integer :: i

          call new(C, blocksizes=blocksizes(A))
          do i = 1, size(A)
            C(i)%block = matmul(A(i)%block, B(i)%block)
          end do
        end function

恕我直言,这种写法实际上比以前更好,而不是 "dirty hack"。 无法再使用 blocksizeblocks.

的不同大小调用 new

未决问题

说 C type(t_blockdiagonal) :: blocks(n) 应该是 float** blocks[n] 所以只是指向指针的指针向量。 实际块的分配也发生在堆上的第一个版本中。因此,我没有得到包含 ca 的向量的 Whosebug。 10 个指针。

Intel® Fortran Compiler - Increased stack usage of 8.0 or higher compilers causes segmentation fault

根据这篇文章,ulimit -s unlimited并不意味着堆栈会字面上的"unlimited":

The size of "unlimited" varies by Linux configuration, so you may need to specify a larger, specific number to ulimit (for example, 999999999). On Linux also note that many 32bit Linux distributions ship with a pthread static library (libpthread.a) that at runtime will fix the stacksize to 2093056 bytes regardless of the ulimit setting.

考虑到 -heap-arrays 解决了问题,很可能您确实遇到了堆栈溢出问题。