我们有一个项目可以发展Nvidia GPU和Intel Xeon Phi . 主机代码和GPU代码用Fortran编写,由pgfortran编译 . 为了将我们的一些工作卸载到Phi,我们必须创建一个由ifort编译的共享库(静态链接不能工作),并从代码的pgfortran部分调用共享子例程 . 通过这样做,我们可以将代码的pgfortran部分中的数组卸载到可以与Xeon Phi通信的intel fortran共享库中 .
现在我正在尝试将包含可分配数组的派生类型从代码的pgfortran部分传递给ifort共享库 . 看起来有一些问题 .
这是一个简单的例子(这里没有Xeon Phi卸载指令):
caller.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
called.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
Makefile文件:
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)
" here, witout the " (1:10)
”没有任何打印 .
此代码最终打印出 cl(1)%a
中的元素,然后在下一行中遇到分段错误,我试图打印出数组 cl(1)%b
.
如果我更改“ cl%a(1:10)
" to " cl%a(1:100)", and delete the " print *, cl%b(1:10)
” . 它会得到以下结果:
我们可以发现b数组中的元素存在,但我无法通过“ cl%b(1:10)
”获取它们 .
我知道这可能是由不同编译器的不同派生类型结构引起的 . 但我真的想要一种可以在编译器之间传递这种派生类型的方法 . 有解决方案?
谢谢!
1 回答
编译器的ABI可以不同 . 您不应该直接传递结构,而是在子例程中构建它们并使用指针,您应该将它们作为
type(c_ptr)
或假定的大小数组传递(但是可以发生复制!) .与Fortran 2003中的C的互操作性并不仅仅意味着与C交互,而是与C可互操作的任何其他编译器 . 它可以是不同的Fortran编译器 .
请注意,除非类型为
sequence
或bind(C)
,否则在更多位置声明相同类型并将其用作相同类型是违反Fortran规则的 . 这是您的程序不符合标准的另一个原因 .called.f90:
caller.f90:
与gfortran和ifort:
这里不需要动态库 .
对于100%理论上的可移植性,可以使用
c_int
,c_float
,......格式化可能会更好等等,但是你明白了 .您还可以重载
cell
和cell_C
之间的分配以简化转换 .