Sök…


Anmärkningar

  • Profileringskod är ett sätt att undvika den fruktade praxis för "för tidig optimering " genom att fokusera utvecklaren på de delar av koden som faktiskt motiverar optimeringsinsatser.
  • MATLAB dokumentationsartikel med titeln " Mät prestanda för ditt program ".

Identifiera prestandaflaskhalsar med hjälp av Profiler

MATLAB Profiler är ett verktyg för programvaruprofilering av MATLAB-kod. Med Profiler är det möjligt att få en visuell representation av både exekveringstid och minneskonsumtion.

Att köra Profiler kan göras på två sätt:

  • Klicka på knappen "Kör och tid" i MATLAB GUI medan en .m fil är öppen i redigeraren (tillagd i R2012b ).

    Knapp i verktygsstrip

  • Programmatiskt med hjälp av:

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

Nedan följer några exempelkoder och resultatet av dess profilering:

function docTest

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

spy

Profilerutgång

Från ovanstående lär vi oss att spy tar cirka 25% av den totala utförandetiden. När det gäller "verklig kod", skulle en funktion som tar en så stor andel av körningstiden vara en bra kandidat för optimering, i motsats till funktioner som är analoga med var och cla vars optimering bör undvikas.

Dessutom är det möjligt att klicka på poster i kolumnen Funktionsnamn för att se en detaljerad fördelning av körningstiden för den posten. Här är exemplet med att klicka på spy :

Intern tidpunkt för "spion"


Det är också möjligt att profilera minnesförbrukning genom att köra profile('-memory') innan du kör Profiler.

ange bildbeskrivning här

Jämför exekveringstid för flera funktioner

Den allmänt använda kombinationen av tic och toc kan ge en grov uppfattning om körningstiden för en funktion eller kodavsnitt.

För att jämföra flera funktioner bör den inte användas. Varför? Det är nästan omöjligt att tillhandahålla lika villkor för alla kodavsnitt att jämföra inom ett skript med ovanstående lösning. Kanske delar funktionerna samma funktionsutrymme och vanliga variabler, så senare kallade funktioner och kodavsnitt utnyttjar redan tidigare initierade variabler och funktioner. Dessutom finns det ingen insikt om JIT-kompilatorn skulle hantera dessa senare kallade utdragen lika.


Den dedikerade funktionen för riktmärken är timeit . Följande exempel illustrerar dess användning.

Det finns matrisen A och matrisen B Det bör bestämmas vilken rad av B är mest lik A genom att räkna antalet olika element.

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

Detta sätt att jämföra först settes i detta svar .

Det är ok att vara "singel"!

Översikt:

Standarddatatypen för numeriska arrayer i MATLAB är double . double är en flytande punktrepresentation av siffror , och detta format tar 8 byte (eller 64 bitar) per värde. I vissa fall, t.ex. om man bara handlar med heltal eller när numerisk instabilitet inte är en överhängande fråga, kanske inte så högt bitdjup krävs. Av detta skäl rekommenderas att ta hänsyn till fördelarna med single precision (eller andra lämpliga typer ):

  • Snabbare körningstid (märks särskilt på GPU: er).
  • Halva minneskonsumtionen: kan lyckas där double misslyckas på grund av ett minnesfel; mer kompakt vid lagring som filer.

Konvertering av en variabel från vilken datatyp som stöds till single görs med:

sing_var = single(var);

Vissa vanligt använda funktioner (som: zeros , eye , ones etc. ) som matar ut double värden som standard, gör det möjligt att ange typ / klass för utgången.

Konvertera variabler i ett skript till en icke-standard precision / typ / klass:

Från och med juli 2016 finns det inget dokumenterat sätt att ändra standardtypen för MATLAB från double .

I MATLAB efterliknar vanligtvis nya variabler datatyperna för variabler som används när du skapar dem. För att illustrera detta, tänk på följande exempel:

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  

Således kan det tyckas tillräckligt att casta / konvertera flera initiala variabler för att ha ändringspermeatet genom hela koden - men detta avskräcks (se Caveats & Pitfalls nedan).

Varningar och fallgropar:

  1. Upprepade omvandlingar avskräcks på grund av införandet av numeriskt brus (vid casting från single till double ) eller förlust av information (vid casting från double till single , eller mellan vissa heltalstyper ), t.ex.

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

    Detta kan mildras något med typecast . Se också Var medveten om flytande punktens felaktighet .

  2. Att bara förlita sig på implicit datatypning (dvs. vad MATLAB antar att typen av utgången från en beräkning borde vara) är avskräckt på grund av flera oönskade effekter som kan uppstå:

    • Förlust av information : när ett double resultat förväntas, men en slarvig kombination av single och double operander ger en single precision.

    • Oväntad hög minneskonsumtion : när ett single resultat förväntas men en slarvig beräkning resulterar i en double utgång.

    • Onödigt overhead när man arbetar med GPU : er : när man blandar gpuArray typer (dvs. variabler lagrade i VRAM) med variabler som inte är gpuArray (dvs de som vanligtvis lagras i RAM) måste data överföras på ett eller annat sätt innan beräkningen kan utföras. Denna operation tar tid och kan vara mycket märkbar vid upprepade beräkningar.

    • Fel vid blandning av flytande punkttyper med heltalstyper : funktioner som mtimes ( * ) definieras inte för blandade ingångar av heltal och flytande punkttyper - och kommer att fel. Funktioner som times ( .* ) Definieras inte alls för heltal-ingångar - och kommer igen att fel.

      >> 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.
      

    För bättre kodläsbarhet och minskad risk för oönskade typer rekommenderas en defensiv strategi, där variabler uttryckligen kastas till önskad typ.


Se även:

Omarrangera en ND-array kan förbättra den totala prestandan

I vissa fall måste vi tillämpa funktioner på en uppsättning ND-arrays. Låt oss titta på detta enkla exempel.

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

Båda matriserna är 3D, låt oss säga att vi måste beräkna följande:

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

Nu ersätter vi ovanstående slinga för följande:

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

Vi omarrangerade data så att vi kan utnyttja cacheminnet. Permutation och omformning kan vara kostsamt men när man arbetar med stora ND-arrayer är beräkningskostnaderna för dessa operationer mycket lägre än att arbeta med inte arrangerade matriser.

Vikten av förfördelning

Matriser i MATLAB hålls som kontinuerliga block i minnet, tilldelas och släpps automatiskt av MATLAB. MATLAB döljer minneshanteringsfunktioner som att ändra storlek på en matris bakom lättanvänd syntax:

a = 1:4

a =

     1     2     3     4

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

a =

     1     2     3     4    10

Det är viktigt att förstå att ovanstående inte är en triviell operation, a(5) = 10 kommer att få MATLAB att tilldela ett nytt minne med storlek 5, kopiera de första 4 siffrorna över och ställa in 5'th till 10. Det är en O(numel(a)) operation och inte O(1) .

Tänk på följande:

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 omfördelas n gånger i denna slinga (exklusive vissa optimeringar som utförs av MATLAB)! Observera att MATLAB ger oss en varning:

"Variabeln 'a' verkar förändra storleken på varje loop-iteration. Överväg att förflytta för hastighet."

Vad händer när vi förflyttar?

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

Elapsed time is 0.410531 seconds.

Vi kan se att körtiden reduceras med en storleksordning.

Metoder för förflyttning:

MATLAB tillhandahåller olika funktioner för allokering av vektorer och matriser, beroende på användarens specifika krav. Dessa inkluderar: zeros , ones , nan , eye , true etc.

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

En datatyp kan också specificeras:

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

Det är också lätt att klona storleken på en befintlig matris:

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

Och klona typen:

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

Observera att "gilla" också klonar komplexitet och gleshet .

Förhandslokalisering uppnås implicit med användning av alla funktioner som returnerar en matris med den slutliga önskade storleken, såsom rand , gallery , kron , bsxfun , colon och många andra. Till exempel är ett vanligt sätt att tilldela vektorer med linjärt varierande element genom att använda kolonoperatören (med antingen 2- eller 3-operandvarianten 1 ):

a = 1:3 
a =

     1     2     3

a = 2:-3:-4
a =

     2    -1    -4

Celluppsättningar kan allokeras med hjälp av funktionen cell() på ungefär samma sätt som zeros() .

a = cell(2,3)
a = 

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

Observera att celluppsättningar fungerar genom att hålla pekare till platserna i minnet för cellinnehållet. Så alla förfördelningstips gäller även för de enskilda celluppsättningselementen.


Vidare läsning:



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow