1

我们有一个发展 Nvidia GPU 和 Intel Xeon Phi 的项目。主机代码和 GPU 代码用 Fortran 编写并由 pgfortran 编译。要将我们的一些工作卸载到 Phi,我们必须创建一个由 ifort 编译的共享库(静态链接无法工作)并从代码的 pgfortran 部分调用共享子例程。通过这样做,我们可以将数组从代码的 pgfortran 部分卸载到可以与 Xeon Phi 通信的 intel fortran 共享库。

现在我正在尝试将包含来自代码的 pgfortran 部分的可分配数组的派生类型传递给 ifort 共享库。看起来有些问题。

这是一个简单的例子(这里没有 Xeon Phi 卸载指令):

来电者.f90:

program caller                                                                           

type cell                                                                                
integer :: id                                                                            
real, allocatable :: a(:)                                                                
real, allocatable :: b(:)                                                                
real, allocatable :: c(:)                                                                
end type cell                                                                            

integer :: n,i,j

type(cell) :: cl(2)                                                                      
 n=10                                                                                    
do i=1,2                                                                                 
allocate(cl(i)%a(n))                                                                     
allocate(cl(i)%b(n))                                                                     
allocate(cl(i)%c(n))                                                                     
end do                                                                                   


do j=1, 2                                                                                
do i=1, n                                                                                
cl(j)%a(i)=10*j+i                                                                        
cl(j)%b(i)=10*i+j                                                                        
end do                                                                                   
end do                                                                                   


call offload(cl(1))                                                                      
print *, cl(1)%c  
end program caller

称为.f90:

subroutine  offload(cl)                                                                  
type cell                                                                                
integer :: id                                                                            
real, allocatable :: a(:)                                                                
real, allocatable :: b(:)                                                                
real, allocatable :: c(:)                                                                
end type cell

type(cell) :: cl                                                                         
integer :: n

print *, cl%a(1:10)
print *, cl%b(1:10)   
end subroutine offload    

生成文件:

run: caller.o libcalled.so
           pgfortran  -L. caller.o -lcalled  -o $@
caller.o: caller.f90
           pgfortran  -c caller.f90
libcalled.so: called.f90
           ifort  -shared -fPIC $^ -o $@

注意cl%a(1:10)这里的“”,没有“ (1:10)”就不会打印任何内容。

这段代码最终打印出了 中的元素,cl(1)%a然后在我试图打印出数组的下一行遇到了分段错误cl(1)%b

如果我将“ cl%a(1:10)”更改为“cl%a(1:100)”,并删除“ print *, cl%b(1:10)”。它将给出以下结果:

结果

我们可以发现 b 数组中的元素在那里,但我无法通过“ cl%b(1:10)”获取它们。

我知道这可能是不同编译器的不同派生类型结构造成的。但我真的想要一种我们可以在编译器之间传递这种派生类型的方法。有什么解决办法吗?

谢谢!

4

1 回答 1

4

编译器的 ABI 可能不同。您不应该直接传递结构,而是在子例程中构建它们并使用指针,您应该将其作为type(c_ptr)或假定大小的数组传递(但那时可能会发生副本!)。

Fortran 2003 与 C 的互操作性不仅意味着与 C 交互,还意味着与 C 互操作的任何其他编译器。它可以是不同的 Fortran 编译器。

请注意,在更多地方声明相同类型并将其用作相同类型是违反 Fortran 规则的,除非该类型是sequenceor bind(C)。这是您的程序不符合标准的另一个原因。

称为.f90:

subroutine  offload(cl_c)                                                                  
use iso_c_binding

type, bind(C) :: cell_C                                                                               
integer :: id                                                                            
integer :: na, nb, nc
type(c_ptr)  :: a,b,c                                                                                                                             
end type cell_C

type cell                                                                               
integer :: id
real, pointer :: a(:)                                                                
real, pointer :: b(:)                                                                
real, pointer :: c(:)                                                                
end type cell

type(cell) :: cl
type(cell_C) :: cl_C
integer :: n

cl%id = cl_C%id
call c_f_pointer(cl_C%a, cl%a, [cl_c%na])
call c_f_pointer(cl_C%b, cl%b, [cl_c%nb])
call c_f_pointer(cl_C%c, cl%c, [cl_c%nc])

print *, cl%a(1:10)
print *, cl%b(1:10)   
end subroutine offload  

来电者.f90:

program caller
use iso_c_binding

type, bind(C) :: cell_C                                                                               
integer :: id                                                                            
integer :: na, nb, nc
type(c_ptr)  :: a,b,c                                                                                                                             
end type cell_C

type cell                                                                                
integer :: id                                                                            
real, allocatable :: a(:)                                                                
real, allocatable :: b(:)                                                                
real, allocatable :: c(:)                                                                
end type cell                                                                            

integer :: n,i,j

type(cell),target :: cl(2)
type(cell_c) :: cl_c
 n=10                                                                                    
do i=1,2                                                                                 
allocate(cl(i)%a(n))                                                                     
allocate(cl(i)%b(n))                                                                     
allocate(cl(i)%c(n))                                                                     
end do                                                                                   

do j=1, 2                                                                                
do i=1, n                                                                                
cl(j)%a(i)=10*j+i                                                                        
cl(j)%b(i)=10*i+j                                                                        
end do                                                                                   
end do                                                                                   

cl_c%a = c_loc(cl(1)%a)
cl_c%b = c_loc(cl(1)%b)
cl_c%c = c_loc(cl(1)%c)
cl_c%na =  size(cl(1)%a)
cl_c%nb =  size(cl(1)%b)
cl_c%nc =  size(cl(1)%c)
cl_c%id =  cl(1)%id

call offload(cl_c)                                                                      
print *, cl(1)%c  
end program caller

使用 gfortran 和 ifort:

>gfortran  called.f90 -c -o called.o
>ifort  caller.f90 -c -o caller.o
>ifort -o a.out called.o caller.o -lgfortran
>./a.out
11.0000000       12.0000000       13.0000000       14.0000000       15.0000000       16.0000000       17.0000000       18.0000000       19.0000000       20.0000000    
   11.0000000       21.0000000       31.0000000       41.0000000       51.0000000       61.0000000       71.0000000       81.0000000       91.0000000       101.000000    
  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00
  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00  0.0000000E+00

这里不需要动态库。

对于 100% 的理论可移植性,可以使用c_int, c_float,... 格式可能会更好等等,但你明白了。

您还可以重载和之间的分配cellcell_C简化转换。

于 2013-11-15T21:33:18.297 回答