1

ここでのこの関数は、私の実行で多くの時間を費やしています。しかし、表示されるのは、ほとんどの場合、組み込み関数polyareaです。このコードをベクトル化してパフォーマンスを向上させることはできますか?

プロファイラーレポート-

  time   calls
                  1 function [S S_area] = Polygons_intersection_Compute_area(S)
                  2 % Guillaume JACQUENOT
                  3 % guillaume at jacquenot at gmail dot com
                  4 % 2007_10_08
                  5 % 2009_06_16
                  6 % Compute area of each polygon of in S.
                  7 % Results are stored as a field in S
                  8 
  0.50   51945    9 S_area = struct('A', {}); 
  0.20   51945   10 for i=1:numel(S) 
  0.28  103890   11     S(i).area = 0; 
  1.34  103890   12     S_area(i).A = zeros(1,numel(S(i).P)); 
  0.69  103890   13     for j=1:numel(S(i).P) 
  9.24  103890   14         S_area(i).A(j) = polyarea(S(i).P(j).x,S(i).P(j).y); 
  0.28  103890   15         S(i).area      = S(i).area + (1-2*S(i).P(j).hole) * S_area(i).A(j);         
  0.01  103890   16     end 
  0.08  103890   17 end 
4

1 に答える 1

5

4つの問題があります。潜在的なパフォーマンス向上の昇順でそれらについて説明します。

まず、ループ変数名としてiとを使用します。jこれらは、Matlabの架空の単位の名前でもあります。つまり、Matlabは、どちらを意味するのかを調べるために時間を費やす必要があります。つまり、ループがJITされていない場合は、反復ごとにそれを実行する必要があります(これは、JITされていません。これについては、説明します)。

2番目:多次元構造のインデックス作成には、思ったよりも時間がかかります。マルチD構造は、この点でやや悪名高いので、インデックス作成操作が多すぎないようにする必要があります。多くの場合、要素の単純なコピーを作成し、そのコピーに対してすべての操作を実行してから、そのコピーを構造に書き戻すと、パフォーマンスが大幅に向上します。

S_area第三に、最も効率的な方法で事前に割り当てることはありません。構造を事前に割り当てることすらしませんが、事前に割り当てるときに最初のループで構造を拡張しますS_area(i).A。これはすべて改善できます(以下を参照)。

4番目:polyareaは組み込み関数ではないため、この二重ループはJITされません。ユーザーまたはMathworksが(Cではなく)M言語で記述したループ内で関数を呼び出すと、JITコンパイラーはループをコンパイルできなくなります。これは、JITフレームワークで最も厄介な(そして改善可能な)制限ですが、JITされたループは、JITされていないループよりも100倍以上速く実行されることがよくあります。

多くの場合、唯一の解決策は、ループ本体に組み込まれていない関数を「インライン化」することです。Matlabでは、次のことを意味します。関数本体の内容全体をコピーしてループに貼り付け、その本体で呼び出されるすべての非組み込み関数に対してこれを再帰的に実行します。

上記のすべてが、このバージョンのコードにつながります。

% pre-allocate S_area
S_area(numel(S)).A = [];
As = cellfun(@(x) zeros(numel(x),1), {S.P}, 'UniformOutput', false);
[S_area.A] = deal(As{:});

% number of polygons for all S(ii)
numPolys = cellfun(@numel, {S.P});

% enter loop
for ii = 1:numel(S)
    % extract S(ii) only once
    Sii = S(ii);

    Sii.area = 0;
    Aii = S_area(ii).A;        
    for jj = 1:numPolys(ii)

        p = Sii.P(jj);  % extract polygon only once
        x = p.x; % and its x and y components
        y = p.y;            
        sz = size(p);

        % NOTE: core of polyarea. Note that all checks and flexibility, and 
        % therefore user-friendliness, is GONE. Very little has to go wrong 
        % here before a hard-to-understand error is issued. 
        Area = reshape(abs(sum( (x([2:sz(1) 1],:) - x(:,:)).* ...
            (y([2:sz(1) 1],:) + y(:,:)))/2),[1 sz(2:end)]);

        Aii(jj) = Area;
        Sii.area = Sii.area + Area*(1-2*p.hole);
    end

    % place copies back into the strucure
    S_area(ii).A = Aii;
    S(ii).area = Sii.area;

end

これを適切にテストできなかったため、エラーが見つかった場合はお知らせください。修正を試みます。

于 2012-10-30T10:58:26.450 に答える