为什么arrayfun不会提高我的struct array操作性能

时间:2012-06-23 14:00:45

标签: matlab struct profiler vectorization

这是输入数据:

  % @param Landmarks:
  %           Landmarks should be 1*m struct. 
  %           m is the number of training set.
  %           Landmark(i).data is a n*2 matrix

功能:

  function Landmarks=CenterOfGravity(Landmarks)
  % align center of gravity

  for i=1 : length(Landmarks)
      Landmarks(i).data=Landmarks(i).data - ones(size(Landmarks(i).data,1),1)...
          *mean(Landmarks(i).data);
  end
  end

使用arrayfun的新函数:

  function [Landmarks] = center_to_gravity(Landmarks)
  Landmarks = arrayfun(@(struct_data)...
                          struct('data', struct_data.data - repmat(mean(struct_data.data), [size(struct_data.data, 1), 1]))...
                                              ,Landmarks);
  end %function center_to_gravity

使用分析器时,我发现时间的使用是 NOT 我所期望的:

  Function          Total Time    Self Time*
  CenterOfGravity     0.011s      0.004 s
  center_to_gravity   0.029s      0.001 s

有人可以告诉我为什么吗?

BTW ......我不能将“arrayfun”添加为我声誉的新标签。

3 个答案:

答案 0 :(得分:4)

使用arrayfun并不算作“代码化矢量化”,如每篇Matlab性能博客文章中所述。

如果您的.data字段与所有地标条目的长度相同,您可以通过首先将所有数据放入单个DATASIZE-BY-LANDMARKSIZE martix,然后运行此命令来矢量化此代码

meanRemovedData = bsxfun(@minus, data, mean(data,1));

但是你失去了很多代码清晰度。 (我很确定bsxfun通常具有类似矢量化的速度优势,但我今天早上没有进行任何测试。)


就其原因而言,我不是一个真正合适的人。但是,矢量化的许多优点依赖于执行连续的存储块的简单操作。存储在结构数组中的数据(我相信)存储为指向不同内存位置的指针数组,这就是为什么你可以在不重新分配整个结构数组的情况下更改Landmarks(i).data的大小或类的原因。

答案 1 :(得分:3)

感谢Amro和Pursuit对我的问题充满热情。

我在Matlab的Jan Simon答案中获得最佳解决方案:

why arrayfun does NOT improve my struct array operation performance

有些观点可以改善效果:

  1. 令人惊讶的是,SUM / LENGTH比MEAN快
  2. timeit可以提供更准确的结果。
  3. 最快的方法是使用这样的技巧:

    m = sum(data,1)/ size(data,1); data(:,1)= data(:,1) - m(1);

答案 2 :(得分:1)

考虑以下三种实现(全部使用BSXFUN进行矢量化):

function s = func1(s)
    for i=1:numel(s)
        s(i).data = bsxfun(@minus, s(i).data, mean(s(i).data));
    end
end

function v = func2(s)
    v = arrayfun(@(ss) bsxfun(@minus,ss.data,mean(ss.data)), ...
        s, 'UniformOutput',false);
    v = struct('data',v);
end

function v = func3(s)
    v = arrayfun(@(ss) struct('data',bsxfun(@minus,ss.data,mean(ss.data))), ...
        s, 'UniformOutput',true);
end

说明:

  • 首先使用for循环迭代结构数组。
  • 其次使用ARRAYFUN返回数据矩阵的单元格数组,然后传递给STRUCT以构建结构数组。
  • 最后一个使用ARRAYFUN并在每次迭代时直接构建结构。

这是一个比较时间的简单测试:

function testArrayStruct()
    %# sample array of structures
    s = struct('data',[]);
    for i=5000:-1:1
        s(i).data = rand(randi(1000),2);
    end

    %# timing
    tic; v1 = func1(s); toc
    tic; v2 = func2(s); toc
    tic; v3 = func3(s); toc

    %# check all have the same output
    assert(isequal(v1,v2,v3))
end

结果:

Elapsed time is 0.357796 seconds.         %# func1
Elapsed time is 0.427568 seconds.         %# func2
Elapsed time is 0.537971 seconds.         %# func3

所以你可以看到基于循环的解决方案实际上是最快的..