MATLAB Language
Prestanda och benchmarking
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 ).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
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
:
Det är också möjligt att profilera minnesförbrukning genom att köra profile('-memory')
innan du kör Profiler.
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:
Upprepade omvandlingar avskräcks på grund av införandet av numeriskt brus (vid casting från
single
tilldouble
) eller förlust av information (vid casting fråndouble
tillsingle
, 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 .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 avsingle
ochdouble
operander ger ensingle
precision.Oväntad hög minneskonsumtion : när ett
single
resultat förväntas men en slarvig beräkning resulterar i endouble
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 ärgpuArray
(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 somtimes
(.*
) 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:
- MATLAB-dokumentation: Flyttalsnummer .
- Mathworks tekniska artikel: Bästa metoder för konvertering av MATLAB-kod till fast punkt .
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:
- Officiell MATLAB-dokumentation om " Förkunna minne ".
- Officiell MATLAB-dokumentation om " Hur MATLAB tilldelar minne ".
- Förhandslokaliseringsprestanda på odokumenterad matlab .
- Förstå Array Preallocation på Loren om konsten att MATLAB