问题描述
我正在用 Matlab r2019a 编写一个 FORTRAN mex 文件。我已经用小数组测试了代码,一切正常。但是,当我尝试增加数组的大小时,出现以下错误。
致命错误 LNK1248:图像大小 (9B993000) 超过最大允许大小 (80000000)
我在 Windows 10 上使用 Visual Studio 2017 和英特尔 FORTRAN 编译器,我有 16GB 的内存。 此链接似乎有解决方案,但我似乎无法在 VS 2017 中找到用于传递 -heap-array 的链接器选项。
有人能告诉我如何增加尺寸吗?
谢谢 基南
#include "fintrf.h"
C Gateway routine
subroutine mexFunction(nlhs,plhs,nrhs,prhs)
C Declarations
implicit none
C mexFunction arguments:
mwPointer plhs(*),prhs(*)
mwSize nlhs,nrhs
C Function declarations:
mwPointer mxCreateDoubleMatrix
mwPointer mxDuplicateArray
mwPointer mxGetPr
mwPointer mxGetM,mxGetN
mwSize mxIsNumeric
C Pointers to input/output mxArrays:
mwPointer coord_pr,dualnumfam_pr,dualfail_pr,dualpointfam_pr
mwPointer dualnodefam_pr,totnode_pr,width_pr,scr0_pr,vol_pr,bc_pr
mwPointer disp_pr,numfam_pr,nodefam_pr,pointfam_pr,fail_pr
mwPointer dmgpar1_pr,dmgpar2_pr,pforce_pr,dualpforce_pr
mwPointer fails_pr,dualfails_pr
C Array information:
mwPointer m,n,x,y,u,l,o,p
mwSize mm,nn
mwSize size,row,sizes
C Arguments for computational routine:
real*8 coord(450000),dualnumfam(300000)
real*8 dualfail(30000000),dualpointfam(150000)
real*8 dualnodefam(30000000),totnode(1),width(1)
real*8 scr0(150000),vol(150000),bc(150000)
real*8 disp(450000),numfam(150000),nodefam(30000000)
real*8 pointfam(150000),fail(30000000)
real*8 dmgpar1(150000),dmgpar2(150000)
real*8 pforce(450000),dualpforce(450000)
real*8 fails(30000000),dualfails(30000000)
character*200 msg
character*20 fmt
character*10 sm,sn,sx,sy,so,sp
C-----------------------------------------------------------------------
C Check for proper number of arguments.
if (nrhs .ne. 15) then
call mexErrMsgIdAndTxt ('MATLAB:test:nInput',+ 'One inputs required.')
endif
C Validate inputs
C Check to see both inputs are numeric.
if (mxIsNumeric(prhs(1)) .ne. 1) then
call mexErrMsgIdAndTxt ('MATLAB:test:NonNumeric1',+ 'Input # 1 is not a numeric.')
endif
C Check that input #1 is a scalar.
m = mxGetM(prhs(1))
n = mxGetN(prhs(1))
size = m*n
x = mxGetM(prhs(3))
y = mxGetN(prhs(3))
sizes=x*y
u = mxGetM(prhs(5))
fmt = '(I8)'
write (sm,fmt) m
write (sn,fmt) n
write (sx,fmt) x
write (sy,fmt) y
msg = 'm=' // trim(sm) // ',\t n=' // trim(sn) // '\n'
call mexPrintf(trim(msg))
msg = 'x=' // trim(sx) // ',\t y=' // trim(sy) // '\n'
call mexPrintf(trim(msg))
C Create matrix for the return argument.
call mexPrintf("one")
plhs(1) = mxCreateDoubleMatrix(m,1,0)
plhs(2) = mxCreateDoubleMatrix(m,0)
plhs(3) = mxCreateDoubleMatrix(m,3,0)
plhs(4) = mxCreateDoubleMatrix(m,0)
plhs(5) = mxCreateDoubleMatrix(x,0)
plhs(6) = mxCreateDoubleMatrix(x,0)
call mexPrintf("two")
coord_pr = mxGetPr(prhs(1))
dualnumfam_pr = mxGetPr(prhs(2))
dualfail_pr = mxGetPr(prhs(3))
dualpointfam_pr = mxGetPr(prhs(4))
dualnodefam_pr = mxGetPr(prhs(5))
totnode_pr = mxGetPr(prhs(6))
width_pr = mxGetPr(prhs(7))
scr0_pr = mxGetPr(prhs(8))
vol_pr = mxGetPr(prhs(9))
bc_pr = mxGetPr(prhs(10))
disp_pr = mxGetPr(prhs(11))
numfam_pr = mxGetPr(prhs(12))
nodefam_pr = mxGetPr(prhs(13))
pointfam_pr = mxGetPr(prhs(14))
fail_pr = mxGetPr(prhs(15))
dmgpar1_pr = mxGetPr(plhs(1))
dmgpar2_pr = mxGetPr(plhs(2))
pforce_pr = mxGetPr(plhs(3))
dualpforce_pr = mxGetPr(plhs(4))
fails_pr = mxGetPr(plhs(5))
dualfails_pr = mxGetPr(plhs(6))
call mexPrintf("three")
C Load the data into Fortran arrays.
call mxCopyPtrToReal8(coord_pr,coord,size)
call mxCopyPtrToReal8(dualnumfam_pr,dualnumfam,m)
call mxCopyPtrToReal8(dualfail_pr,dualfail,sizes)
call mxCopyPtrToReal8(dualpointfam_pr,dualpointfam,m)
call mxCopyPtrToReal8(dualnodefam_pr,dualnodefam,u)
call mxCopyPtrToReal8(totnode_pr,totnode,1)
call mxCopyPtrToReal8(width_pr,width,1)
call mxCopyPtrToReal8(scr0_pr,scr0,m)
call mxCopyPtrToReal8(vol_pr,vol,m)
call mxCopyPtrToReal8(bc_pr,bc,m)
call mxCopyPtrToReal8(disp_pr,disp,size)
call mxCopyPtrToReal8(numfam_pr,numfam,m)
call mxCopyPtrToReal8(nodefam_pr,nodefam,u)
call mxCopyPtrToReal8(pointfam_pr,pointfam,m)
call mxCopyPtrToReal8(fail_pr,fail,sizes)
call mxCopyPtrToReal8(dmgpar1_pr,dmgpar1,m)
call mxCopyPtrToReal8(dmgpar2_pr,dmgpar2,m)
call mxCopyPtrToReal8(pforce_pr,pforce,size)
call mxCopyPtrToReal8(dualpforce_pr,dualpforce,size)
call mexPrintf("four")
call mxCopyPtrToReal8(fails_pr,fails,sizes)
call mxCopyPtrToReal8(dualfails_pr,dualfails,sizes)
C Call the computational subroutine.
call body(coord,+ totnode,fail
+,m,dualfails)
C Load the output into a MATLAB array.
call mxCopyReal8ToPtr(dmgpar1,dmgpar1_pr,m)
call mxCopyReal8ToPtr(dmgpar2,m)
call mxCopyReal8ToPtr(pforce,size)
call mxCopyReal8ToPtr(dualpforce,dualpforce_pr,size)
call mxCopyReal8ToPtr(fails,fails_pr,sizes)
call mxCopyReal8ToPtr(dualfails,dualfails_pr,sizes)
return
end
解决方法
与堆相比,堆栈是相对较小的内存量。堆栈是固定数量的内存,它本质上是程序的一部分。它用于在例程之间传递参数、局部变量内存等。由于其相对较小,因此不应创建可能溢出堆栈的大型局部变量。这适用于任何语言,而不仅仅是 Fortran。所以创建一个这样的局部变量:
real*8 dualfail(30000000)
导致来自堆栈的双故障内存。
处理大变量的更好方法是从堆中为它们分配内存,这实质上是您的整个主计算机内存。例如,
real*8,allocatable :: dualfail(:)
integer alloc_stat
allocate(dualfail(30000000),stat=alloc_stat)
if( alloc_stat /= 0 ) then
! allocation failed,so take action here
endif
! code to use dualfail here
deallocate(dualfail)
你所有的大变量都应该使用这种技术。
话虽如此,您在 mex 例程中拥有这些大变量的唯一原因似乎是为 body( ) 例程制作 MATLAB 输入和输出的副本。这是一种非常低效的管理方式。你也在深度复制你的输出变量,这些变量只是 0,即使我猜测它们无论如何都会被 body() 覆盖。与其采用这种方法,尤其是在处理大变量时,最好将指向内存的“指针”传递给例程。一种方法是使用 %VAL( ) 结构。这将首先消除创建这些大型局部变量的需要。例如,
call body(%VAL(coord_pr),%VAL(dualnumfam_pr),%VAL(dualfail_pr),etc.
因此,无论您在何处传递 MATLAB 变量数据的副本,您都会将“指针”(实际上是按值传递整数中包含的地址)传递到 MATLAB 变量的原始数据区域。只要您的 body( ) 例程将 MATLAB prhs( ) 输入视为只读,那么这将起作用,而无需像您所做的那样进行深拷贝。这消除了对所有这些 mxCopyReal8ToPtr( ) 和 mxCopyPtrToReal8( ) 调用的需要。
请注意,我在上面使用的“指针”一词是通用的……这些不是 Fortran 指针变量。但是使用实际的 Fortran 指针将是避免深度数据复制的另一种方法……即将 mxGetPr( ) 返回的整数转换为常规 Fortran 指针,然后在代码的下游使用该指针。
我还要指出,您的 mex 例程严重缺乏输入参数检查。您只检查输入的数量并且第一个输入是数字。您应该做的是检查您的每一个输入都是双重的、非复杂的、非稀疏的,并且大小正确。因为它的例程并不健壮,如果输入不完全符合预期,您就有得到错误结果或使 MATLAB 崩溃的风险。
最后,请注意,出于某种原因,The Mathworks 选择将 /fixed 选项硬编码到他们的 Fortran mex 构建文件中。我已经要求他们删除它,但从 R2020a 开始它仍然存在。这会强制编译器将所有文件视为固定格式,即使是以 .f90 结尾的文件。我的建议是在您的系统上找到那些 xml 构建文件,并从这些文件中删除 /fixed 选项。这样您就可以自然地将 .f90 文件编译为自由格式。