为啥到达 MEX 文件的最后一行后返回 Matlab 需要这么长时间?

Posted

技术标签:

【中文标题】为啥到达 MEX 文件的最后一行后返回 Matlab 需要这么长时间?【英文标题】:Why does it take such a long time to return to Matlab after reaching the last line of a MEX file?为什么到达 MEX 文件的最后一行后返回 Matlab 需要这么长时间? 【发布时间】:2018-09-15 03:59:36 【问题描述】:

在我的 MEX 文件的最后一行完成执行后,大约需要 14 秒才能返回到 matlab 命令行。

当matlab中的MEX文件计时:

D=rand(14000)+rand(14000)*1i;
tic;
[A B C]=myMexFile(D);
toc
disp(datetime('now'));

输出是:

Elapsed time is 35.192704 seconds.
   15-Sep-2018 16:51:35

使用以下最小工作示例从 C 中对 MEX 文件进行计时:

#include <mex.h>
#include <sys/time.h>
#include <time.h>
#include <cuComplex.h>

double getHighResolutionTime() 
    struct timeval tod;
    gettimeofday(&tod, NULL);
    double time_seconds = (double) tod.tv_sec + ((double) tod.tv_usec / 1000000.0);
    return time_seconds;


void double2cuDoubleComplex(cuDoubleComplex* p, double* pr, double* pi,int numElements)
    for(int j=0;j<numElements;j++)
        p[j].x=pr[j];
        p[j].y=pi[j];
    


void cuDoubleComplex2double(cuDoubleComplex* p, double* pr, double* pi,int numElements)
    for(int j=0;j<numElements;j++)
        pr[j]= p[j].x;
        pi[j]= p[j].y;
    


void mexFunction( int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[]) 

    double tic=getHighResolutionTime();

    int m=(int)mxGetM(prhs[0]);
    int n=(int)mxGetN(prhs[0]);
    int SIZE=m*n;

    //get pointers to input data from matlab and convert to 
    //interleaved (Fortran) ordering
    cuDoubleComplex *Gr= (cuDoubleComplex*) mxMalloc(SIZE*sizeof(cuDoubleComplex));
    double2cuDoubleComplex(Gr,mxGetPr(prhs[0]),mxGetPi(prhs[0]),SIZE);


    //modify the input data, allocate output matrices, and convert 
    //back to split (matlab) ordering.
    Gr[0].x=0.0;
    plhs[0] = mxCreateDoubleMatrix(m,m,mxCOMPLEX);
    cuDoubleComplex2double(Gr,mxGetPr(plhs[0]),mxGetPi(plhs[0]),SIZE);

    Gr[0].x=1.0;
    plhs[1] = mxCreateDoubleMatrix(m,m,mxCOMPLEX);
    cuDoubleComplex2double(Gr,mxGetPr(plhs[1]),mxGetPi(plhs[1]),SIZE);

    Gr[0].x=2.0;
    plhs[2] = mxCreateDoubleMatrix(m,m,mxCOMPLEX);
    cuDoubleComplex2double(Gr,mxGetPr(plhs[2]),mxGetPi(plhs[2]),SIZE);

    mxFree(Gr);

    double elapsed=getHighResolutionTime()-tic;mexPrintf("%f\n", elapsed);
    time_t current_time = time(NULL);
    char* c_time_string = ctime(&current_time);
    mexPrintf("time at end of MEX file %s\n", c_time_string);

输出是:

21.676793
time at end of MEX file Sat Sep 15 16:51:21 2018

Matlab 返回的时间为 35.19 秒,而 MEX 文件实际上需要 21.67 秒才能到达最后一行。日期和时间相隔约 14 秒,即 MEX 文件为 16:51:21,matlab 为 16:51:35。

输出是非常大的矩阵,但它们在 MEX 文件的最后一行之前成功分配和初始化。我想不出别的了。是什么导致了这种行为,我该如何避免?

更新:我已经在更多机器上尝试过,但时间差异仍然存在。

更新:我已经用一个最小的工作示例替换了上面的伪代码。请注意,上述代码实际上并未使用任何 GPU 功能。我包含 cuComplex.h 标头只是为了使用 cuDoubleComplex 数据类型。

【问题讨论】:

也许是垃圾收集器?尝试删除部分代码,直到它停止发生 @ThP 我试过摆脱 mxFree,以及用 malloc 和 free 替换 mxMalloc 和 mxFree。结果相同。 感谢您使用最小示例进行更新。我想我知道会发生什么。我怀疑您使用 MATLAB R2018a 或更高版本,并在 R2017b 兼容模式(这是默认模式)下编译 MEX 文件。 R2018a 将复数存储在一个内存块中,而不是像在早期版本中那样使用 2 个单独的块。在兼容模式下编译 MEX 文件意味着 MATLAB 在运行 MEX 文件之前将复杂数据复制到旧格式,并将任何复杂输出数据复制到新格式。我敢打赌这就是你看到的延迟! @Cris Luengo 你成功了!这正是问题所在。我以为我正在提交作业以在 R2018a 之前版本的 Matlab 上运行,但事实证明我的提交脚本中有一个错误,代码实际上是在 R2018a 上运行的......!不过,我会用这个警告更新问题。 【参考方案1】:

从 MATLAB R2018a 开始,MATLAB internally stores complex arrays in an interleaved format。在以前的版本中,MATLAB 使用两个独立的内存块来存储复数数据:一个用于实数值,一个用于虚数值。在 MEX 文件中,您使用 mxGetPr()mxGetPi() 获取指向这两个内存块的指针(这些函数称为“分离复杂 API”)。

从 R2018a 开始,使用新的内部数据表示,可以通过两种不同的方式编译 MEX 文件:

    一种兼容模式(这是默认设置,您可以在mex 命令中添加-R2017b 以强制使用此模式),您可以在其中编译旧的MEX 文件而无需修改。因此,这些 MEX 文件使用“Separate Complex API”。 MATLAB 在执行 MEX 文件代码之前将复数数据从其新的交错表示复制到单独的实数和虚数内存块中,并将任何复数输出数组复制回交错格式。这显然需要一些时间。这是 OP 观察到延迟的原因。

    一种新模式(将 -R2018a 添加到 mex 命令),其中 MEX 文件使用 the new "Interleaved Complex API"。也就是说,MEX 文件代码适用于使用新的交错复杂格式。由于您可能希望从 MEX 文件调用的大多数 C 和 C++ 库都使用交错格式,这实际上是一个很大的优势。

避免在处理复杂数组的 MEX 文件的开始和结束处出现大延迟的解决方案是重写它们以使用新的“交错复杂 API”。这需要the following changes:

查找mxGetPr()mxGetPi() 函数的所有用途。后者不再可用。如果输入数组是复值,mxGetPr() 现在会引发错误。相反,使用mxGetData(),它将返回一个指向复杂交错数据的指针。请注意,他们建议您不要将其用于数字数据,似乎是they prefer you use the new "typed data access functions"。 mxGetImagData(),和mxGetPi()一样,已经不存在了。

设置数据指针 (mxSet...()) 的函数也是如此。

不要忘记使用mxIsComplex()mxIsDouble() 检查输入数组是否真的复数和双精度类型。

函数 mxGetElementSize 现在为复数双精度数据返回 16,而不是像以前那样返回 8。

使用 mex -R2018a &lt;filename&gt; 编译您的 MEX 文件。

Here are some more troubleshooting tips.

【讨论】:

以上是关于为啥到达 MEX 文件的最后一行后返回 Matlab 需要这么长时间?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 N Queens 算法会到达最后一行?

为啥堆栈展开后程序无法到达正确的返回指令?

python文件读取到最后一行后怎么返回第一行

为啥我收到错误“无效的 MEX 文件”,而文件位于当前文件夹中?

为啥 TTTAttributedLabel 在 IOS 上的某些特殊符号后不显示最后一行内容?

如何诊断为啥在 MATLAB 中执行 .mex 文件时无法加载 .so 文件?