为什么用于阈值矩阵元素的 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 );
祝你好运。
我目前使用 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 );
祝你好运。