2009-11-04 1 views
6

Per rifattare il mio codice MATLAB, ho pensato di passare le funzioni come argomenti (quello che MATLAB chiama funzioni anonime), ispirato dalla programmazione funzionale.Rallentamento delle prestazioni utilizzando le funzioni anonime in MATLAB ... altri lo hanno notato?

Tuttavia, sembra che le prestazioni siano colpite in modo piuttosto grave. Negli esempi di seguito, confronto diversi approcci. (Il frammento di codice è avvolto in una funzione per essere in grado di utilizzare sottofunzioni)

Il risultato che ottiene è 0 secondi per diretti, quasi 0 secondi utilizzando una sottofunzione, e 5 secondi con funzioni anonime. Sto usando MATLAB 7.7 (R2007b) su OS X 10.6, su un C2D 1.8 GHz.

Qualcuno può eseguire il codice e vedere cosa ottengono? Sono particolarmente interessato alle prestazioni su Windows.

function [] = speedtest() 


clear all; close all; 

function y = foo(x) 
    y = zeros(1,length(x)); 
    for j=1:N 
     y(j) = x(j)^2; 
    end 
end 

x = linspace(-100,100,100000); 
N = length(x); 


%% direct 
t = cputime; 

y = zeros(1,N); 
for i=1:N 
    y(i) = x(i)^2; 
end 

r1 = cputime - t; 

%% using subfunction 
t = cputime; 
y = foo(x); 
r2 = cputime - t; 

%% using anon function 
fn = @(x) x^2; 

t = cputime; 

y = zeros(1,N); 
for i=1:N 
    y(i) = fn(x(i)); 
end 

r3 = cputime-t; 

[r1 r2 r3] 

end 
+0

Qual è la tua domanda esattamente ?? – Amro

+0

Mi chiedo perché stai facendo calcoli troppo complicati comunque ... puoi sostituire la necessità di un ciclo for usando le operazioni vettoriali. Ad esempio, 'y = x.^2;' quadrerà ogni elemento di 'x' e salverà il vettore risultante in' y'. – gnovice

+1

FYI: domanda simile sulle prestazioni di invio del metodo OOP: http://stackoverflow.com/questions/1693429/matlab-oop-is-it-slow-or-am-i-doing-something-wrong/1745686#1745686 –

risposta

15

stai barando con la funzione annidata. :) La funzione anonima viene chiamata all'interno di un ciclo, quindi stai misurando il costo di chiamarlo 100.000 volte. La funzione annidata viene chiamata una sola volta, quindi la sua funzione chiamata overhead è trascurabile. Per confrontare il costo della chiamata delle funzioni anonime contro quelle denominate, dovresti fare in modo che la funzione nidificata esegua lo stesso lavoro della funzione anonima e quindi la chiami da dentro un ciclo.

L'ho fatto e ho ancora ottenuto risultati simili. La funzione anonima è circa 20 volte più lenta.

Tuttavia, è possibile utilizzare ancora handle di funzione con funzioni non anonime e che non hanno lo stesso impatto sulle prestazioni delle funzioni anonime. Funziona con funzioni annidate (come con foo nel test originale) o sottofunzioni non annidate (che non agiscono da chiusure e potrebbero avere meno overhead).

function [] = speedtest() 

function y = foo(x) 
    y = x^2; 
end 

r = struct; 

... 

%% using nested function through function handle 
fn = @foo; 
y = zeros(1,N); 
t = cputime; 
for i=1:N 
    y(i) = fn(x(i)); 
end 
r.nested_handle = cputime - t; 

... 

%% using subfunction through function handle 
fn = @subfunction_foo; 
y = zeros(1,N); 
t = cputime; 
for i=1:N 
    y(i) = fn(x(i)); 
end 
r.subfunction_handle = cputime - t; 

... 

end % end function speedtest 

function y = subfunction_foo(x) 
y = x^2; 
end 

Ottengo questo su R2009b in Windows.

 
>> speedtest 
       direct: 0 
       nested: 0.0469 
     nested_handle: 0.0781 
      subfunction: 0.0313 
    subfunction_handle: 0.0313 
      anonymous: 1.2344 

Un altro modo di vedere le cose è quello di strutturare il codice in modo che sia "vettorizzati" e funziona su array, riducendo il numero di chiamate di funzione e il costo della chiamata di funzione non importa così tanto. Questo sarebbe più idiomatico di Matlab: il tipico consiglio di performance è quello di ignorare il costo delle chiamate e dei loop di funzioni perché comunque si dovrebbero fare meno chiamate su argomenti più grandi.

+0

Errore di battitura: nel tuo codice, ciò che chiami "sottofunzione" è in realtà la tua funzione annidata e ciò che chiami "funzione locale" "è in realtà la sottofunzione. – gnovice

+1

Modificato per usare la terminologia corretta come indicato da gnovice; "sottofunzione" è adesso. –

+0

Sì, hai ragione - sto imbrogliando. Non di proposito però ;-) Attualmente mi sto abituando a vettorializzare il mio codice, es. evitando per-loop e invece di fare cose come A (A <0) = 0. Ma non riesco a vedere come utilizzare funzioni anonime correla alle idiomi di Matlab - forse dovrei semplicemente evitare di utilizzarle? – Grav

1

I risultati di una macchina Windows, Matlab 2009a

>> test 

ans = 

    0 0.0156 1.1094 
1

posso confermare i vostri risultati Grav. La funzione speedtest restituisce quanto segue sul mio computer.

>> speedtest() 
ans = 
     0 0.0313 1.3906 

Come sidenote, la funzione cputime non è il metodo migliore per misurare il tempo di calcolo. Utilizzare invece le funzioni tic e toc. see link Queste funzioni forniscono una risoluzione temporale molto più alta e, utilizzando il modulo, ottengo quanto segue.

>> speedtest() 
ans = 
     0.0062 0.0162 1.3495 
1

Ho affrontato lo stesso problema di Gary, ho pensato che sarebbe stato utile verificare la risposta di Andrew su una versione più recente di Matlab (2014a) (Mac).I risultati del primo:

direct: 0.0722 
anonymous: 0.3916 
subfunction: 0.2277 

E il codice che ho usato:

function []=SpeedTest() 

fanon = @(x,y) x*x+y*y; 

iter=1000000; 
x=1:iter; 
y=1:iter; 
var1=nan(size(x)); 
var2=nan(size(x)); 
var3=nan(size(x)); 
timefor=struct('direct',nan,'anonymous',nan','subfunction',nan); 

tic; 
for i=1:iter 
    var1(i)=x(i)*x(i)+y(i)*y(i); 
end 
timefor.direct=toc; 

tic; 
for i=1:iter 
    var2(i)=fanon(x(i),y(i)); 
end 
timefor.anonymous=toc; 

tic; 
for i=1:iter 
    var3(i)=fsub(x(i),y(i)); 
end 
timefor.subfunction=toc; 

display(timefor); 
end 

function [z]=fsub(x,y) 
z=x*x+y*y; 
end