2013-08-17 14 views
5

Quando ho iniziato a lavorare con Matlab qualche tempo fa nell'università, il mio supervisore mi uccideva se vedeva un ciclo non necessario (chiedeva di scambiarlo per kron o qualsiasi tipo di manipolazione degli indici possibile). Più tardi, stavo cercando di evitare il più possibile ogni ciclo su MATLAB, cercando i modi di codifica matlab più scuri per fare magia nera invece di un semplice ciclo.Cellfun contro Simple Matlab Loop performance

E un giorno ho scoperto il cellfun, che ha reso la magia nera del tutto più semplice, ho potuto cambiare molte cicli di lavoro con le cellule e cellfun combo, ma un giorno ho visto uno post about cellfun che mi ha fatto domanda se la mia conoscenza MATLAB ereditato era vero , ovvero: che i loop matlab sarebbero sempre più lenti di una funzione compilata incorporata, che era una cosa di cui avevo tanta fiducia. L'ho provato in una delle mie implementazioni e infatti il ​​ciclo per il ciclo sarebbe stato più veloce! Ero tipo, OMG, tutti quei giorni facendo codice oscuro sprecato per niente hahaha. Da quel giorno, ho smesso di lavorare così duramente per cercare di ottimizzare il codice matlab, normalmente dipende da ogni caso e così via.

Oggi I saw this answer, che ha ricordato il mio impegno per evitare il maggior numero possibile di loop matlab (non so se è stato l'autore ad evitare per le prestazioni, ma comunque ha ricordato tutto questo matlab performance del ciclo). E una domanda mi è venuta in mente: Cellfun è migliore di quello dei loop? Quando sarebbe vero?

+1

Per quanto riguarda la mia risposta che hai menzionato: sì, cerco di evitare cicli 'for', come fai tu (o facevi), e la mia motivazione è la performance. Penso (o era solito pensare?) È vero che i cicli "for" sono di solito più lenti e dovrebbero essere evitati. –

+0

Apparentemente 'cellfun' è più lento tranne che in casi speciali. Vedere http://www.mathworks.com/matlabcentral/answers/42335 e http: //www.mathworks.com/matlabcentral/newsreader/view_thread/301894 –

+0

@LuisMendo Sì, esattamente! Ma sfortunatamente 'cellfun' con le funzioni anonime gestite sono di solito più lente dei cicli' for' ... questo mi renderebbe così annoiato dallo sforzo per nulla. Grazie per i riferimenti. – Werner

risposta

8

Se le prestazioni sono un fattore importante che si dovrebbe evitare di utilizzare le cellule, loop o cellfun/arrayfun. In genere è molto più rapido utilizzare un'operazione vettoriale (supponendo che sia possibile).

Il codice seguente si espande nell'esempio di esempio di Werner con operazioni di loop e array di array standard.

I risultati sono:

  • cellulare Loop Tempo - 0,1679
  • Cellfun Tempo - 2,9973
  • Loop Array Tempo - 0,0465
  • Array Tempo - 0,0019

Codice:

nTimes = 1000; 
nValues = 1000; 
myCell = repmat({0},1,nValues); 
output = zeros(1,nValues); 

% Basic operation 
tic; 
for k=1:nTimes 
    for m=1:nValues 
    output(m) = myCell{m} + 1; 
    end 
end 
cell_loop_timeAdd=toc;  
fprintf(1,'Cell Loop Time %0.4f\n', cell_loop_timeAdd); 

tic;   
for k=1:nTimes 
    output = cellfun(@(in) in+1,myCell); 
end 
cellfun_timeAdd=toc; 
fprintf(1,'Cellfun Time %0.4f\n', cellfun_timeAdd); 


myData = repmat(0,1,nValues); 
tic; 
for k=1:nTimes 
    for m=1:nValues 
    output(m) = myData(m) + 1; 
    end 
end 
loop_timeAdd=toc; 
fprintf(1,'Loop Array Time %0.4f\n', loop_timeAdd); 

tic; 
for k=1:nTimes 
    output = myData + 1; 
end 
array_timeAdd=toc; 
fprintf(1,'Array Time %0.4f\n', array_timeAdd); 
+0

Grande, bel punto e contributo. Un solo dettaglio: il cellfun tic toc contiene anche la funzione fprintf. Questo non influenzerà i risultati, ma comunque ... – Werner

4

Aggiungerò una risposta con i risultati che ho provato personalmente, ma sarei lieto se le persone contribuissero con le loro conoscenze, questo è solo un semplice test che ho fatto.

Ho testato le seguenti condizioni con celle di 1000 e 1000 loop (risultati sul tempo totale e probabilmente dovrei eseguire più di 1000 volte, perché ho un po 'di fluttuazione sui risultati, ma comunque , questo non è un articolo scientifico):

  • operazioni di base (somma)
    • semplice ciclo for: 0,2663 s
    • cellfun: 9,4612 s
  • String Operation (strcmp)
    • semplice ciclo for: 1,3124 s
    • cellfun: 11,8099 s
  • incorporato (IsEmpty)
  • non uniforme (regexp)
    • semplice ciclo: 24,2157 s
    • cellfun (ingresso stringa): 44.0424 s

Così, sembra che cellfun con chiamate di funzione anonimi sono più lento di un semplice ciclo for, ma se si utilizza un metodo di MATLAB incorporato, farlo con cellfun e utilizzarlo con la citazione di stringa . Questo non è necessariamente vero per tutti i casi, ma almeno per le funzioni testate.

Il codice di prova implementato (io sono ben lungi dall'essere uno specialista di ottimizzazione, ecco il codice nel caso in cui ho fatto qualcosa di sbagliato):

function ... 
    [loop_timeAdd,cellfun_timeAdd,... 
    loop_timeStr,cellfun_timeStr,... 
    loop_timeBuiltIn,cellfun_timeBuiltInStrInput,... 
    cellfun_timeBuiltyInFcnHandle,... 
    loop_timeNonUniform,cellfun_timeNonUniform] ... 
    = test_cellfun(nTimes,nCells) 

myCell = repmat({0},1,nCells); 
output = zeros(1,nCells); 

% Basic operation 
tic; 
for k=1:nTimes 
    for m=1:nCells 
    output(m) = myCell{m} + 1; 
    end 
end 
loop_timeAdd=toc; 

tic; 
for k=1:nTimes 
    output = cellfun(@(in) in+1,myCell); 
end 
cellfun_timeAdd=toc; 

% String operation 
myCell = repmat({'matchStr'},1,nCells); % Add str that matches 
myCell(1:2:end) = {'dontMatchStr'}; % Add another str that doesnt match 
output = zeros(1,nCells); 

tic; 
for k=1:nTimes 
    for m=1:nCells 
    output(m) = strcmp(myCell{m},'matchStr'); 
    end 
end 
loop_timeStr=toc; 

tic; 
for k=1:nTimes 
    output = cellfun(@(in) strcmp(in,'matchStr'),myCell); 
end 
cellfun_timeStr=toc; 

% Builtin function (isempty) 
myCell = cell(1,nCells); % Empty 
myCell(1:2:end) = {0}; % not empty 
output = zeros(1,nCells); 

tic; 
for k=1:nTimes 
    for m=1:nCells 
    output(m) = isempty(myCell{m}); 
    end 
end 
loop_timeBuiltIn=toc; 

tic; 
for k=1:nTimes 
    output = cellfun(@isempty,myCell); 
end 
cellfun_timeBuiltyInFcnHandle=toc; 

tic; 
for k=1:nTimes 
    output = cellfun('isempty',myCell); 
end 
cellfun_timeBuiltInStrInput=toc; 

% Builtin function (isempty) 
myCell = repmat({'John'},1,nCells); 
myCell(1:2:end) = {'Doe'}; 
output = cell(1,nCells); 

tic; 
for k=1:nTimes 
    for m=1:nCells 
    output{m} = regexp(myCell{m},'John','match'); 
    end 
end 
loop_timeNonUniform=toc; 

tic; 
for k=1:nTimes 
    output = cellfun(@(in) regexp(in,'John','match'),myCell,... 
    'UniformOutput',false); 
end 
cellfun_timeNonUniform=toc; 
0
clear all; 
ntimes = 1000; 

r = 100; 
c = 100; 
d = 100; 
A = rand(r, c, d); 
B = rand(r, c, d); 

tic 
for i = 1:ntimes 
    for j = 1 : d 
     result = A(:, :, j) * B(:, :, j); 
    end 
end 
toc 

A_cell = num2cell(A, [1 2]); 
B_cell = num2cell(B, [1 2]); 
tic 
for i = 1 : ntimes 
    result2 = cellfun(@(x, y) x*y, A_cell, B_cell, 'uni', 0); 
end 
toc 

Prova questo forse. Provalo per qualche altra volta. In media, cellfun è più veloce del doppio ciclo.

+0

Il ciclo esterno che hai commentato con "cosa sta facendo?" si suppone che ripeta il ciclo misurato n volte, in modo da ridurre il bias del risultato e tendenzialmente al tempo medio di esecuzione. Se lo rimuovi, come hai fatto, stai confrontando 1 tempo di esecuzione con altri cicli che sono ripetuti n volte. – Werner

+0

@Werner Ho aggiornato il codice. Sto discutendo perché sto facendo cose simili nel mio progetto. Ho testato la versione a doppio loop nel mio progetto e la versione cellfun, che cellfun è molto più veloce del doppio ciclo .. Non ho idea del perché. Ma questo esempio può mostrare che cellfun è più veloce. – user3390652

+0

Il confronto sembra non essere corretto, la cella ha dimensione 1x2 e il rand ha 100x100x100. Inoltre, ho usato un po 'di tempo per matlab, ma sembra che tu possa cambiare 'per j = 1: d; result = A() ... 'da un' bsxfun' che probabilmente sarebbe più veloce. – Werner

0

Ecco come vorrei in genere decidere su quale soluzione da utilizzare:

  1. Posso farlo con operazioni di matrice semplici? Fallo, sarà il più veloce che puoi ottenere e in genere più leggibile.
  2. Sto facendo qualcosa di semplice? Vai a 3, altrimenti vai a 4.
  3. Posso farlo con bsxfun? Fallo, sarà molto veloce e, si spera, leggibile.
  4. altrimenti utilizzare un semplice ciclo for

Come si può notare, ho praticamente mai utilizzare cellfun per le prestazioni. Ciò è dovuto alla seguente logica:

  1. Cellfun non esegue molto più rapidamente di un ciclo in genere ed è principalmente utile per gestire le celle.
  2. Se le prestazioni sono importanti, probabilmente si desidera evitare tutte le celle e pertanto non sarà necessario cellfun.