サーチ…


備考

プロファイラを使用したパフォーマンスボトルネックの特定

MATLAB Profilerは、MATLABコードのソフトウェアプロファイリングツールです。プロファイラを使用すると、実行時間とメモリ消費の両方を視覚的に表示することができます。

プロファイラを実行するには、次の2つの方法があります。

  • MATLAB GUIで "Run and Time"ボタンをクリックし、エディタで.mファイルを開きます( R2012bに追加)。

    ツールストリップのボタン

  • プログラムを使用して、

    profile on
    <some code we want to test>
    profile off
    

以下に、サンプルコードとそのプロファイリングの結果を示します。

function docTest

for ind1 = 1:100
  [~] = var(...
            sum(...
                randn(1000)));
end

spy

プロファイラ出力

上記から、 spy関数は実行時間の約25%を占めることがわかります。 「実コード」の場合、このような実行時間の割合が大きい関数は、最適化を避けるべきvarおよびcla類似した関数ではなく、最適化の候補となります。

さらに、 機能名列の項目をクリックすると、その項目の実行時間の詳細を見ることができます。 spyをクリックする例です:

「スパイ」の内部タイミング


また、Profilerを実行する前にprofile('-memory')実行してメモリ消費量をプロファイルすることもできます。

ここに画像の説明を入力

複数の関数の実行時間の比較

広く使われているtictoc組み合わせは、関数やコードスニペットの実行時間を大まかに知ることができます。

いくつかの関数を比較するためには使用すべきではありません。どうして?上記のソリューションを使用してスクリプト内で比較するために、すべてのコードスニペットに等しい条件を指定することはほとんど不可能です。関数が同じ関数空間と共通変数を共有しているので、あとで呼び出される関数とコードスニペットは、すでに初期化された変数と関数を既に利用しています。また、JITコンパイラがこれらのスニペットと同じように処理するかどうかについても洞察はありません。


ベンチマークの専用機能はtimeitです。次の例は、その使用方法を示しています。

配列Aと行列Bます。異なる要素の数を数えることによって、 Bのどの行がA最も類似しているかを判断する必要があります。

function t = bench()
    A = [0 1 1 1 0 0];
    B = perms(A);

    % functions to compare
    fcns = {
        @() compare1(A,B);
        @() compare2(A,B);
        @() compare3(A,B);
        @() compare4(A,B);
    };

    % timeit
    t = cellfun(@timeit, fcns);
end

function Z = compare1(A,B)  
    Z = sum(  bsxfun(@eq,  A,B) , 2);
end
function Z = compare2(A,B)  
    Z = sum(bsxfun(@xor, A, B),2);
end
function Z = compare3(A,B)  
    A = logical(A);
    Z = sum(B(:,~A),2) + sum(~B(:,A),2);
end
function Z = compare4(A,B)  
     Z = pdist2( A, B, 'hamming', 'Smallest', 1 );
end

このベンチマークのこの方法は、最初にこの答えで見られました。

「シングル」になるのは大丈夫です!

概要:

MATLABの数値配列のデフォルトのデータ型はdoubleです。 double数値の浮動小数点表現であり 、この形式は値ごとに8バイト(または64ビット)を要します。 いくつかのケースでは、例えば、整数でのみ扱う場所や数値不安定性が差し迫っ問題ではないときに、このような高いビット深度が必要とされないことがあります。このような理由から、の利点を検討することをお勧めしsingleの精度(または他の適切なタイプを ):

  • より速い実行時間(特にGPUで顕著)。
  • メモリ消費量の半分:メモリ不足エラーのためにdouble障害が発生した場合は成功するかもしれません。ファイルとして保存するときはよりコンパクトです。

変数をサポートされている任意のデータ型からsingle変数に変換するには、以下を使用します。

sing_var = single(var);

デフォルトでdouble値を出力する一般的に使用される関数( zeroseyeones など )は、出力の型/クラスを指定することができます。

スクリプト内の変数をデフォルト以外の精度/型/クラスに変換する:

2016年7月現在、 デフォルトの MATLABデータ型をdoubleから変更する方法はありません。

MATLABでは、新しい変数は通常、作成時に使用される変数のデータ型を模倣します。これを説明するために、次の例を考えてみましょう:

A = magic(3);
B = diag(A);
C = 20*B;
>> whos C
  Name      Size            Bytes  Class     Attributes
  C         3x1                24  double 
A = single(magic(3)); % A is converted to "single"
B = diag(A);
C = B*double(20);     % The stricter type, which in this case is "single", prevails
D = single(size(C));  % It is generally advised to cast to the desired type explicitly.
>> whos C
  Name      Size            Bytes  Class     Attributes
  C         3x1                12  single  

したがって、いくつかの初期変数をキャスト/変換してコード全体に変更が浸透しているように見えるかもしれませんこれは推奨されません (後述の注意点と落とし穴を参照)。

注意点と落とし穴:

  1. 数値ノイズ( singleからdouble singleまでのキャスト時)や情報の損失( doubleからsingle doubleに、または特定の整数型間でキャストする場合)の導入により、繰り返しの変換はお勧めできません

    double(single(1.2)) == double(1.2)   
    ans =
         0
    

    これは、 typecastを使用していくらか軽減できます。 浮動小数点の不正確さに注意してください。

  2. 暗黙的なデータタイピング(すなわち、MATLABが計算の出力のタイプを推測するもの)のみに依存することは、いくつかの望ましくない影響が生じる可能性があるため、 推奨されません。

    • 情報の損失double結果が予想されるが、 singleオペランドとdoubleオペランドの不注意な組み合わせがsingle精度をもたらす。

    • 予期しないほど高いメモリ消費single結果が期待されるが、不注意な計算の結果、 double出力となる。

    • GPUで作業する場合の不要なオーバーヘッドgpuArrayタイプ(つまりVRAMに格納された変数)と非gpuArray変数( 通常はRAMに格納されている変数)をgpuArrayさせる場合、計算を実行する前にデータを一方向に転送する必要があります。この操作には時間がかかり、繰り返し計算では非常に目立つことがあります。

    • 整数型と浮動小数点型を混在させるときのエラー整数型と浮動小数点型の混在入力に対しては、 mtimes* )などの関数は定義されていません。 times.* )のような関数は、整数型の入力に対しては全く定義されていません。また、再びエラーになります。

      >> ones(3,3,'int32')*ones(3,3,'int32')
      Error using  * 
      MTIMES is not fully supported for integer classes. At least one input must be scalar.
      
      >> ones(3,3,'int32').*ones(3,3,'double')
      Error using  .* 
      Integers can only be combined with integers of the same class, or scalar doubles.
      

    コードの可読性を向上させ、不要なタイプのリスクを減らすために、変数が明示的に目的のタイプにキャストされる防御的なアプローチが推奨されます。


関連項目:

NDアレイを再配置すると、全体のパフォーマンスが向上することがあります

場合によっては、一連のND配列に関数を適用する必要があります。この簡単な例を見てみましょう。

A(:,:,1) = [1 2; 4 5];
A(:,:,2) = [11 22; 44 55];
B(:,:,1) = [7 8; 1 2];
B(:,:,2) = [77 88; 11 22];

A =

ans(:,:,1) =

   1   2 
   4   5 

ans(:,:,2) =

   11   22
   44   55

>> B
B =

ans(:,:,1) =

   7   8
   1   2

ans(:,:,2) =

   77   88
   11   22

どちらの行列も3Dですが、次のように計算する必要があります。

result= zeros(2,2);
...
for k = 1:2 
   result(i,j) = result(i,j) + abs( A(i,j,k) - B(i,j,k) );
...

if k is very large, this for-loop can be a bottleneck since MATLAB order the data in a column major fashion. So a better way to compute "result" could be:

% trying to exploit the column major ordering
Aprime = reshape(permute(A,[3,1,2]), [2,4]);
Bprime = reshape(permute(B,[3,1,2]), [2,4]);


>> Aprime
Aprime =

    1    4    2    5
   11   44   22   55

>> Bprime
Bprime =

    7    1    8    2
   77   11   88   22

今度は、上記のループを次のように置き換えます。

result= zeros(2,2);
....
temp = abs(Aprime - Bprime);
for k = 1:2
    result(i,j) = result(i,j) + temp(k, i+2*(j-1));
...

私たちはキャッシュメモリを利用できるようにデータを並べ替えました。並べ替えと再形成にはコストがかかる可能性がありますが、大きなND配列で作業する場合、配列にない配列を使用するよりも、これらの演算に関連する計算コストが大幅に低くなります。

事前配分の重要性

MATLABの配列はメモリ内の連続ブロックとして保持され、MATLABによって自動的に割り当てられ、解放されます。 MATLABは、配列のサイズ変更などのメモリ管理操作を、使いやすい構文の背後に隠します。

a = 1:4

a =

     1     2     3     4

a(5) = 10  % or alternatively a = [a, 10]

a =

     1     2     3     4    10

これは重要なことですが、 a(5) = 10はMATLABにサイズ5の新しいメモリブロックを割り当て、最初の4つの数値をコピーし、5番目を10に設定します。これはO(numel(a))操作であり、 O(1)ではありません。

次の点を考慮してください。

clear all
n=12345678;
a=0;
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 3.004213 seconds.

aはこのループでn回再割り当てされます(MATLABによって行われるいくつかの最適化を除く)。 MATLABは警告メッセージを表示します。

"変数 'a'はループの繰り返しごとにサイズが変わるようですが、速度を事前に割り当てることを検討してください。"

私たちが事前に配属するとどうなりますか?

a=zeros(1,n);
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 0.410531 seconds.

ランタイムが一桁減少していることがわかります。

事前配分の方法:

MATLABは、ユーザーの特定の要件に応じて、ベクトルと行列の割り当てにさまざまな機能を提供します。これらには、 zerosonesnaneyetrueなどが含まれtrue

a = zeros(3)       % Allocates a 3-by-3 matrix initialized to 0
a =

     0     0     0
     0     0     0
     0     0     0

a = zeros(3, 2)     % Allocates a 3-by-2 matrix initialized to 0
a =

     0     0
     0     0
     0     0

a = ones(2, 3, 2)      % Allocates a 3 dimensional array (2-by-3-by-2) initialized to 1
a(:,:,1) =

     1     1     1
     1     1     1


a(:,:,2) =

     1     1     1
     1     1     1

a = ones(1, 3) * 7  % Allocates a row vector of length 3 initialized to 7
a =

     7     7     7

データ型も指定できます。

a = zeros(2, 1, 'uint8');  % allocates an array of type uint8

既存の配列のサイズを複製することも簡単です:

a = ones(3, 4);       % a is a 3-by-4 matrix of 1's
b = zeros(size(a));  % b is a 3-by-4 matrix of 0's

そして、タイプをクローン:

a = ones(3, 4, 'single');       % a is a 3-by-4 matrix of type single
b = zeros(2, 'like', a);        % b is a 2-by-2 matrix of type single

「like」は複雑さ希薄性もクローン化することに注意してください。

事前kronrandgallerykronbsxfuncolonなど、必要な最終サイズの配列を返す関数を使用して暗黙的に実現されます。たとえば、線形に変化する要素を持つベクトルを割り当てる一般的な方法は、コロン演算子(2または3オペランドバリアント1を使用 )を使用することです。

a = 1:3 
a =

     1     2     3

a = 2:-3:-4
a =

     2    -1    -4

セル配列は、 zeros()とほぼ同じ方法でcell()関数を使用して割り当てることができます。

a = cell(2,3)
a = 

    []    []    []
    []    []    []

セル配列は、セルの内容を記憶している場所へのポインタを保持することによって機能することに注意してください。したがって、すべての事前割り当てのヒントは個々のセル配列要素にも適用されます。


参考文献:



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow