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

    Pulsante nella striscia di strumento

  • 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

Uscita del profiler

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 :

Tempi interni di "spy"


È anche possibile profilare il consumo di memoria eseguendo il profile('-memory') prima di eseguire il Profiler.

inserisci la descrizione dell'immagine qui

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:

  1. Le conversioni ripetute sono scoraggiate a causa dell'introduzione del rumore numerico (quando si esegue il cast da single a double ) o della perdita di informazioni (quando si esegue il cast da double a single 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 .

  2. 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 operandi single e double produce single precisione single .

    • Consumo di memoria inaspettatamente alto : quando è previsto un single risultato ma un calcolo disattento genera una double uscita.

    • Overhead non necessario quando si lavora con GPU : quando si mischiano i tipi gpuArray (cioè le variabili memorizzate in VRAM) con variabili non gpuArray (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 come times ( .* ) 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:

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:



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow