MATLAB OOP yavaş mı yoksa yanlış bir şey mi yapıyorum?


144

Birlikte deniyorum MATLAB OOP bir benim C ++ 'ın Kaydedici sınıfları taklit başlar başlamaz, ve bunu gibi şeyler yapabilmek için çok iyi olurdu düşünerek bir dize sınıfındaki tüm dize yardımcı işlevlerini koyuyorum a + b, a == b, a.find( b )yerine strcat( a b ), strcmp( a, b ), öğesinin ilk öğesini al strfind( a, b )vb.

Sorun: yavaşlama

Yukarıdaki şeyleri kullanmaya başladım ve hemen ciddi bir yavaşlama fark ettim . Yanlış mı yapıyorum?

Test durumum

İşte dize için yaptığım basit bir test, temelde sadece bir dize eklemek ve ekli parçayı tekrar kaldırmak:

Not: Gerçek kodda böyle bir String sınıfı yazmayın! Matlab'ın artık yerel bir stringdizi türü var ve bunun yerine bunu kullanmalısınız.

classdef String < handle
  ....
  properties
    stringobj = '';
  end
  function o = plus( o, b )
    o.stringobj = [ o.stringobj b ];
  end
  function n = Length( o )
    n = length( o.stringobj );
  end
  function o = SetLength( o, n )
    o.stringobj = o.stringobj( 1 : n );
  end
end

function atest( a, b ) %plain functions
  n = length( a );
  a = [ a b ];
  a = a( 1 : n );

function btest( a, b ) %OOP
  n = a.Length();
  a = a + b;
  a.SetLength( n );

function RunProfilerLoop( nLoop, fun, varargin )
  profile on;
  for i = 1 : nLoop
    fun( varargin{ : } );
  end
  profile off;
  profile report;

a = 'test';
aString = String( 'test' );
RunProfilerLoop( 1000, @(x,y)atest(x,y), a, 'appendme' );
RunProfilerLoop( 1000, @(x,y)btest(x,y), aString, 'appendme' );

Sonuçlar

1000 yineleme için saniye cinsinden toplam süre:

btest 0.550 (String.SetLength 0.138, String.plus 0.065, String.Length 0.057 ile)

en az 0.015

Logger sistemi için sonuçlar aynı şekilde: 1000 frpintf( 1, 'test\n' )sınıfı için 0.1 saniye , String sınıfını dahili olarak kullanırken sistemime 1000 çağrı için 7 (!) Saniye (Tamam, içinde çok daha fazla mantık var, ancak C ++ ile karşılaştırmak için: kullandığı zaman sistemin genel std::string( "blah" )ve std::coutova vs çıkış tarafında std::cout << "blah"1 milisaniye üzerindedir.)

Sınıf / paket fonksiyonlarına bakarken yük mü?

MATLAB yorumlandığından, çalışma zamanında bir işlevin / nesnenin tanımına bakmalıdır. Bu yüzden, yoldaki fonksiyonlara karşı sınıf veya paket fonksiyonuna bakmak için çok daha fazla yükün olduğunu merak ediyordum. Bunu test etmeye çalıştım ve sadece garipleşiyor. Sınıfların / nesnelerin etkisini ortadan kaldırmak için, yoldaki bir işlevi çağırmakla paketteki bir işlevi karşılaştırdım:

function n = atest( x, y )
  n = ctest( x, y ); % ctest is in matlab path

function n = btest( x, y )
  n = util.ctest( x, y ); % ctest is in +util directory, parent directory is in path

Sonuçlar, yukarıdaki gibi toplandı:

testte en az 0.004 saniye, 0.001 saniye

0.060 saniye, 0.014 saniye util.ctest

Peki, tüm bu ek yük sadece MATLAB'ın OOP uygulaması için tanımları aramak için zaman harcamasından kaynaklanırken, bu ek yük doğrudan yoldaki fonksiyonlar için mevcut değil mi?


5
Bu soru için teşekkürler! Matlab yığınının performansı (OOP / kapanışları) yıllardır beni rahatsız etti, bkz. Stackoverflow.com/questions/1446281/matlabs-garbage-collector . MatlabDoug / Loren / MikeKatz'nin yayınınıza ne yanıt vereceğini gerçekten merak ediyorum.
Mikhail

1
^ bu ilginç bir okumaydı.
stijn

1
@ MatlabDoug: belki meslektaşın Mike Karr OP'ye yorum yapabilir mi?
Mikhail

4
Okuyucular ayrıca en son R2012a sürümünde OOP performansını tartışan bu son blog yazısını (Dave Foti tarafından) kontrol etmelidir: Nesneye Dayalı MATLAB Kodunda Performansı Göz önünde bulundurma
Amro

1
Alt öğelerin yöntemlerinin çağrısının döngüden çıkarıldığı kod yapısına duyarlılığın basit bir örneği. for i = 1:this.get_n_quantities() if(strcmp(id,this.get_quantity_rlz(i).get_id())) ix = i; end end2.2 saniye nq = this.get_n_quantities(); a = this.get_quantity_realizations(); for i = 1:nq c = a{i}; if(strcmp(id,c.get_id())) ix = i; end endalır, 0.01 alır, mag iki sipariş
Jose Ospina

Yanıtlar:


223

Bir süredir OO MATLAB ile çalışıyorum ve benzer performans sorunlarına baktım.

Kısa cevap: evet, MATLAB'ın OOP'si biraz yavaş. Ana akım OO dillerinden daha yüksek, önemli bir yöntem çağrısı vardır ve bu konuda yapabileceğiniz çok şey yoktur. Bunun nedeni, deyimsel MATLAB'ın yöntem çağrılarının sayısını azaltmak için "vectorized" kodunu kullanması ve çağrı başına ek yükün yüksek bir öncelik olmaması olabilir.

Çeşitli işlevler ve yöntemler olarak "hiçbir şey" nop "işlevleri yazarak performansı karşılaştırdım. İşte bazı tipik sonuçlar.

>> call_nops
Bilgisayar: PCWIN Sürüm: 2009b
Her işlevin / yöntemin 100000 kez çağrılması
nop () işlevi: arama başına 0,02261 sn 0,23 usec
nop1-5 () fonksiyonları: arama başına 0,02182 sn 0,22 usec
nop () alt fonksiyonu: arama başına 0,02244 sn 0,22 usec
@ () [] anonim işlev: arama başına 0,08461 sn 0,85 usec
nop (obj) yöntemi: arama başına 0,24664 sn 2,47 usec
nop1-5 (obj) yöntemleri: arama başına 0,23469 sn 2,35 usec
nop () özel işlevi: arama başına 0,02197 sn 0,22 usec
classdef nop (obj): Çağrı başına 0.90547 sn 9.05 usec
classdef obj.nop (): arama başına 1.75522 sn 17,55 usec
classdef private_nop (obj): arama başına 8,4738 sn 8,47 usec
classdef nop (obj) (m-dosyası): arama başına 0.90560 sn 9.06 usec
classdef class.staticnop (): arama başına 1.16361 sn 11,64 usec
Java nop (): arama başına 2.43035 sn 24.30 usec
Java static_nop (): arama başına 0,8768 sn 8,77 usec
Java'dan Java nop (): Çağrı başına 0.00014 sn 0.00 usec
MEX mexnop (): arama başına 0,11409 sn 1,14 usec
C nop (): Arama başına 0,00001 sn 0,00 usec

R2008a ile R2009b arasındaki benzer sonuçlar. Bu, 32-bit MATLAB çalıştıran Windows XP x64'te bulunmaktadır.

"Java nop ()", bir M kodu döngüsünün içinden çağrılan bir hiçbir şey yapmayan Java yöntemidir ve her çağrıda MATLAB'dan Java'ya dağıtım yükünü içerir. "Java'dan Java nop ()", Java için () döngüsünde çağrılanla aynıdır ve bu sınır cezasına neden olmaz. Java ve C zamanlamalarını bir tuz tanesi ile alın; akıllı bir derleyici çağrıları tamamen optimize edebilir.

Paket kapsam belirleme mekanizması yenidir, classdef sınıflarıyla hemen hemen aynı zamanda tanıtılır. Davranışı ilişkili olabilir.

Birkaç geçici sonuç:

  • Yöntemler işlevlerden daha yavaştır.
  • Yeni stil (classdef) yöntemleri eski stil yöntemlerinden daha yavaştır.
  • Yeni obj.nop()sözdizimi, nop(obj)classdef nesnesindeki aynı yöntem için bile sözdiziminden daha yavaştır . Java nesneleri için aynıdır (gösterilmemiştir). Hızlı gitmek istiyorsanız arayın nop(obj).
  • Windows'ta 64-bit MATLAB'da yöntem çağrısı yükü daha yüksektir (yaklaşık 2x). (Gösterilmemiş.)
  • MATLAB yöntemi gönderimi diğer dillerden daha yavaştır.

Bunun neden böyle olduğunu söylemek benim açımdan spekülasyon olurdu. MATLAB motorunun OO dahili parçaları herkese açık değil. Kendi başına yorumlanmış ve derlenmiş bir sorun değildir - MATLAB bir JIT'e sahiptir - ancak MATLAB'ın daha gevşek yazması ve sözdizimi çalışma zamanında daha fazla çalışma anlamına gelebilir. (Örneğin, "f (x)" ifadesinin bir diziye bir işlev çağrısı veya dizin olup olmadığını tek başına sözdiziminden anlatamazsınız; çalışma zamanında çalışma alanının durumuna bağlıdır.) Bunun nedeni MATLAB'ın sınıf tanımlarının bağlı olması olabilir. diğer birçok dilde olmayacak şekilde dosya sistemi durumuna.

Peki ne yapmalı?

Bunun deyimsel bir MATLAB yaklaşımı, sınıf tanımlarınızı bir nesne örneği bir diziyi tamamlayacak şekilde yapılandırarak kodunuzu "vektörleştirmektir"; yani, alanlarının her biri paralel dizilere sahiptir (MATLAB belgelerinde "düzlemsel" organizasyon olarak adlandırılır). Her biri skaler değerleri tutan alanları olan bir nesne dizisine sahip olmak yerine, kendileri dizili olan nesneleri tanımlar ve yöntemlerin dizileri girdi olarak almasını sağlar ve alanlar ve girdiler üzerinde vectorized çağrılar yapar. Bu, umarım gönderme yükünün bir darboğaz olmaması için yeterli olan yöntem çağrısı sayısını azaltır.

MATLAB'da bir C ++ veya Java sınıfını taklit etmek muhtemelen uygun olmayacaktır. Java / C ++ sınıfları genellikle nesnelerin olabildiğince küçük (yani birçok farklı sınıf) yapı taşları olacak ve dizilerde, toplama nesnelerinde vb. Oluşturulacak ve döngülerle yinelenecek şekilde oluşturulur. Hızlı MATLAB sınıfları yapmak için bu yaklaşımı ters yüz edin. Alanları diziler olan daha büyük sınıflara sahip olun ve bu dizilerde vectorized yöntemler çağırın.

Mesele, kodunuzu dilin güçlü yönlerine (dizi işleme, vektörize matematik) oynamak ve zayıf noktaları önlemek için düzenlemektir.

EDIT: Orijinal gönderiden bu yana, R2010b ve R2011a çıktı. Genel resim aynıdır, MCOS çağrıları biraz daha hızlı olur ve Java ve eski stil yöntemi çağrıları yavaşlar .

DÜZENLEME: Burada "Yol hassasiyeti" ile ilgili bazı notlar vardı, burada ek işlev çağrısı zamanlamaları tablosu, fonksiyon zamanlarının Matlab yolunun nasıl yapılandırıldığından etkilendi, ancak bu benim özel ağ kurulumumda bir sapma gibi görünüyor zaman. Yukarıdaki grafik, testlerimin zaman içindeki baskınlığının tipik zamanlarını yansıtır.

Güncelleme: R2011b

EDIT (2/13/2012): R2011b çıktı ve performans resmi bunu güncelleyecek kadar değişti.

Arch: PCWIN Sürümü: 2011b 
Makine: R2011b, Windows XP, 8x Core i7-2600 @ 3.40GHz, 3 GB RAM, NVIDIA NVS 300
Her işlemi 100000 kez yapmak
çağrı başına toplam µsec stili
nop () işlevi: 0.01578 0.16
nop (), 10x döngü açma: 0,01477 0,15
nop (), 100x döngü açma: 0,01518 0,15
nop () alt fonksiyonu: 0.01559 0.16
@ () [] anonim işlev: 0.06400 0.64
nop (obj) yöntemi: 0.28482 2.85
nop () özel işlevi: 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
classdef sabiti: 1.51890 15.19
classdef özelliği: 0.12992 1.30
alıcı ile classdef özelliği: 1.39912 13.99
+ pkg.nop () işlevi: 0.87345 8.73
+ pkg.nop () içeriden + 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'dan Java obj.nop (): 0.00010 0.00
MEX mexnop (): 0,08709 0,87
C nop (): 0.00001 0.00
j () (yerleşik): 0.00251 0.03

Bence bunun sonucu şu:

  • MCOS / classdef yöntemleri daha hızlıdır. foo(obj)Sözdizimini kullandığınız sürece maliyet artık eski stil sınıflarıyla aynı düzeydedir. Bu nedenle, yöntem hızı artık çoğu durumda eski stil sınıflarına bağlı kalmak için bir neden değildir. (Kudos, MathWorks!)
  • İşlevleri ad alanlarına yerleştirmek onları yavaşlatır. (R2011b'de yeni değil, sadece testimde yeni.)

Güncelleme: R2014a

Kıyaslama kodunu yeniden oluşturdum ve R2014a'da çalıştırdım.

PCWIN64 üzerinde Matlab R2014a  
Matlab 8.3.0.532 (R2014a) / PCWIN64 Windows 7 6.1 üzerinde Java 1.7.0_11 (eilonwy-win7) 
Makine: Core i7-3615QM CPU @ 2.30GHz, 4 GB RAM (VMware Sanal Platformu)
Sular = 100000 

Çalışma Süresi (µsn)  
nop () işlevi: 0.14 
nop () alt işlevi: 0.14 
@ () [] anonim işlev: 0.69 
nop (obj) yöntemi: 3.28 
nop () özel fcn on @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 
classdef sabiti: 4.18 
classdef özelliği: 1.18 
alıcı ile classdef özelliği: 19.26 
+ pkg.nop () işlevi: 4.03 
+ pkg.nop () içeriden + 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'dan Java obj.nop (): 0.01 
MEX mexnop (): 0,91 
yerleşik j (): 0,02 
struct s.foo sahaya erişim: 0.14 
isempty (kalıcı): 0.00 

Güncelleme: R2015b: Nesneler hızlandı!

@Shaked tarafından sağlanan R2015b sonuçları. Bu büyük bir değişiklik: OOP önemli ölçüde daha hızlı ve şimdi obj.method()sözdizimi method(obj)eski OOP nesnelerinden daha hızlı ve çok daha hızlı.

PCWIN64 üzerinde Matlab R2015b  
Matlab 8.6.0.267246 (R2015b) / PCWIN64 Windows 8 6.2 üzerinde Java 1.7.0_60 (nanit sallanmış) 
Makine: Core i7-4720HQ CPU @ 2.60GHz, 16 GB RAM (20378)
Sular = 100000 

Çalışma Süresi (µsn)  
nop () işlevi: 0.04 
nop () alt işlevi: 0,08 
@ () [] anonim işlev: 1.83 
nop (obj) yöntemi: 3.15 
nop () özel fcn on @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 
classdef sabiti: 0.25 
classdef özelliği: 0.25 
alıcı ile classdef özelliği: 0.64 
+ pkg.nop () işlevi: 0.04 
+ pkg.nop () içeriden + 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'dan Java obj.nop (): 0.01 
MEX mexnop (): 0,33 
yerleşik j (): 0.15 
struct s.foo sahaya erişim: 0.25 
isempty (kalıcı): 0.13 

Güncelleme: R2018a

İşte R2018a sonuçları. Yeni yürütme motoru R2015b'de piyasaya sürüldüğünde gördüğümüz büyük sıçrama değil, ancak yine de yıldan yıla kayda değer bir gelişme. Özellikle, anonim işlev tutamaçları çok daha hızlı hale geldi.

MACI64 üzerinde Matlab R2018a  
MacI64 Mac OS X 10.13.5 (eilonwy) üzerinde Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 
Makine: Core i7-3615QM CPU @ 2.30GHz, 16 GB RAM 
Sular = 100000 

Çalışma Süresi (µsn)  
nop () işlevi: 0.03 
nop () alt işlevi: 0,04 
@ () [] anonim işlev: 0.16 
classdef nop (obj): 0,16 
classdef obj.nop (): 0.17 
classdef pivate_nop (obj): 0.16 
classdef class.static_nop (): 0.03 
classdef sabiti: 0.16 
classdef özelliği: 0.13 
alıcı ile classdef özelliği: 0.39 
+ pkg.nop () işlevi: 0,02 
+ pkg.nop () içeriden + 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'dan Java obj.nop (): 0.03 
MEX mexnop (): 3,54 
yerleşik j (): 0.10 
struct s.foo sahaya erişim: 0.16 
isempty (kalıcı): 0,07 

Güncelleme: R2018b ve R2019a: Değişiklik yok

Önemli değişiklik yok. Test sonuçlarını eklemek için uğraşmıyorum.

Deneyler için Kaynak Kodu

Bu kriterler için kaynak kodunu MIT Lisansı altında yayınlanan GitHub'a yerleştirdim. https://github.com/apjanke/matlab-bench


5
@AndrewJanke R2012a ile karşılaştırmayı tekrar çalıştırabileceğinizi düşünüyor musunuz? Bu gerçekten ilginç.
Dang Khoa

7
Merhaba millet. Hala kaynak kodla ilgileniyorsanız, kodu yeniden oluşturdum ve GitHub'da açık kaynaklı hale getirdim. github.com/apjanke/matlab-bench
Andrew Janke

2
@Seeda: Bu sonuçlarda statik yöntemler "classdef class.static_nop ()" olarak listelenir. Fonksiyonlara kıyasla oldukça yavaştırlar. Sık sık çağrılmazlarsa, önemli değil.
Andrew Janke


2
Vaov! Bu sonuçlar devam ederse, tüm bu cevabı gözden geçirmem gerekebilir. Katma. Teşekkürler!
Andrew Janke

3

Tanıtıcı sınıfı, temizleme amacıyla tüm başvuruları izlemekten ek bir ek yüke sahiptir.

Tanıtıcı sınıfı kullanmadan aynı denemeyi deneyin ve sonuçlarınızın ne olduğunu görün.


1
String ile tam olarak aynı deneyi, ancak şimdi bir değer sınıfı olarak (başka bir makinede olsa da); atest: 0.009, test: o.356. Bu temelde tanıtıcı ile aynı fark, bu yüzden izleme referansları kilit cevap olduğunu düşünmüyorum. Ayrıca fonksiyonlardaki genel yükü ve paketlerdeki fonksiyonu açıklamaz.
stijn

Hangi matlab sürümünü kullanıyorsunuz?
MikeEL

1
Ben tanıtıcı ve değer sınıfları arasında bazı benzer karşılaştırmalar çalıştırın ve ikisi arasında bir performans farkı fark etmedim.
RjOllos

Artık bir fark görmüyorum.
MikeEL

Mantıklı: Matlab'da, yalnızca nesneleri işlemekle kalmayan tüm diziler referans sayılır, çünkü yazma üzerine kopyalama kullanır ve temel ham verileri paylaşırlar.
Andrew Janke

1

OO performansı, kullanılan MATLAB versiyonuna önemli ölçüde bağlıdır. Tüm sürümler hakkında yorum yapamam, ancak 2012a'nın 2010 sürümlerine göre çok daha gelişmiş olduğunu biliyorum. Hiçbir kriter ve böylece hiçbir sayı mevcut. Yalnızca tanıtıcı sınıfları kullanılarak yazılan ve 2012a altında yazılan kodum önceki sürümlerin hiçbirinde çalışmaz.


1

Aslında kodunuzla ilgili bir sorun yok ama Matlab ile ilgili bir sorun var. Bence bir tür oyun gibi görünüyor. Sınıf kodunu derlemek yükten başka bir şey değildir. Basit sınıf noktası (bir kez tanıtıcı olarak) ve diğer (bir kez değer sınıfı olarak) ile test yaptım

    classdef Pointh < handle
    properties
       X
       Y
    end  
    methods        
        function p = Pointh (x,y)
            p.X = x;
            p.Y = y;
        end        
        function  d = dist(p,p1)
            d = (p.X - p1.X)^2 + (p.Y - p1.Y)^2 ;
        end

    end
end

test burada

%handle points 
ph = Pointh(1,2);
ph1 = Pointh(2,3);

%values  points 
p = Pointh(1,2);
p1 = Pointh(2,3);

% vector points
pa1 = [1 2 ];
pa2 = [2 3 ];

%Structur points 
Ps.X = 1;
Ps.Y = 2;
ps1.X = 2;
ps1.Y = 3;

N = 1000000;

tic
for i =1:N
    ph.dist(ph1);
end
t1 = toc

tic
for i =1:N
    p.dist(p1);
end
t2 = toc

tic
for i =1:N
    norm(pa1-pa2)^2;
end
t3 = toc

tic
for i =1:N
    (Ps.X-ps1.X)^2+(Ps.Y-ps1.Y)^2;
end
t4 = toc

Sonuçlar t1 =

% 12.0212 Kulp

t2 =

% 12.0042 değer

t3 =

0.5489  % vector

t4 =

0.0707 % structure 

Bu nedenle verimli performans için OOP kullanmaktan kaçının, yapı değişkenleri gruplamak için iyi bir seçimdir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.