MATLAB Language
Prestazioni e Benchmarking
Ricerca…
Osservazioni
- Il codice di profilazione è un modo per evitare la temuta pratica di " ottimizzazione prematura ", concentrando lo sviluppatore su quelle parti del codice che effettivamente giustificano gli sforzi di ottimizzazione.
- Articolo della documentazione di MATLAB intitolato " Misura le prestazioni del tuo programma ".
Identificazione dei colli di bottiglia delle prestazioni utilizzando il Profiler
Il MATLAB Profiler è uno strumento per la profilazione software del codice MATLAB. Usando il Profiler, è possibile ottenere una rappresentazione visiva del tempo di esecuzione e del consumo di memoria.
L'esecuzione di Profiler può essere eseguita in due modi:
Facendo clic sul pulsante "Esegui e ora" nella GUI MATLAB mentre si ha un file
.m
aperto nell'editor (aggiunto in R2012b ).Programmaticamente, utilizzando:
profile on <some code we want to test> profile off
Di seguito sono riportati alcuni esempi di codice e il risultato della sua profilazione:
function docTest
for ind1 = 1:100
[~] = var(...
sum(...
randn(1000)));
end
spy
Da quanto sopra, apprendiamo che la funzione spy
impiega circa il 25% del tempo totale di esecuzione. Nel caso del "codice reale", una funzione che richiede una percentuale così elevata del tempo di esecuzione sarebbe un buon candidato per l'ottimizzazione, al contrario delle funzioni analoghe a var
e cla
cui ottimizzazione dovrebbe essere evitata.
Inoltre, è possibile fare clic sulle voci nella colonna Nome funzione per visualizzare un'analisi dettagliata del tempo di esecuzione per quella voce. Ecco l'esempio di clic spy
:
È anche possibile profilare il consumo di memoria eseguendo il profile('-memory')
prima di eseguire il Profiler.
Confronto del tempo di esecuzione di più funzioni
La combinazione ampiamente utilizzata di tic
e toc
può fornire un'idea approssimativa del tempo di esecuzione di una funzione o di snippet di codice.
Per confrontare parecchie funzioni non dovrebbe essere usato. Perché? È quasi impossibile fornire condizioni uguali per tutti i frammenti di codice da confrontare all'interno di uno script utilizzando la soluzione di cui sopra. Forse le funzioni condividono lo stesso spazio di funzione e le variabili comuni, quindi in seguito chiamate funzioni e frammenti di codice già sfruttano le variabili e le funzioni precedentemente inizializzate. Inoltre, non vi è alcuna idea se il compilatore JIT gestisca allo stesso modo questi snippet chiamati successivamente.
La funzione dedicata per i benchmark è timeit
. L'esempio seguente illustra il suo uso.
Ci sono la matrice A
e la matrice B
Dovrebbe essere determinato quale riga di B
è la più simile ad A
contando il numero di elementi diversi.
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
Questo metodo di riferimento è stato visto per la prima volta in questa risposta .
Va bene essere "single"!
Panoramica:
Il tipo di dati predefinito per gli array numerici in MATLAB è double
. double
è una rappresentazione in virgola mobile di numeri e questo formato richiede 8 byte (o 64 bit) per valore. In alcuni casi, dove ad esempio si tratta solo di numeri interi o quando l'instabilità numerica non è un problema imminente, una tale profondità di bit potrebbe non essere richiesta. Per questo motivo, si consiglia di considerare i vantaggi della precisione single
(o di altri tipi appropriati):
- Tempi di esecuzione più rapidi (particolarmente evidenti sulle GPU).
- Metà del consumo di memoria: può riuscire dove il
double
fallisce a causa di un errore di memoria insufficiente; più compatto quando si archiviano come file.
La conversione di una variabile da qualsiasi tipo di dati supportato a single
viene eseguita utilizzando:
sing_var = single(var);
Alcune funzioni di uso comune (come: zeros
, eye
, ones
, etc. ) che di uscita double
valori di default, permettono specificando il tipo / classe dell'uscita.
Conversione di variabili in uno script in una precisione / tipo / classe non predefinita:
A partire da luglio 2016, non esiste alcun modo documentato per modificare il tipo di dati MATLAB predefinito da double
.
In MATLAB, le nuove variabili di solito imitano i tipi di dati delle variabili utilizzate durante la loro creazione. Per illustrare questo, considera il seguente esempio:
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
Pertanto, può sembrare sufficiente eseguire il cast / convertire più variabili iniziali per far sì che il cambiamento permei attraverso il codice, tuttavia ciò è scoraggiato (vedere Caveat & Pitfalls di seguito).
Avvertenze e insidie:
Le conversioni ripetute sono scoraggiate a causa dell'introduzione del rumore numerico (quando si esegue il cast da
single
adouble
) o della perdita di informazioni (quando si esegue il cast dadouble
asingle
o tra determinati tipi di interi ), ad esempio:double(single(1.2)) == double(1.2) ans = 0
Questo può essere in qualche modo mitigato usando
typecast
. Vedi anche Sii consapevole dell'imprecisione di virgola mobile .Affidarsi esclusivamente alla tipizzazione implicita dei dati (cioè quale MATLAB indovina il tipo di output di un calcolo dovrebbe essere) è scoraggiato a causa di diversi effetti indesiderati che potrebbero sorgere:
Perdita di informazioni : quando è previsto un
double
risultato, ma una combinazione incurante di operandisingle
edouble
producesingle
precisionesingle
.Consumo di memoria inaspettatamente alto : quando è previsto un
single
risultato ma un calcolo disattento genera unadouble
uscita.Overhead non necessario quando si lavora con GPU : quando si mischiano i tipi
gpuArray
(cioè le variabili memorizzate in VRAM) con variabili nongpuArray
(cioè quelle normalmente memorizzate nella RAM) i dati dovranno essere trasferiti in un modo o nell'altro prima che il calcolo possa essere eseguito. Questa operazione richiede tempo e può essere molto evidente nei calcoli ripetitivi.Errori durante la miscelazione di tipi in virgola mobile con tipi interi : funzioni come
mtimes
(*
) non sono definite per gli input misti di tipi interi e in virgola mobile e si verificheranno errori. Funzioni cometimes
(.*
) Non sono affatto definite per gli input di tipo intero e si ripeteranno di nuovo.>> 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.
Per una migliore leggibilità del codice e una riduzione del rischio di tipi indesiderati, si consiglia un approccio difensivo, in cui le variabili vengono espresse espressamente per il tipo desiderato.
Guarda anche:
- Documentazione MATLAB: numeri a virgola mobile .
- Articolo tecnico di Mathworks: best practice per la conversione del codice MATLAB in punto fisso .
riorganizzare un array ND può migliorare le prestazioni generali
In alcuni casi è necessario applicare le funzioni a un set di ND-array. Diamo un'occhiata a questo semplice esempio.
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
Entrambe le matrici sono 3D, diciamo che dobbiamo calcolare quanto segue:
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
Ora sostituiamo il ciclo precedente per quanto segue:
result= zeros(2,2);
....
temp = abs(Aprime - Bprime);
for k = 1:2
result(i,j) = result(i,j) + temp(k, i+2*(j-1));
...
Abbiamo riorganizzato i dati in modo da poter sfruttare la memoria cache. La permutazione e la rimodellazione possono essere costose, ma quando si lavora con array ND di grandi dimensioni, il costo computazionale relativo a queste operazioni è molto più basso rispetto al lavoro con gli array non organizzati.
L'importanza della preallocazione
Gli array in MATLAB sono conservati come blocchi continui in memoria, allocati e rilasciati automaticamente da MATLAB. MATLAB nasconde le operazioni di gestione della memoria come il ridimensionamento di una matrice dietro la sintassi facile da usare:
a = 1:4
a =
1 2 3 4
a(5) = 10 % or alternatively a = [a, 10]
a =
1 2 3 4 10
È importante capire che quanto sopra non è un'operazione banale, a(5) = 10
causerà MATLAB per allocare un nuovo blocco di memoria di dimensione 5, copiare i primi 4 numeri e impostare il 5'th a 10. Questa è un'operazione O(numel(a))
e non O(1)
.
Considera quanto segue:
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
è riallocato n
volte in questo ciclo (escludendo alcune ottimizzazioni intraprese da MATLAB)! Nota che MATLAB ci dà un avvertimento:
"La variabile 'a' sembra cambiare dimensione ad ogni iterazione del ciclo. Prendi in considerazione la preallocazione per la velocità."
Cosa succede quando preassegniamo?
a=zeros(1,n);
tic
for i = 2:n
a(i) = sqrt(a(i-1)) + i;
end
toc
Elapsed time is 0.410531 seconds.
Possiamo vedere che il runtime è ridotto di un ordine di grandezza.
Metodi per la preallocazione:
MATLAB offre varie funzioni per l'assegnazione di vettori e matrici, a seconda delle esigenze specifiche dell'utente. Questi includono: zeros
, ones
, nan
, eye
, true
ecc.
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
Un tipo di dati può anche essere specificato:
a = zeros(2, 1, 'uint8'); % allocates an array of type uint8
È anche facile clonare le dimensioni di un array esistente:
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
E clona il tipo:
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
nota che "come" clona anche la complessità e la scarsità .
La preallocazione viene raggiunta implicitamente utilizzando qualsiasi funzione che restituisce un array della dimensione finale richiesta, come rand
, gallery
, kron
, bsxfun
, colon
e molti altri. Ad esempio, un modo comune per allocare vettori con elementi che variano linearmente è usando l'operatore colon (con la variante 1 o 3-operando 1 ):
a = 1:3
a =
1 2 3
a = 2:-3:-4
a =
2 -1 -4
Gli array di celle possono essere allocati usando la funzione cell()
allo stesso modo degli zeros()
.
a = cell(2,3)
a =
[] [] []
[] [] []
Nota che gli array di celle funzionano tenendo puntatori alle posizioni in memoria del contenuto delle celle. Quindi tutti i suggerimenti per la preallazione si applicano anche agli elementi del singolo array di celle.
Ulteriori letture:
- Documentazione ufficiale di MATLAB su " Preallocare memoria ".
- Documentazione ufficiale di MATLAB su " Come MATLAB assegna memoria ".
- Prestazioni di preallocazione su matlab non documentato .
- Comprendere la preallocazione delle matrici su Loren sull'arte di MATLAB