Performance 如何在MATLAB中编写矢量化函数
我正在学习MATLAB,我发现很难理解循环与矢量化函数的性能因数。 在我的上一个问题中:我意识到使用矢量化函数与使用4个嵌套循环会使运行时间相差7倍 在该示例中,不需要遍历四维数组的所有维并计算每个向量的中值,而只调用中值(stack,n),其中n表示中值函数的工作维,更简洁、更快 但中间值只是一个非常简单的例子,我很幸运它实现了这个维度参数 我的问题是,如何自己编写一个函数,使其与实现此维度范围的函数一样高效地工作 例如,您有一个函数Performance 如何在MATLAB中编写矢量化函数,performance,matlab,loops,for-loop,vectorization,Performance,Matlab,Loops,For Loop,Vectorization,我正在学习MATLAB,我发现很难理解循环与矢量化函数的性能因数。 在我的上一个问题中:我意识到使用矢量化函数与使用4个嵌套循环会使运行时间相差7倍 在该示例中,不需要遍历四维数组的所有维并计算每个向量的中值,而只调用中值(stack,n),其中n表示中值函数的工作维,更简洁、更快 但中间值只是一个非常简单的例子,我很幸运它实现了这个维度参数 我的问题是,如何自己编写一个函数,使其与实现此维度范围的函数一样高效地工作 例如,您有一个函数my_median_1D,它只对一维向量起作用并返回一个数字
my_median_1D
,它只对一维向量起作用并返回一个数字
如何通过采用n维数组和“工作维”参数编写一个函数my_median\u nD
,其作用类似于MATLAB的median
更新
我找到了在更高维度中计算中值的代码
% In all other cases, use linear indexing to determine exact location
% of medians. Use linear indices to extract medians, then reshape at
% end to appropriate size.
cumSize = cumprod(s);
total = cumSize(end); % Equivalent to NUMEL(x)
numMedians = total / nCompare;
numConseq = cumSize(dim - 1); % Number of consecutive indices
increment = cumSize(dim); % Gap between runs of indices
ixMedians = 1;
y = repmat(x(1),numMedians,1); % Preallocate appropriate type
% Nested FOR loop tracks down medians by their indices.
for seqIndex = 1:increment:total
for consIndex = half*numConseq:(half+1)*numConseq-1
absIndex = seqIndex + consIndex;
y(ixMedians) = x(absIndex);
ixMedians = ixMedians + 1;
end
end
% Average in second value if n is even
if 2*half == nCompare
ixMedians = 1;
for seqIndex = 1:increment:total
for consIndex = (half-1)*numConseq:half*numConseq-1
absIndex = seqIndex + consIndex;
y(ixMedians) = meanof(x(absIndex),y(ixMedians));
ixMedians = ixMedians + 1;
end
end
end
% Check last indices for NaN
ixMedians = 1;
for seqIndex = 1:increment:total
for consIndex = (nCompare-1)*numConseq:nCompare*numConseq-1
absIndex = seqIndex + consIndex;
if isnan(x(absIndex))
y(ixMedians) = NaN;
end
ixMedians = ixMedians + 1;
end
end
你能给我解释一下为什么这个代码比简单的嵌套循环有效吗?它与其他函数一样具有嵌套循环
我不明白怎么会快7倍,而且为什么这么复杂
更新2
我意识到使用中值并不是一个好例子,因为它本身是一个复杂的函数,需要对数组进行排序或其他巧妙的技巧。我用mean重新做了测试,结果更疯狂:
19秒对0.12秒。
这意味着sum的内置方式比嵌套循环快160倍
我真的很难理解一种行业领先的语言怎么会在编程风格的基础上产生如此巨大的性能差异,但我看到了下面答案中提到的要点。更新2(解决您更新的问题)
MATLAB经过优化,可以很好地处理阵列。一旦你习惯了,它实际上是非常好的,只需键入一行,让MATLAB自己做完整的4D循环的东西,而不必担心它。MATLAB通常用于原型设计/一次性计算,因此节省编码时间并放弃C[++|#]的一些灵活性是有意义的
这就是为什么MATLAB在内部将一些循环做得非常好的原因——通常是将它们编码为编译函数
您给出的代码片段实际上并不包含执行主要工作的相关代码行,即
% Sort along given dimension
x = sort(x,dim);
换句话说,您显示的代码只需要在现在已排序的多维数组x
中通过正确的索引访问中值(这不需要太多时间)。访问所有数组元素的实际工作是通过sort
完成的,这是一个内置(即编译和高度优化)函数
原始答案(关于如何在阵列上构建自己的fast函数)
实际上,有相当多的内置函数采用维度参数:min(stack,[],n)
,max(stack,[],n)
,mean(stack,n)
,std(stack,[],n)
,median(stack,n)
,sum(stack,n)
。。。再加上其他内置函数,如exp()
,sin()
自动作用于整个数组的每个元素(即sin(stack)
如果stack
为4D,则会自动为您执行四个嵌套循环),您可以构建许多可能需要的功能,只需依赖现有的内置功能即可
如果这对于一个特定的情况还不够,你应该看一看,哪些是非常强大的函数,可以用“MATLAB方式”来做事情。只要继续搜索问题(或者更确切地说是答案),我就通过这种方式了解了MATLABs的许多优点
作为一个示例,假设您想要实现沿维度n
的堆栈,您可以编写
function result=pnorm(stack, p, n)
result=sum(stack.^p,n)^(1/p);
。。。您可以有效地重用sum
的“哪个维度功能”
更新
正如Max在评论中指出的那样,还可以看看,这是一个非常强大的工具,用于从数组中选择元素(或者甚至更改其形状,这通常是使用)
一般来说,请查看帮助中的部分-它包含上面提到的repmat
等,但也包含cumsum
和一些更模糊的辅助函数,您应该将其用作构建块。
M = median(A,dim) returns the median values for elements along the dimension of A specified by scalar dim
但使用通用函数,您可以尝试使用mat2cell
(可用于n-D数组而不仅仅是矩阵)拆分数组,并通过cellfun
应用my_median_1D
函数。下面我将使用median
作为一个示例来说明您得到的结果是等效的,但是您可以将m文件中定义的任何函数或使用@(args)
符号定义的匿名函数传递给它
>> testarr = [[1 2 3]' [4 5 6]']
testarr =
1 4
2 5
3 6
>> median(testarr,2)
ans =
2.5000
3.5000
4.5000
>> shape = size(testarr)
shape =
3 2
>> cellfun(@median,mat2cell(testarr,repmat(1,1,shape(1)),[shape(2)]))
ans =
2.5000
3.5000
4.5000
你能给我解释一下为什么这个代码比简单的嵌套循环更有效吗?它与其他函数一样具有嵌套循环
嵌套循环的问题不在于嵌套循环本身。这是你在里面执行的操作
每个函数调用(特别是对非内置函数的调用)都会产生一点开销;如果函数执行错误检查(例如,无论输入大小,所需时间相同),则情况会更糟。因此,如果一个函数的开销只有1毫秒,如果调用它1000次,就会浪费一秒钟。如果可以的话
%# sequence of 10 images
fPath = fullfile(matlabroot,'toolbox','images','imdemos');
files = dir( fullfile(fPath,'AT3_1m4_*.tif') );
files = strcat(fPath,{filesep},{files.name}'); %'
I = imread( files{1} );
%# stacked images along the 1st dimension: [numImages H W RGB]
stack1 = zeros([numel(files) size(I) 3], class(I));
for i=1:numel(files)
I = imread( files{i} );
stack1(i,:,:,:) = repmat(I, [1 1 3]); %# grayscale to RGB
end
%# stacked images along the 4th dimension: [H W RGB numImages]
stack4 = permute(stack1, [2 3 4 1]);
%# compute median image from each of these two stacks
tic, m1 = squeeze( median(stack1,1) ); toc
tic, m4 = median(stack4,4); toc
isequal(m1,m4)
Elapsed time is 0.257551 seconds. %# stack1
Elapsed time is 17.405075 seconds. %# stack4