Ho lavorato con OO MATLAB per un po 'e ho finito per esaminare problemi di prestazioni simili.
La risposta breve è: sì, OOP di MATLAB è un po 'lento. Esiste un sostanziale overhead di chiamata del metodo, superiore alle lingue OO tradizionali e non c'è molto che puoi fare al riguardo. Parte del motivo potrebbe essere che MATLAB idiomatico utilizza un codice "vettorializzato" per ridurre il numero di chiamate al metodo e l'overhead per chiamata non è una priorità assoluta.
Ho confrontato le prestazioni scrivendo funzioni "nop" do-nothing come vari tipi di funzioni e metodi. Ecco alcuni risultati tipici.
>> call_nops
Computer: versione PCWIN: 2009b
Chiamando ogni funzione / metodo 100000 volte
Funzione nop (): 0,02261 sec 0,23 usec per chiamata
nop1-5 () funzioni: 0,02182 sec 0,22 usec per chiamata
Sotto funzione nop (): 0,02244 sec 0,22 usec per chiamata
@ () [] funzione anonima: 0,08461 sec 0,85 usec per chiamata
Metodo nop (obj): 0,24664 sec 2,47 usec per chiamata
nop1-5 (obj) metodi: 0,23469 sec 2,35 usec per chiamata
nop () funzione privata: 0,02197 sec 0,22 usec per chiamata
classdef nop (obj): 0.90547 sec 9.05 usec per chiamata
classdef obj.nop (): 1.75522 sec 17.55 usec per chiamata
classdef private_nop (obj): 0,84738 sec 8,47 usec per chiamata
classdef nop (obj) (m-file): 0.90560 sec 9.06 usec per chiamata
classdef class.staticnop (): 1.16361 sec 11.64 usec per chiamata
Java nop (): 2.43035 sec 24.30 usec per chiamata
Java static_nop (): 0,87682 sec 8,77 usec per chiamata
Java nop () da Java: 0,00014 sec 0,00 usec per chiamata
MEX mexnop (): 0,11409 sec 1,14 usec per chiamata
C nop (): 0,00001 sec 0,00 usec per chiamata
Risultati simili su R2008a fino a R2009b. Questo è su Windows XP x64 con MATLAB a 32 bit.
"Java nop ()" è un metodo Java do-nothing chiamato dall'interno di un ciclo M-code e include l'overhead di invio MATLAB-Java ad ogni chiamata. "Java nop () da Java" è la stessa cosa chiamata in un ciclo Java for () e non comporta questa penalità al contorno. Prendi i tempi Java e C con un granello di sale; un compilatore intelligente potrebbe ottimizzare completamente le chiamate.
Il meccanismo di scoping del pacchetto è nuovo, introdotto più o meno contemporaneamente alle classi classdef. Il suo comportamento può essere correlato.
Alcune conclusioni provvisorie:
- I metodi sono più lenti delle funzioni.
- I nuovi metodi di stile (classdef) sono più lenti dei metodi di vecchio stile.
- La nuova
obj.nop()
sintassi è più lenta della nop(obj)
sintassi, anche per lo stesso metodo su un oggetto classdef. Lo stesso vale per gli oggetti Java (non mostrati). Se vuoi andare veloce, chiama nop(obj)
.
- Il sovraccarico della chiamata del metodo è maggiore (circa 2x) in MATLAB a 64 bit su Windows. (Non mostrato.)
- L'invio del metodo MATLAB è più lento di alcune altre lingue.
Dire perché è così sarebbe solo una speculazione da parte mia. Gli interni OO del motore MATLAB non sono pubblici. Non è un problema interpretato vs compilato di per sé - MATLAB ha una JIT - ma la digitazione e la sintassi più lente di MATLAB possono significare più lavoro in fase di esecuzione. (Ad esempio, dalla sintassi non si può dire da soli se "f (x)" è una chiamata di funzione o un indice in un array; dipende dallo stato dell'area di lavoro in fase di esecuzione.) Potrebbe essere perché le definizioni di classe di MATLAB sono legate allo stato del filesystem in un modo in cui molte altre lingue non lo sono.
Quindi che si fa?
Un approccio MATLAB idiomatico a questo consiste nel "vettorializzare" il codice strutturando le definizioni delle classi in modo tale che un'istanza di oggetto avvolga un array; vale a dire, ciascuno dei suoi campi contiene matrici parallele (chiamate organizzazione "planare" nella documentazione MATLAB). Piuttosto che avere una matrice di oggetti, ognuno con campi che contengono valori scalari, definiscono oggetti che sono essi stessi matrici e fanno in modo che i metodi prendano le matrici come input ed effettuino chiamate vettorializzate sui campi e sugli input. Ciò riduce il numero di chiamate di metodo effettuate, si spera abbastanza che l'overhead di spedizione non sia un collo di bottiglia.
Imitare una classe C ++ o Java in MATLAB probabilmente non sarà ottimale. Le classi Java / C ++ sono in genere costruite in modo tale che gli oggetti siano i più piccoli blocchi predefiniti, il più specifici possibile (vale a dire molte classi diverse) e li componi in matrici, oggetti di raccolta, ecc. E li esegui ripetutamente con loop. Per rendere veloci le classi MATLAB, capovolgi questo approccio. Hanno classi più grandi i cui campi sono matrici e chiamano metodi vettorializzati su tali matrici.
Il punto è organizzare il tuo codice affinché giochi ai punti di forza del linguaggio - gestione degli array, matematica vettoriale - ed evita i punti deboli.
EDIT: Dal post originale, R2010b e R2011a sono usciti. Il quadro generale è lo stesso, con le chiamate MCOS un po 'più veloci e le chiamate con metodi Java e vecchio stile diventano più lente .
EDIT: Avevo qui alcune note sulla "sensibilità del percorso" con una tabella aggiuntiva dei tempi delle chiamate di funzione, in cui i tempi di funzione erano influenzati dalla configurazione del percorso di Matlab, ma sembra essere stata un'aberrazione della mia particolare configurazione di rete in il tempo. La tabella sopra riflette i tempi tipici della preponderanza dei miei test nel tempo.
Aggiornamento: R2011b
EDIT (13/02/2012): R2011b è uscito e l'immagine delle prestazioni è cambiata abbastanza per aggiornarla.
Arch: PCWIN Rilascio: 2011b
Macchina: R2011b, Windows XP, 8x Core i7-2600 a 3,40 GHz, 3 GB di RAM, NVIDIA NVS 300
Ogni operazione viene eseguita 100000 volte
stile totale µsec per chiamata
Funzione nop (): 0,01578 0,16
nop (), 10x loop svolgersi: 0,01477 0,15
nop (), 100x loop unroll: 0.01518 0.15
Sottofunzione nop (): 0,01559 0,16
@ () [] funzione anonima: 0.06400 0.64
Metodo nop (obj): 0,28482 2,85
nop () funzione privata: 0.01505 0.15
classdef nop (obj): 0.43323 4.33
classdef obj.nop (): 0.81087 8.11
classdef private_nop (obj): 0.32272 3.23
classdef class.staticnop (): 0.88959 8.90
costante classdef: 1.51890 15.19
proprietà classdef: 0.12992 1.30
proprietà classdef con getter: 1.39912 13.99
+ Funzione pkg.nop (): 0.87345 8.73
+ pkg.nop () dall'interno + pkg: 0.80501 8.05
Java obj.nop (): 1.86378 18.64
Java nop (obj): 0.22645 2.26
Java feval ('nop', obj): 0,52544 5,25
Java Klass.static_nop (): 0.35357 3.54
Java obj.nop () da Java: 0.00010 0.00
MEX mexnop (): 0,08709 0,87
C nop (): 0,00001 0,00
j () (incorporato): 0,00251 0,03
Penso che il risultato di questo sia che:
- I metodi MCOS / classdef sono più veloci. Il costo è ora alla pari con le classi di vecchio stile, purché si utilizzi la
foo(obj)
sintassi. Quindi la velocità del metodo non è più un motivo per restare fedeli alle classi di vecchio stile nella maggior parte dei casi. (Complimenti, MathWorks!)
- Inserire le funzioni negli spazi dei nomi le rallenta. (Non nuovo in R2011b, solo nuovo nel mio test.)
Aggiornamento: R2014a
Ho ricostruito il codice di benchmarking ed eseguito su R2014a.
Matlab R2014a su PCWIN64
Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 su PCWIN64 Windows 7 6.1 (eilonwy-win7)
Macchina: Core i7-3615QM CPU @ 2.30GHz, 4 GB RAM (VMware Virtual Platform)
nIters = 100000
Tempo operativo (µsec)
Funzione nop (): 0.14
Sottofunzione nop (): 0.14
@ () [] funzione anonima: 0.69
Metodo nop (obj): 3.28
nop () fcn privato su @class: 0.14
classdef nop (obj): 5.30
classdef obj.nop (): 10.78
classdef pivate_nop (obj): 4.88
classdef class.static_nop (): 11.81
costante classdef: 4.18
proprietà classdef: 1.18
proprietà classdef con getter: 19.26
+ Funzione pkg.nop (): 4.03
+ pkg.nop () dall'interno + pkg: 4.16
feval ('nop'): 2.31
feval (@nop): 0.22
eval ('nop'): 59.46
Java obj.nop (): 26.07
Java nop (obj): 3.72
Java feval ('nop', obj): 9.25
Java Klass.staticNop (): 10.54
Java obj.nop () da Java: 0.01
MEX mexnop (): 0.91
builtin j (): 0.02
accesso al campo struct s.foo: 0.14
isempty (persistente): 0.00
Aggiornamento: R2015b: gli oggetti sono diventati più veloci!
Ecco i risultati di R2015b, gentilmente forniti da @Shaked. Questo è un grande cambiamento: OOP è significativamente più veloce e ora la obj.method()
sintassi è più veloce method(obj)
e molto più veloce degli oggetti OOP legacy.
Matlab R2015b su PCWIN64
Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 su PCWIN64 Windows 8 6.2 (shaker di nanit)
Macchina: Core i7-4720HQ CPU @ 2.60GHz, 16 GB RAM (20378)
nIters = 100000
Tempo operativo (µsec)
Funzione nop (): 0.04
Sottofunzione nop (): 0.08
@ () [] funzione anonima: 1.83
Metodo nop (obj): 3.15
nop () fcn privato su @class: 0.04
classdef nop (obj): 0,28
classdef obj.nop (): 0.31
classdef pivate_nop (obj): 0.34
classdef class.static_nop (): 0.05
costante classdef: 0,25
proprietà classdef: 0,25
proprietà classdef con getter: 0.64
+ Funzione pkg.nop (): 0.04
+ pkg.nop () dall'interno + pkg: 0.04
feval ('nop'): 8.26
feval (@nop): 0.63
eval ('nop'): 21.22
Java obj.nop (): 14.15
Java nop (obj): 2.50
Java feval ('nop', obj): 10.30
Java Klass.staticNop (): 24.48
Java obj.nop () da Java: 0.01
MEX mexnop (): 0,33
builtin j (): 0.15
accesso al campo struct s.foo: 0.25
isempty (persistente): 0.13
Aggiornamento: R2018a
Ecco i risultati di R2018a. Non è stato il grande salto che abbiamo visto quando è stato introdotto il nuovo motore di esecuzione in R2015b, ma è comunque un miglioramento apprezzabile anno dopo anno. In particolare, gli handle di funzioni anonimi sono diventati molto più veloci.
Matlab R2018a su MACI64
Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 su MACI64 Mac OS X 10.13.5 (eilonwy)
Macchina: CPU Core i7-3615QM @ 2,30 GHz, 16 GB RAM
nIters = 100000
Tempo operativo (µsec)
Funzione nop (): 0.03
Sotto funzione nop (): 0.04
@ () [] funzione anonima: 0.16
classdef nop (obj): 0.16
classdef obj.nop (): 0.17
classdef pivate_nop (obj): 0.16
classdef class.static_nop (): 0.03
costante classdef: 0,16
proprietà classdef: 0.13
proprietà classdef con getter: 0,39
+ Funzione pkg.nop (): 0.02
+ pkg.nop () dall'interno + pkg: 0.02
feval ('nop'): 15.62
feval (@nop): 0.43
eval ('nop'): 32.08
Java obj.nop (): 28.77
Java nop (obj): 8.02
Java feval ('nop', obj): 21.85
Java Klass.staticNop (): 45.49
Java obj.nop () da Java: 0.03
MEX mexnop (): 3.54
builtin j (): 0.10
accesso al campo struct s.foo: 0.16
isempty (persistente): 0.07
Aggiornamento: R2018b e R2019a: nessuna modifica
Nessun cambiamento significativo. Non mi preoccupo di includere i risultati del test.
Codice sorgente per benchmark
Ho inserito il codice sorgente di questi benchmark su GitHub, rilasciato sotto licenza MIT. https://github.com/apjanke/matlab-bench