MATLAB Language
Prestaties en benchmarking
Zoeken…
Opmerkingen
- Profilering code is een manier om de gevreesde praktijk van "het voorkomen voortijdige optimalisatie ", door zich te richten de ontwikkelaar op die delen van de code die daadwerkelijk optimalisatie inspanningen te rechtvaardigen.
- MATLAB-documentatieartikel getiteld " Prestaties van uw programma meten ".
Knelpunten in de prestaties identificeren met de Profiler
De MATLAB Profiler is een hulpmiddel voor softwareprofilering van MATLAB-code. Met behulp van de Profiler is het mogelijk om een visuele weergave te krijgen van zowel de uitvoeringstijd als het geheugenverbruik.
Het uitvoeren van de Profiler kan op twee manieren:
Klik op de knop "Uitvoeren en tijd" in de MATLAB GUI terwijl er een
.m
bestand geopend is in de editor (toegevoegd in R2012b ).Programmatisch met behulp van:
profile on <some code we want to test> profile off
Hieronder vindt u enkele voorbeeldcode en het resultaat van de profilering:
function docTest
for ind1 = 1:100
[~] = var(...
sum(...
randn(1000)));
end
spy
Uit het bovenstaande leren we dat de spy
ongeveer 25% van de totale uitvoeringstijd in beslag neemt. In het geval van "echte code" zou een functie die zo'n groot percentage van de uitvoeringstijd in beslag neemt een goede kandidaat zijn voor optimalisatie, in tegenstelling tot functies analoog aan var
en cla
waarvan de optimalisatie moet worden vermeden.
Bovendien is het mogelijk om op items in de kolom Functienaam te klikken om een gedetailleerd overzicht van de uitvoeringstijd voor dat item te zien. Hier is het voorbeeld van klikken op spy
:
Het is ook mogelijk om geheugenconsumptie te profileren door profile('-memory')
voordat de Profiler wordt uitgevoerd.
Vergelijking van de uitvoeringstijd van meerdere functies
De veel gebruikte combinatie van tic
en toc
kan een ruw idee geven van de uitvoeringstijd van een functie of codefragmenten.
Voor het vergelijken van verschillende functies moet het niet worden gebruikt. Waarom? Het is bijna onmogelijk om alle codefragmenten gelijke voorwaarden te bieden om in een script met bovenstaande oplossing te vergelijken. Misschien delen de functies dezelfde functieruimte en gemeenschappelijke variabelen, zodat later genoemde functies en codefragmenten al profiteren van eerder geïnitialiseerde variabelen en functies. Ook is er geen inzicht of de JIT-compiler deze vervolgens genoemde fragmenten gelijk zou behandelen.
De speciale functie voor benchmarks is timeit
. Het volgende voorbeeld illustreert het gebruik ervan.
Er zijn de array A
en de matrix B
Bepaald moet worden welke rij van B
het meest op A
lijkt door het aantal verschillende elementen te tellen.
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
Deze manier van benchmark werd voor het eerst in dit antwoord gezien .
Het is ok om `single 'te zijn!
Overzicht:
Het standaard gegevenstype voor numerieke arrays in MATLAB is double
. double
is een weergave van getallen met drijvende komma en dit formaat neemt 8 bytes (of 64 bits) per waarde in beslag. In sommige gevallen, waar bijvoorbeeld alleen het omgaan met gehele getallen of wanneer numerieke instabiliteit geen dreigend probleem is, is een dergelijke hoge bitdiepte mogelijk niet vereist. Om deze reden wordt geadviseerd om de voordelen van single
precisie (of andere geschikte typen ) te overwegen:
- Snellere uitvoeringstijd (vooral merkbaar op GPU's).
- De helft van het geheugenverbruik: kan slagen wanneer
double
mislukt vanwege een geheugenfout; compacter bij het opslaan als bestanden.
Het omzetten van een variabele van elk ondersteund gegevenstype naar single
gebeurt met behulp van:
sing_var = single(var);
Sommige veelgebruikte functies (zoals: zeros
, eye
, ones
, etc. ) die standaard double
waarden uitvoeren, maken het mogelijk om het type / de klasse van de uitvoer op te geven.
Variabelen in een script omzetten naar een niet-standaard precisie / type / klasse:
Vanaf juli 2016 bestaat er geen gedocumenteerde manier om het standaard MATLAB-gegevenstype te wijzigen van double
.
In MATLAB bootsen nieuwe variabelen meestal de gegevenstypen van variabelen na die worden gebruikt bij het maken ervan. Om dit te illustreren, overweeg het volgende voorbeeld:
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
Het kan dus voldoende lijken om verschillende initiële variabelen te casten / converteren om de verandering door de code te laten doordringen - dit wordt echter afgeraden (zie kanttekeningen en valkuilen hieronder).
Voorbehoud & valkuilen:
Herhaalde conversies worden afgeraden vanwege de introductie van numerieke ruis (bij het casten van
single
naardouble
) of verlies van informatie (bij het casten vandouble
naarsingle
of tussen bepaalde typen gehele getallen ), bijvoorbeeld:double(single(1.2)) == double(1.2) ans = 0
Dit kan enigszins worden beperkt met
typecast
. Zie ook Let op drijvende komma onnauwkeurigheid .Het uitsluitend ontmoedigen van impliciet typen van gegevens (dat wil zeggen wat MATLAB denkt dat het type uitvoer van een berekening zou moeten zijn) wordt afgeraden vanwege verschillende ongewenste effecten die kunnen optreden:
Verlies van informatie : wanneer een
double
resultaat wordt verwacht, maar een achteloze combinatie vansingle
endouble
operanden levertsingle
precisie op.Onverwacht hoog geheugenverbruik : wanneer een
single
resultaat wordt verwacht, maar een onzorgvuldige berekening resulteert in eendouble
uitvoer.Onnodige overhead bij het werken met GPU's : bij het mengen van
gpuArray
typen (dwz variabelen die zijn opgeslagen in VRAM) met niet-gpuArray
variabelen (dwz die meestal worden opgeslagen in RAM) moeten de gegevens op de een of andere manier worden overgedragen voordat de berekening kan worden uitgevoerd. Deze bewerking kost tijd en kan zeer merkbaar zijn in repetitieve berekeningen.Fouten bij het mengen van drijvende-komma-typen met gehele getallen : functies zoals
mtimes
(*
) zijn niet gedefinieerd voor gemengde invoer van gehele en drijvende komma-typen - en zullen fouten bevatten. Functies zoalstimes
(.*
) Zijn helemaal niet gedefinieerd voor invoer van het gehele type - en zullen opnieuw fouten maken.>> 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.
Voor een betere leesbaarheid van de code en een verminderd risico op ongewenste typen, wordt een defensieve aanpak geadviseerd , waarbij variabelen expliciet naar het gewenste type worden gegoten.
Zie ook:
- MATLAB-documentatie: getallen met drijvende komma .
- Mathworks 'technisch artikel: Best Practices voor het converteren van MATLAB-code naar een vast punt .
een ND-array herschikken kan de algehele prestaties verbeteren
In sommige gevallen moeten we functies toepassen op een set ND-arrays. Laten we dit eenvoudige voorbeeld bekijken.
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
Beide matrices zijn 3D, laten we zeggen dat we het volgende moeten berekenen:
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 vervangen we de bovenstaande lus als volgt:
result= zeros(2,2);
....
temp = abs(Aprime - Bprime);
for k = 1:2
result(i,j) = result(i,j) + temp(k, i+2*(j-1));
...
We hebben de gegevens opnieuw ingedeeld zodat we het cachegeheugen kunnen benutten. Permutatie en hervorming kunnen kostbaar zijn, maar bij het werken met grote ND-arrays zijn de rekenkosten voor deze bewerkingen veel lager dan bij het werken met niet-gearrangeerde arrays.
Het belang van preallocatie
Arrays in MATLAB worden als continue blokken in het geheugen bewaard, toegewezen en automatisch vrijgegeven door MATLAB. MATLAB verbergt geheugenbeheerbewerkingen zoals het wijzigen van het formaat van een array achter eenvoudig te gebruiken syntaxis:
a = 1:4
a =
1 2 3 4
a(5) = 10 % or alternatively a = [a, 10]
a =
1 2 3 4 10
Het is belangrijk om te begrijpen dat het bovenstaande geen triviale bewerking is, a(5) = 10
zorgt ervoor dat MATLAB een nieuw geheugenblok van grootte 5 toewijst, de eerste 4 nummers kopieert en de 5e op 10 zet. Dat is een O(numel(a))
bewerking en geen O(1)
.
Stel je de volgende situatie voor:
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
wordt n
keer opnieuw toegewezen in deze lus (exclusief enkele optimalisaties uitgevoerd door MATLAB)! Merk op dat MATLAB ons een waarschuwing geeft:
"De variabele 'a' lijkt van grootte te veranderen bij elke lus-iteratie. Overweeg voorallocatie voor snelheid."
Wat gebeurt er als we vooraf toewijzen?
a=zeros(1,n);
tic
for i = 2:n
a(i) = sqrt(a(i-1)) + i;
end
toc
Elapsed time is 0.410531 seconds.
We kunnen zien dat de looptijd wordt verlaagd met een orde van grootte.
Methoden voor preallocatie:
MATLAB biedt verschillende functies voor het toewijzen van vectoren en matrices, afhankelijk van de specifieke vereisten van de gebruiker. Deze omvatten: 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
Een gegevenstype kan ook worden gespecificeerd:
a = zeros(2, 1, 'uint8'); % allocates an array of type uint8
Het is ook eenvoudig om de grootte van een bestaande array te klonen:
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
En kloon het type:
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
merk op dat 'like' ook complexiteit en spaarzaamheid kloneert .
Preallocatie wordt impliciet bereikt met behulp van elke functie die een array van de uiteindelijke vereiste grootte retourneert, zoals rand
, gallery
, kron
, bsxfun
, colon
en vele anderen. Een veelgebruikte manier om vectoren toe te wijzen met lineair variërende elementen is bijvoorbeeld met de dubbele punt-operator (met de 2- of 3-operandvariant 1 ):
a = 1:3
a =
1 2 3
a = 2:-3:-4
a =
2 -1 -4
Celmatrices kunnen worden toegewezen met behulp van de functie cell()
op vrijwel dezelfde manier als zeros()
.
a = cell(2,3)
a =
[] [] []
[] [] []
Merk op dat celmatrices werken door wijzers naar de locaties in het geheugen van celinhoud te houden. Dus alle preallocatietips zijn ook van toepassing op de afzonderlijke celarray-elementen.
Verder lezen:
- Officiële MATLAB-documentatie over " Preallocating Memory ".
- Officiële MATLAB-documentatie over " Hoe MATLAB geheugen toewijst ".
- Preallocatieprestaties op Matlab zonder papieren .
- Inzicht in Array Preallocation op Loren over de kunst van MATLAB