arrayfun, matlab'deki açık bir döngüden önemli ölçüde daha yavaş olabilir. Neden?


105

Aşağıdakiler için aşağıdaki basit hız testini düşünün arrayfun:

T = 4000;
N = 500;
x = randn(T, N);
Func1 = @(a) (3*a^2 + 2*a - 1);

tic
Soln1 = ones(T, N);
for t = 1:T
    for n = 1:N
        Soln1(t, n) = Func1(x(t, n));
    end
end
toc

tic
Soln2 = arrayfun(Func1, x);
toc

Makinemde (Linux Mint 12 üzerinde Matlab 2011b), bu testin çıktısı:

Elapsed time is 1.020689 seconds.
Elapsed time is 9.248388 seconds.

Ne?!? arrayfun, kuşkusuz daha temiz görünen bir çözüm olsa da, çok daha yavaştır. Burada neler oluyor?

Dahası, benzer bir test tarzı yaptım cellfunve bunun açık bir döngüden yaklaşık 3 kat daha yavaş olduğunu buldum. Yine, bu sonuç beklediğimin tam tersi.

Sorum şu: Neden arrayfunve cellfunçok daha yavaş? Ve bu göz önüne alındığında, bunları kullanmak için herhangi bir iyi sebep var mı (kodu güzel göstermek dışında)?

Not:arrayfun Paralel işleme araç kutusundaki GPU sürümünden DEĞİL, buranın standart sürümünden bahsediyorum .

DÜZENLEME: Açık Func1olmak gerekirse , yukarıda Oli'nin belirttiği gibi vektörleştirilebileceğinin farkındayım . Sadece asıl sorunun amaçları doğrultusunda basit bir hız testi sağladığı için seçtim.

DÜZENLEME: Grungetta'nın önerisini takiben testi ile yeniden yaptım feature accel off. Sonuçlar:

Elapsed time is 28.183422 seconds.
Elapsed time is 23.525251 seconds.

Başka bir deyişle, farkın büyük bir kısmının, JIT hızlandırıcının açık fordöngüyü hızlandırmaktan çok daha iyi bir iş çıkarması olduğu görülüyor arrayfun. arrayfunAslında daha fazla bilgi sağladığı için bu bana garip geliyor, yani kullanımı aramaların sırasının Func1önemli olmadığını ortaya koyuyor . Ayrıca, JIT hızlandırıcının açık veya kapalı olmasına bakılmaksızın, sistemimin yalnızca bir CPU kullandığını fark ettim ...


10
Neyse ki, "standart çözüm" açık ara en hızlısı olmaya devam ediyor: tic; 3 * x. ^ 2 + 2 * x-1; toc Geçen süre 0,030662 saniyedir.
Oli

4
@Oli Sanırım birinin bunu göstereceğini ve vektörleştirilemeyen bir işlevi kullanacağını tahmin etmeliydim :-)
Colin T Bowers

3
JIT hızlandırıcı kapatıldığında bu zamanlamanın nasıl değiştiğini görmek isterim. 'Özellik hızlanma kapalı' komutunu yürütün ve ardından testinizi yeniden çalıştırın.
grungetta

@grungetta İlginç bir öneri. Sonuçları birkaç yorumla birlikte soruya ekledim.
Colin T Bowers

Yanıtlar:


101

Kodunuzun diğer sürümlerini çalıştırarak fikri edinebilirsiniz. Döngünüzde bir işlev kullanmak yerine, hesaplamaları açıkça yazmayı düşünün

tic
Soln3 = ones(T, N);
for t = 1:T
    for n = 1:N
        Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
    end
end
toc

Bilgisayarımda hesaplama süresi:

Soln1  1.158446 seconds.
Soln2  10.392475 seconds.
Soln3  0.239023 seconds.
Oli    0.010672 seconds.

Şimdi, tamamen 'vektörleştirilmiş' çözüm açıkça en hızlısı olsa da, her x girişi için çağrılacak bir işlevi tanımlamanın çok büyük bir ek yük olduğunu görebilirsiniz. Sadece hesaplamayı açıkça yazmak bize 5 faktör hız artışı sağladı. Sanırım bu MATLABs JIT derleyicisinin satır içi işlevleri desteklemediğini gösteriyor . Orada gnovice'nin cevabına göre, anonim bir işlev yerine normal bir işlev yazmak aslında daha iyidir. Dene.

Sonraki adım - iç döngüyü kaldırın (vektörleştirin):

tic
Soln4 = ones(T, N);
for t = 1:T
    Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc

Soln4  0.053926 seconds.

Başka bir faktör 5 hızlanma: Bu ifadelerde MATLAB'da döngülerden kaçınmanız gerektiğini söyleyen bir şey var ... Yoksa gerçekten var mı? Şuna bir bak o zaman

tic
Soln5 = ones(T, N);
for n = 1:N
    Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc

Soln5   0.013875 seconds.

'Tam' vektörleştirilmiş sürüme çok daha yakın. Matlab, matrisleri sütun olarak depolar. Her zaman (mümkün olduğunda) hesaplamalarınızı 'sütun bazında' vektörleştirilecek şekilde yapılandırmalısınız.

Şimdi Soln3'e geri dönebiliriz. Döngü sırası 'satır bazında'. Hadi değiştirelim

tic
Soln6 = ones(T, N);
for n = 1:N
    for t = 1:T
        Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
    end
end
toc

Soln6  0.201661 seconds.

Daha iyi, ama yine de çok kötü. Tek döngü - iyi. Çift döngü - kötü. Sanırım MATLAB, döngülerin performansını iyileştirmek için bazı iyi çalışmalar yaptı, ancak yine de döngü ek yükü var. İçeride daha ağır bir işiniz olsaydı, fark etmezdiniz. Ancak bu hesaplama bellek bant genişliğine bağlı olduğundan, döngü ek yükünü görüyorsunuz. Ve edecek daha da net orada FUNC1 çağıran ek yükünü bakın.

Öyleyse arrayfun'dan ne haber? Orada da işlev yok, bu yüzden çok fazla ek yük var. Ama neden çift iç içe bir döngüden çok daha kötü? Aslında, cellfun / arrayfun kullanma konusu birçok kez kapsamlı olarak tartışılmıştır (örneğin burada , burada , burada ve burada ). Bu işlevler yavaştır, bu tür ince taneli hesaplamalar için bunları kullanamazsınız. Bunları kod kısalığı ve hücreler ile diziler arasında süslü dönüşümler için kullanabilirsiniz. Ancak işlevin yazdığınızdan daha ağır olması gerekir:

tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc

Soln7  0.016786 seconds.

Soln7'nin artık bir hücre olduğuna dikkat edin .. bazen bu yararlıdır. Kod performansı şimdi oldukça iyi ve çıktı olarak hücreye ihtiyacınız varsa, tamamen vektörleştirilmiş çözümü kullandıktan sonra matrisinizi dönüştürmenize gerek yok.

Öyleyse neden arrayfun basit bir döngü yapısından daha yavaş? Maalesef, kaynak kodu olmadığı için kesin olarak söylemek mümkün değil. Sadece arrayfun'un her türden farklı veri yapısını ve argümanı işleyen genel amaçlı bir işlev olduğundan, doğrudan döngü yuvaları olarak ifade edebileceğiniz basit durumlarda mutlaka çok hızlı olmayacağını tahmin edebilirsiniz. Ek yükün nereden geldiğini bilemeyiz. Daha iyi bir uygulama ile genel giderler önlenebilir mi? Belki değil. Ancak maalesef yapabileceğimiz tek şey, iyi çalıştığı ve çalışmadığı durumları belirlemek için performansı incelemek.

Güncelleme Bu testin yürütme süresi kısa olduğundan, güvenilir sonuçlar elde etmek için şimdi testlerin etrafına bir döngü ekledim:

for i=1:1000
   % compute
end

Aşağıda verilen bazı zamanlar:

Soln5   8.192912 seconds.
Soln7  13.419675 seconds.
Oli     8.089113 seconds.

Görüyorsunuz ki dizifun hala kötü, ama en azından üç kat daha kötü değil. Öte yandan, sütun bazlı hesaplamalara sahip tek bir döngü, tamamen vektörleştirilmiş sürüm kadar hızlıdır ... Hepsi tek bir CPU üzerinde yapıldı. Soln5 ve Soln7 sonuçları, 2 çekirdeğe geçersem değişmez - Soln5'te paralel hale getirmek için bir parfor kullanmam gerekir. Hızlanmayı unutun ... Soln7 paralel olarak çalışmaz çünkü arrayfun paralel çalışmaz. Olis vektörize edilmiş versiyonu ise:

Oli  5.508085 seconds.

9
Mükemmel cevap! Ve matlab central'daki bağlantıların hepsi çok ilginç okumalar sağlar. Çok teşekkürler.
Colin T Bowers

Bu güzel bir analiz.
H.Muster

Ve ilginç bir güncelleme! Bu cevap vermeye devam ediyor :-)
Colin T Bowers

3
sadece küçük bir yorum; MATLAB 6.5'te cellfunbir MEX dosyası olarak uygulandı (yanında C kaynak kodu ile). Aslında oldukça basitti. Tabii ki sadece 6 kodlanmış işlevlerden birini uygulayarak desteklenen (bir fonksiyon kolu, biriyle sadece bir dize işlev adları geçmektedir edemedim)
Amro

1
arrayfun + işlev tutamacı = yavaş! ağır kodda bunlardan kaçının.
Yvon

-8

Çünkü!!!!

x = randn(T, N); 

gpuarraytip değil ;

Tek yapmanız gereken

x = randn(T, N,'gpuArray');

2
Sanırım soruyu ve mükemmel cevabı @angainor tarafından biraz daha dikkatli okumanız gerekiyor. Onunla hiçbir ilgisi yok gpuarray. Bu yanıtın reddedilmesinin nedeni neredeyse kesinlikle budur.
Colin T Bowers

@Colin - Angainor'un daha kapsamlı olduğuna katılıyorum, ancak yanıt 'gpuArray'den bahsetmiyor. 'GpuArray'in burada iyi bir katkı olduğunu düşünüyorum (eğer doğruysa). Ayrıca, soru "Burada neler oluyor?" İle biraz özensizleşti. , bu yüzden verileri vektörleştirmek ve bir GPU'ya göndermek gibi ek yöntemler için kapıyı açtığını düşünüyorum. Bu cevabın sürmesine izin veriyorum çünkü gelecekteki ziyaretçiler için değer katabilir. Yanlış arama yaptıysam özür dilerim.
jww

1
Ayrıca gpuarraysadece nVidia grafik kartları için desteklenen gerçeğini de unutuyorsunuz . Böyle bir donanıma sahip değillerse, tavsiyeniz (veya eksikliğiniz) anlamsızdır. -1
rayryeng

Öte yandan, gpuarray, matlab vektörleştirilmiş programlamanın ışık kılıcıdır.
MrIO
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.