在 Matlab 上增加 Mex 的文件大小

问题描述

我正在用 Matlab r2019a 编写一个 FORTRAN mex 文件。我已经用小数组测试了代码,一切正常。但是,当我尝试增加数组的大小时,出现以下错误。

致命错误 LNK1248:图像大小 (9B993000) 超过最大允许大小 (80000000)

我在 Windows 10 上使用 Visual Studio 2017 和英特尔 FORTRAN 编译器,我有 16GB 的内存。 此链接似乎有解决方案,但我似乎无法在 VS 2017 中找到用于传递 -heap-array 的链接器选项。

https://software.intel.com/content/www/us/en/develop/articles/intel-fortran-compiler-increased-stack-usage-of-80-or-higher-compilers-causes-segmentation-fault.html

有人能告诉我如何增加尺寸吗?

谢谢 基南

    #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 文件编译为自由格式。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...