为什么用于阈值矩阵元素的 Matlab 逻辑索引操作在性能上优于 mex 实现?

Why is a Matlab logical indexing operation for thresholding matrix elements superior in performance to a mex implementation?

我目前使用 Matlab 代码

m = m.*(abs(m)>=THRESH)

将矩阵 m 中位于零两侧 THRESH 内的元素设置为零。这段代码被调用了数十万次,因此对性能至关重要。通常 [1000, 400] = size(m).

我决定看看是否可以通过使用 mex 函数来提高性能。因此,在 x64 版本编译中使用速度优化设置,我使用以下核心 C++ 代码对矩阵进行阈值处理:

void thresholdArrayMex(mxArray *inArr, const double THRESH){
    double *indata = (double*)mxGetData(inArr);
    const mwSize mrows = mxGetM(inArr);
    const mwSize ncols = mxGetN(inArr);
    const mwSize size = mrows * ncols;

    for (mwSize idx = size; idx--; ){
        if (fabs(indata[idx]) < THRESH) {
            indata[idx] = 0.0;
        }
    }
}

在父函数中,我使用了 mxCreateSharedDataCopy,如 here 所述,这样我就不需要对与 inArr 关联的底层数据进行深度复制(已传递来自 Matlab)。不幸的是,整个 mex 实现平均要慢三倍。罪魁祸首是行 indata[idx] = 0.0;。如果我只从字面上注释掉这一行(保留循环和逻辑比较),mex 文件 运行s 比 matlab 代码快十倍,从而从我们的查询中消除了任何 mex 开销或 lib 链接等。性能下降。

有谁知道为什么将 double 数组的元素赋值为零时性能会受到如此大的影响?难道是因为内存不连续?这是否与我使用 mxCreateSharedDataCopy 就地访问基础数据的方式有关,而 Matlab 在幕后做一些可能很昂贵的事情?我已经尝试在阈值化之前深度复制数组,但是 i) 对 mxDuplicateArray 的调用太昂贵了并且 ii) 赋值操作仍然很昂贵。

编辑 1: 回应评论: 我不是做矩阵乘法或任何其他 Matlab 高度优化的操作。我用

在 Matlab 中进行分析
t1 = 0;
t2 = 0;
t3 = 0;
N = 10000;
THRESH = 0.4;

ThresholdElementsMex(1, rand(1000, 400)); %call once to mitigate any dynamic loading effects

for i = 1:N
    mat1 = 2*(rand(1000, 400)-0.5);
    mat2 = mat1;
    mat3 = mat1;

    atic = tic();
    mat1 = ThresholdElementsMex(THRESH, mat1);
    ta = toc(atic);
    t1 = t1+ta;

    btic = tic();
    ThresholdElementsMex(THRESH, mat2);
    tb = toc(btic);
    t2 = t2+tb;

    ctic = tic();
    mat3 = mat3.*(abs(mat3)>=THRESH);
    tc = toc(ctic);
    t3 = t3+tc;
end

t1 = t1/N
t2 = t2/N
t3 = t3/N

就目前而言,使用 mxCreateSharedDataCopy 的典型平均时间是

t1 = 0.0018
t2 = 0.0020
t3 = 0.00094 % matlab is twice as fast

但简单地注释掉 indata[idx] = 0.0; 给出

t1 = 0.000013 % removing the C++ assignment line reveals its cost
t2 = 0.00038
t3 = 0.00094

所有这些结果都没有显着差异(如果我 运行 多次使用该计时脚本)。

我使用的 C++ 编译器优化是 Maximize Speed (/O2)Yes to Enable Intrinsic Functions (/Oi)Favor fast code (/Ot)

循环中的 if-clause 是要避免的事情。你可以试试

indata[idx] *= double( fabs(indata[idx]) >= THRESH );

祝你好运。