Kodumun seri performansını iyileştirmek için bazı iyi stratejiler nelerdir?


66

Hesaplamalı bilimde çalışıyorum ve sonuç olarak, zamanımın önemsiz bir miktarını birçok kodun bilimsel verimini arttırmanın yanı sıra bu kodların verimliliğini anlamak için harcıyorum.

Üzerinde çalıştığım yazılımın okunabilirlik / yeniden kullanılabilirlik / bakım edilebilirlik değişimini karşılaştırdığımı ve performans için zamanın geldiğine karar verdiğimi varsayalım. Ayrıca benim sorunum için daha iyi bir algoritmaya sahip olmadığımı bildiğimi varsayalım (flop / s ve bellek bant genişliği açısından). Ayrıca kod tabanımın C, C ++ veya Fortran gibi düşük seviyeli bir dilde olduğunu varsayabilirsiniz. Son olarak, kodda olması gereken hiçbir paralellik olmadığını veya yalnızca tek bir çekirdekteki performansla ilgilendiğimizi varsayalım.

İlk denenecek en önemli şeyler nelerdir? Ne kadar performans alabileceğimi nasıl bilebilirim?

Yanıtlar:


66

Her şeyden önce, usta ve Dan'ın işaret ettiği gibi, profil oluşturma esastır. Ben şahsen Linux üzerinde Intel'in VTune Amplifikatörünü kullanıyorum , çünkü bana ne zaman harcadığı konusunda çok ince bir genel bakış sunuyor.

Algoritmayı değiştirmeyecekseniz (yani, tüm optimizasyonlarınızı eski hale getirecek büyük değişiklikler olmayacaksa), o zaman büyük bir fark yaratabilecek bazı genel uygulama ayrıntılarını aramanızı öneririm:

  • Hafızanın bulunduğu yer : birlikte okunan / birlikte kullanılan veriler de birlikte depolanır mı, yoksa burada ve orada bit ve parçaları alıyor musunuz?

  • Bellek hizalaması : Çiftleriniz aslında 4 byte'a mı ayarlanmış? Nasıl toplandın structs? Pedantik olmak posix_memalignyerine , kullanın malloc.

  • Önbellek verimliliği : Yerellik, çoğu önbellek verimliliği sorunuyla ilgilenir, ancak sık sık okuduğunuz / yazdığınız bazı küçük veri yapılarınız varsa, bunların bir önbellek tamsayısı veya önbellek satırının bir kısmı (genellikle 64 bayt) olmalarına yardımcı olur. Ayrıca, verilerinizin bir önbellek çizgisinin boyutuyla aynı hizada olup olmadığına da yardımcı olur. Bu, bir veri parçasını yüklemek için gereken okuma sayısını büyük ölçüde azaltabilir.

  • Vektörleştirme : Hayır, el kodlu montajcı ile zihinselleşmeyin. gccotomatik olarak SSE / AltiVec / ne olursa olsun çevrilen vektör türlerini sunar.

  • Öğretim Düzeyi Paralelliği : Vektörleşmenin piç oğlu. Sık sık tekrarlanan bir hesaplama iyi bir şekilde vektörleşmiyorsa, giriş değerlerini toplamayı ve aynı anda birkaç değeri hesaplamayı deneyebilirsiniz. Bu tür döngü döngü açma gibi. Burada sömürdiğiniz şey, CPU'nuzun genellikle çekirdek başına birden fazla kayan nokta birimine sahip olmasıdır.

  • Aritmetik hassasiyet : Yaptığınız her şeyde gerçekten çift hassasiyetli aritmetik gerekli mi? Örneğin, bir Newton yinelemesinde bir düzeltmeyi hesaplıyorsanız, genellikle hesapladığınız tüm rakamlara ihtiyacınız yoktur. Daha ayrıntılı bir tartışma için bu makaleye bakın .

Bu ipuçlarından bazıları daxpy_cvec bu iplikte kullanılır. Fortran kullanıyorsanız (kitaplarımdaki düşük seviyeli bir dil değil), bu "hilelerin" çoğu üzerinde çok az kontrol sahibi olacağınızı söylemiştiniz.

Özel bir donanım üzerinde çalışıyorsanız, örneğin tüm üretim işlemleriniz için kullandığınız bir küme kullanıyorsanız, kullanılan CPU'ların özelliklerini de okumak isteyebilirsiniz. Birleştiriciye doğrudan bu mimari için şeyler yazmanız gerekmez, ancak kaçırmış olabileceğiniz başka optimizasyonları bulmak için size ilham verebilir. Bir özelliği bilmek, onu kullanabilecek kod yazmak için gerekli ilk adımdır.

Güncelleme

Bunu yazdığımdan bu yana bir süre geçti ve bunun çok popüler bir cevap olduğunu fark etmemiştim. Bu nedenle, önemli bir nokta eklemek istiyorum:

  • Yerel Bilgisayar Bilimcinizle konuşun : Sadece algoritmaları ve / veya hesaplamaları daha verimli / zarif / paralel hale getirmekle ilgilenen bir disiplin olsaydı ve hepimiz onlardan tavsiye isteyebilir miyiz? İyi haber şu ki, bu disiplin var: Bilgisayar Bilimi. Muhtemelen, kurumunuzun bile kendisine adanmış bütün bir departmanı vardır. Bu adamlarla konuş.

Eminim ki, birkaç Bilgisayar Bilimi olmayan Bilim insanı, bunun hiçbir şeye yol açmayan söz konusu disiplinle ilgili sinir bozucu tartışmaların anılarını ya da başkalarının anekdotlarının anılarını geri getireceğinden eminim. Cesaretini kırma. Disiplinlerarası işbirliği zor bir şey ve biraz iş gerektiriyor, ancak ödüller çok büyük olabilir.

Tecrübelerime göre, bir Bilgisayar Bilimcisi (CS) olarak, hile hem beklentileri hem de iletişim haklarını elde etmektir.

Beklenti- yönünden, bir CS ancak sorununuzun ilginç olduğunu düşündüğü takdirde size yardımcı olacaktır. Bu, sizin anlamadığınız bir sorun için yazdığınız, ancak gerçekten yorum yapmadığınız bir kod parçasını optimize / vectorize / paralelleştirmeye çalışmak dışındadır. CS'ler genellikle altta yatan sorunla daha fazla ilgilenir, örneğin bunu çözmek için kullanılan algoritmalar. Onlara çözüm vermeyin , sorununuzu sorun .

Ayrıca CS’nin “ bu problem zaten çözüldü ” demesi için hazırlıklı olun ve size yalnızca bir makaleye referans verin. Bir tavsiye kelimesi: Bu makaleyi okuyun ve eğer gerçekten probleminiz için geçerliyse, önerdiği algoritmayı uygulayın. Bu, kendini beğenmiş bir CS değil, sadece sana yardım eden bir CS. Üzülme, hatırlama: Eğer sorun hesaplama açısından ilginç değilse, yani zaten çözülmüş ve çözümün optimal olduğu gösterilmişse, bunun üzerinde çalışmayacak, sizin için daha az kod yazacaktır.

Haberleşme tarzında, en CSS alanınızda uzman olmadığını hatırlamak ve bakımından sorununu açıklamak neyi aksine, yaptığınız nasıl ve niçin . Biz genellikle gerçekten umurumda değil neden ve nasıl olduğunu, iyi, en iyi şeyi yapın.

Örneğin, şu anda SPH ve Multipoles tabanlı simülasyon kodlarının daha iyi bir versiyonunu yazmak için bir grup Hesaplamalı Kozmolog ile çalışıyorum . Karanlık madde ve galaksi haloları (huh?) Anlamında konuşmayı durdurmak ve hesaplamanın özüne inmek, yani her parçacığın belirli bir yarıçapındaki tüm komşuları bulmaları gerektiği, bazılarını hesaplamaları gereken yaklaşık üç toplantı sürdü . bunların üstündeki miktar ve sonra tüm bahsedilen komşuların üzerinden tekrar akın ve bu miktarı başka bir hesaplamada uygulayın. Sonra parçacıkları hareket ettirin ya da en azından bir kısmını hareket ettirin ve hepsini tekrar yapın. Eski, inanılmaz derecede ilginç olsa da (öyle!), İkincisi algoritmalar hakkında düşünmeye başlamak için ihtiyacım olan şey.

Fakat asıl noktadan ayrılıyorum: Hesaplamanızı hızlandırmakla gerçekten ilgileniyorsanız ve kendiniz bir Bilgisayar Bilimcisi değilseniz, bir taneyle konuşun.


4
Profil oluşturma araçları giderken valgrind'i unutmayacağım .
GertVdE

1
Sizinle aynı fikirdeyim Pedro, optimize edilen program bir F1 yarış arabası gibi, zaten optimal olana yakın. Uygulamada gördüğüm programlar, bilimsel değil, çoğu zaman Cadillac Coupe DeVilles gibi. Gerçek bir performans elde etmek için tonlarca yağ kesilebilir. Bundan sonra, döngü tıraş kendi adımlarını vurmaya başlar.
Mike Dunlavey

1
@MikeDunlavey: Tamamen katılıyorum. Daha algoritmik olarak ilgili sorunları ele almak için cevabımı güncelledim.
Pedro

1
@MikeDunlavey, ben değilim CS halk :)
Pedro

2
Bunu U Mass. Lowell’daki bir konuşmada gösterdim. 730x hızlandırmanın tüm aşamalarını gösteren canlı bir demo oldu. Sanırım bir profesör yarım düzine sayıya ulaştı.
Mike Dunlavey

38

Bilimsel yazılım, neyin ayarlanması gerektiğini bilmek kadar, diğer yazılımlardan çok da farklı değildir.

Kullandığım yöntem rastgele duraklatmadır . İşte benim için buldukları bazı hızlandırmalar:

Eğer logve gibi fonksiyonlarda çok fazla zaman harcanıyorsa, expbu fonksiyonların argümanlarının ne olduğunu, çağrılan noktaların bir fonksiyonu olarak görebiliyorum. Genellikle aynı argümanla tekrar tekrar çağırılırlar. Eğer öyleyse, notlama büyük bir hızlandırma faktörü üretir.

BLAS veya LAPACK işlevlerini kullanıyorsam, dizileri kopyalamak, matrisleri çarpmak, choleski dönüşümü vb. İşlemleri yapmak için rutinlerde çok fazla zaman harcandığını bulabilirim.

  • Dizileri kopyalama yordamı hız için orada değil, kolaylık olması için orada. Bunu yapmanın daha az kullanışlı, ancak daha hızlı bir yolu olduğunu görebilirsiniz.

  • Matrisleri çarpma ya da ters çevirme ya da choleski dönüşümü alma rutinleri, üst ya da alt üçgen için 'U' ya da 'L' gibi seçenekleri belirten karakter argümanlarına sahip olma eğilimindedir. Yine, bunlar kolaylık için oradalar. Benim bulduğum şey, matrislerimin çok büyük olmadığından, rutinler zamanlarının yarısından fazlasını harcayarak alt rutini arayarak karakterleri sadece seçenekleri deşifre etmek için harcıyorlardı . En pahalı matematik rutinlerinin özel amaçlı versiyonlarını yazmak, büyük bir hızlanma üretti.

Sadece ikincisini genişletebilirsem: matrix-multiply rutin DGEMM, karakter argümanlarının kodunu çözmek için LSAME öğesini çağırır. Kapsayıcı yüzde zamanına bakmak (bakmaya değer tek istatistik) profilerler "iyi" olarak kabul edilen DGEMM'ye toplam zamanın yüzde bir kısmını (% 80 gibi) ve LSAME'yi toplam zamanın% 50'sini kullanarak gösterebilir. Birincisine bakarsak, "iyi bir şekilde optimize edilmesi gerekiyor, bu konuda yapabileceğim pek bir şey yok" demeye özendirilirsiniz. İkincisine baktığınızda, "Huh? Bunların hepsi bu mu? Bu sadece ufacık bir küçük rutin. Bu profiler yanlış olmalı!"

Yanlış değil, sadece bilmeniz gerekenleri söylemiyor. Rasgele duraklatmanın gösterdiği şey DGEMM'in yığın örneklerinin% 80'inde ve LSAME'in% 50'de olduğu. (Bunu tespit etmek için çok fazla örneğe ihtiyacınız yok. 10 genellikle bol miktarda.) Dahası, bu örneklerin çoğunda, DGEMM LSAME'yi birkaç farklı kod satırından çağırıyor .

Şimdi her iki rutinin neden bu kadar kapsayıcı zaman geçirdiğini biliyorsunuz . Bunca zaman geçirmek için kodunuzda nerelerden çağrıldığını da biliyorsunuz . Bu yüzden rastgele duraklama kullanıyorum ve ne kadar iyi olursa olsun, profilleyicilere sarılıklı bir bakış atıyorum. Neler olduğunu anlatmak yerine ölçüm almakla daha çok ilgileniyorlar.

Matematik kütüphanesi yordamlarının ilk dereceye kadar optimize edildiğini varsaymak kolaydır, ancak gerçekte çok çeşitli amaçlar için kullanılabilir olması için optimize edilmiştir. Neler olduğunu gerçekten görmelisin , varsayılması kolay olanı değil.

EKLENDİ: Son iki sorunuzu cevaplamak için:

İlk denenecek en önemli şeyler nelerdir?

10-20 yığın örneği alın ve yalnızca özetlemeyin, her birinin size ne söylediğini anlayın. Bunu önce, son ve aralarında yapın. ("Denemek" yok, genç Skywalker.)

Ne kadar performans alabileceğimi nasıl bilebilirim?

Yığın örnekleri , zamanın kesirinin ne kadar tasarruf edileceğine dair çok kaba bir tahmin verecektir. (Bu, bir dağılımını izler ; burada , neyi düzelteceğinizi görüntüleyen örnek sayısıdır ve , toplam örnek sayısıdır. Bunu değiştirmek için kullandığınız kodun maliyeti, ki bu umarım küçük olacaktır.) Ardından, hız oranı ve bu büyük olabilir. Bunun matematiksel olarak nasıl davrandığına dikkat edin. Eğer ve , ortalama ve mod 2 bir hızlanma oranı için 0.5, İşte dağıtım bulunuyor: Eğer riskten daha sonra ise, evet var küçük olasılık (0,03%) oβ ( s + 1 , ( n - s ) + 1 ) s n 1 / ( 1 - x ) n = 10 s = 5 x x xxβ(s+1,(ns)+1)sn1/(1x)n=10s=5x
görüntü tanımını buraya girin
x % 11'den daha düşük bir hız için 0,1'den düşüktür. Ancak bu, 10'dan büyük bir hızlanma oranı için 0.9'dan büyük olması eşit bir olasılıktır . Program hızıyla orantılı olarak para alıyorsanız, bu kötü bir ihtimal değil.x

Daha önce de belirttiğim gibi, daha fazla yapamayana kadar tüm prosedürü tekrarlayabilirsiniz ve bileşik hızlanma oranı oldukça büyük olabilir.

EKLENDİ: Pedro'nun yanlış pozitiflerle ilgili endişesine cevap olarak, ortaya çıkmaları beklenebilecek bir örnek oluşturmaya çalışmama izin verin. İki veya daha fazla kez görmedikçe asla potansiyel bir problem üzerinde hareket etmeyiz, bu nedenle bir problemi gördüğümüzde, özellikle toplam örnek sayısı büyük olduğunda, yanlış pozitiflerin bir problemi gördüğümüzde olmasını beklerdik. Diyelim ki 20 örnek aldık ve iki kez görelim. Bu, maliyetinin, tahminin şekli olan toplam yürütme süresinin% 10 olduğunu tahmin ediyor. (Dağılımın ortalaması daha yüksektir - .) Aşağıdaki grafikteki alt eğri dağılımı:(s+1)/(n+2)=3/22=13.6%

görüntü tanımını buraya girin

40 örneğe kadar numune aldığımızı (bir seferde sahip olduğumdan daha fazla) ve sadece ikisinde bir sorun görüp görmediğimizi düşünün. Bu problemin tahmini maliyeti (modu), uzun boylu eğride gösterildiği gibi% 5'tir.

"Yanlış pozitif" nedir? Bir sorunu çözdüğünüzde, beklenenden daha küçük bir kazanç olduğunu fark edersiniz, çözdüğünüz için pişman olursunuz. Eğriler (eğer sorun "küçükse), kazanım onu gösteren örneklerin oranından daha az olabilse de, ortalama olarak daha büyük olacağını göstermektedir.

Çok daha ciddi bir risk var - "yanlış bir negatif". Bir sorun olduğunda o zaman, ama bulunamadı. (Buna katkıda bulunmak, kanıt yokluğunun yokluğun kanıtı olarak değerlendirilme eğiliminde olduğu "onaylama yanlılığı" dır.)

Ne bir profil (iyi bir) almak sorun gerçekte ne hakkında daha az kesin bilgi pahasına, çok daha hassas bir ölçüm (yanlış pozitif dolayısıyla daha az şans) olsun olduğunu (bulma ve alma dolayısıyla daha az şans herhangi bir kazanç). Bu, elde edilebilecek genel hızlanmayı sınırlar.

Profil oluşturucuların kullanıcılarının gerçekte pratikte gördükleri hızlandırma faktörlerini bildirmelerini teşvik ediyorum.


Yeniden yapılması gereken başka bir nokta var. Pedro'nun sahte pozitifler hakkındaki sorusu.

Yüksek derecede optimize edilmiş koddaki küçük sorunlara inerken bir zorluk olabileceğinden bahsetti. (Bana göre küçük bir problem, toplam sürenin% 5'ini veya daha azını ilgilendiren bir problemdir.)

% 5 dışında tamamen optimal bir program inşa etmek tamamen mümkün olduğu için, bu noktaya sadece bu cevapta olduğu gibi deneysel olarak değinilebilir . Deneysel deneyimlerden genellemek için, şöyle devam eder:

Bir program, yazıldığı gibi, genellikle optimizasyon için çeşitli fırsatlar içerir. (Onlara "problemler" diyebiliriz, ancak bunlar genellikle mükemmel kodlardır, sadece önemli ölçüde iyileştirme yeteneğine sahiptirler.) Bu şemada biraz zaman alan yapay bir program gösteriliyor (100s, diyelim) ve A, B, C, ... bulunup düzeltildiğinde, orijinal 100'lerin% 30,% 21, vb.

görüntü tanımını buraya girin

F sorununun orijinal zamanın% 5'ine mal olduğuna, bu nedenle "küçük" olduğuna ve 40 veya daha fazla örnek olmadan bulmanın zor olduğuna dikkat edin.

Bununla birlikte, ilk 10 örnek kolayca A problemini bulur. ** Bu düzeltildiğinde, program 100/70 = 1.43x'lik bir hızlanma için sadece 70s alır. Bu sadece programı daha hızlı kılmakla kalmamakta, aynı zamanda kalan sorunların aldığı yüzdeleri de büyütmektedir. Örneğin, B problemi başlangıçta toplamın% 21'i olan 21'i aldı, ancak A, B'yi çıkardıktan sonra 70'lerden 21'i, veya% 30'u alır, bu yüzden tüm işlemin tekrarlandığını bulmak daha kolaydır.

İşlem beş kez tekrarlandıktan sonra, uygulama süresi 16.8'dir, bunun dışında F problemi% 30'dur,% 5'tir, bu nedenle 10 örnek bunu kolayca bulur.

Demek istediğim bu. Ampirik olarak, programlar büyüklük dağılımına sahip bir dizi problem içerir ve bulunan ve çözülen herhangi bir problem kalanları bulmayı kolaylaştırır. Bunu başarmak için, sorunlardan hiçbiri atlanamaz, çünkü, eğer oradalarsa, orada oturup, zaman içinde toplam hızı sınırlandırır ve kalan sorunları büyütmede başarısız olurlar. Bu yüzden saklanmakta olan sorunları bulmak çok önemlidir .

Eğer A'dan F'ye problemler bulunur ve giderilirse, hızlanma 100 / 11,8 = 8,5x olur. Bunlardan biri kaçırılırsa, örneğin D, o zaman hızlanma sadece 100 / (11.8 + 10.3) = 4.5x olur. Yanlış negatifler için ödenen bedel bu.

Bu yüzden, profilci "burada önemli bir sorun yok gibi gözüküyor" derken (yani iyi kodlayıcı, bu pratik olarak en uygun koddur), belki de doğru ve belki de değildir. ( Yanlış bir negatif .) Başka bir profilleme yöntemi denemeden ve orada olduğunu keşfetmediğiniz sürece, daha yüksek hız için düzeltmek için daha fazla sorun olup olmadığından emin olamazsınız. Tecrübelerime göre, profilleme yönteminin özetlenmiş çok sayıda örneğe ihtiyacı yoktur, özetlenir, ancak her bir örneğin optimizasyon için herhangi bir fırsat tanımak için yeterince iyi anlaşıldığı az sayıda örnek vardır.

2/0.3=6.671 - pbinom(1, numberOfSamples, sizeOfProblem)1 - pbinom(1, 20, 0.3) = 0.9923627

xβ(s+1,(ns)+1)nsy1/(1x)xyy1BetaPrime dağılımı. Bu davranışa ulaşarak 2 milyon örnekle simüle ettim:

         distribution of speedup
               ratio y

 s, n    5%-ile  95%-ile  mean
 2, 2    1.58    59.30   32.36
 2, 3    1.33    10.25    4.00
 2, 4    1.23     5.28    2.50
 2, 5    1.18     3.69    2.00
 2,10    1.09     1.89    1.37
 2,20    1.04     1.37    1.17
 2,40    1.02     1.17    1.08

 3, 3    1.90    78.34   42.94
 3, 4    1.52    13.10    5.00
 3, 5    1.37     6.53    3.00
 3,10    1.16     2.29    1.57
 3,20    1.07     1.49    1.24
 3,40    1.04     1.22    1.11

 4, 4    2.22    98.02   52.36
 4, 5    1.72    15.95    6.00
 4,10    1.25     2.86    1.83
 4,20    1.11     1.62    1.31
 4,40    1.05     1.26    1.14

 5, 5    2.54   117.27   64.29
 5,10    1.37     3.69    2.20
 5,20    1.15     1.78    1.40
 5,40    1.07     1.31    1.17

(n+1)/(ns)s=ny

Bu, hızlanma faktörlerinin ve bunların araçlarının, 5, 4, 3 ve 2 numuneden 2 vuruş için dağılımının bir grafiğidir. Örneğin, 3 örnek alınırsa ve bunlardan 2'si bir soruna isabet ederse ve bu sorun giderilebiliyorsa, ortalama hızlandırma faktörü 4x olur. 2 isabet sadece 2 örnekte görülürse, ortalama hız belirsizdir - kavramsal olarak sonsuz döngülü programlar sıfır olmayan bir olasılıkla var olur!

görüntü tanımını buraya girin


1
Uhm ... Bu bilgiyi profiler arama grafiklerine veya VTune tarafından sağlanan "aşağıdan yukarıya" tip özetlerine bakarak tam olarak anlamıyor musunuz?
Pedro

2
@Pedro: Yalnızca Varsa. Bir yığın örneğinde (& ilgili değişkenler) zaman artışının harcanmasının nedeninin tamamı kodlanmıştır. Neden harcandığını bilmiyorsan, ondan kurtulamazsın. Bazı problemler sınırlı bilgi ile bulunabilir, fakat her biri değil . Eğer bunlardan sadece bir kısmını alırsanız, ama her birini değil, o zaman elde edemeyeceğiniz problemler sizi daha fazla hızlanmadan engeller. Kontrol burada ve burada .
Mike Dunlavey

Muhtemelen, yönteminizi kötü profilleme ile karşılaştırıyorsunuz ... Her işlem için, toplam yürütme süresine katkısından bağımsız olarak profilden de geçebilir ve aynı etkide iyileştirmeler için arama yapabilirsiniz. Yaklaşımınızda endişe duyduğum şey, kodunuzdaki "sıcak noktalar" küçüldükçe ve küçüldükçe takip edeceğiniz artan yanlış pozitif sayısıdır.
Pedro,

@Pedro: Birden fazla örnek üzerinde düzeltebileceğiniz bir şey görene kadar örnek almaya devam edin. Beta distr, ne kadar tasarruf edebileceğini söyler, eğer umursarsan, ama gösterdiğinden daha az hız almaktan korkuyorsan, daha fazla olma ihtimalini de attığını bildiğine dikkat et. ). Profilleyicileri özetleyerek ortaya çıkan en büyük tehlike yanlış negatiflerdir . Bir sorun olabilir, ancak yalnızca , profilerin nerede olabileceği konusunda çok spesifik değilken sezginizin onu koklayacağını umuyorsunuz .
Mike Dunlavey

@Pedro: Bildiğim tek zayıflık, zamanın anlık görüntüsüne bakarak, o zamanın neden harcandığını, örneğin sadece asenkron olayları sakladığı asenkron olayları veya asenkron protokolleri işlediğini anlayamazsınız. Daha "normal" kod için, bana "iyi" bir profil gösterici göster ve sana sorun yaşadığı veya basitçe bulamadığı bir sorun göstereyim (seni yanılmaz akıllılarına geri döndürmeni). Genel olarak böyle bir problemi inşa etmenin yolu, sunulan amacın yerel olarak deşifre edilemeyeceğinden emin olmaktır. Ve bu tür problemler yazılımda çok fazla.
Mike Dunlavey

23

Sadece derleyiciniz hakkında derin bir bilgiye sahip olmakla kalmaz , aynı zamanda hedef mimariniz ve işletim sisteminiz hakkındaki bilginiz de vardır .

Performansı neler etkileyebilir?

Performansın her son onsunu sıkmak istiyorsanız, hedef mimarinizi her değiştirdiğinizde, kodunuzu değiştirmek ve yeniden optimize etmek zorunda kalacaksınız. Bir CPU ile optimizasyon olan bir şey, aynı CPU'nun bir sonraki revizyonunda alt optimal olabilir.

Bunun mükemmel bir örneği CPU önbelleği olacaktır. Programınızı hızlı, küçük önbellekli bir işlemciden biraz daha yavaş, biraz daha büyük önbellekli bir sürücüye taşıyın; profiliniz önemli ölçüde değişebilir.

Hedef mimari değişmese bile, bir işletim sistemine yapılan düşük seviye değişiklikleri de performansı etkileyebilir. Spectre ve Meltdown azaltma yamalarının bazı iş yüklerinde büyük etkisi oldu, bu nedenle bunlar optimizasyonlarınızın yeniden değerlendirilmesini zorlayabilir.

Kodumu nasıl optimize edebilirim?

En iyi duruma getirilmiş kod geliştirilirken, modüler kalması ve aynı algoritmanın farklı sürümlerini içeri ve dışarı kaydırmayı, muhtemelen çalışma kaynaklarında ve mevcut kaynağın boyutuna / karmaşıklığına bağlı olarak çalışma zamanında kullanılan belirli bir sürümü seçerek kolaylaştırmanız gerekir. işlenecek veriler.

Modülarite ayrıca hepsi aynı davranırlar doğrulamak için izin, optimize edilmiş ve iyileştirilmemiş versiyonlarının tümü üzerinde aynı test paketi kullanabilmek için olmak demektir ve bir hızla her biri profil gibi-için-benzeri karşılaştırma. Cevabımda biraz daha ayrıntıya giriyorum: “yoğunlaşarak tanınanların ötesinde” hesaplama yoğun kodları nasıl belgeleyip öğretecekler? .

daha fazla okuma

Ek olarak, Ulrich Drepper'in, “ Her Programcının Bilmeniz Gerekenler başlıklı mükemmel makalesine , David Goldberg'e kayan bir ünvan olan“ Her Bir Bilim Adamının Kayan Nokta Aritmetiği Hakkında Bilmeniz Gerekenler ”olarak bilinmesi gereken mükemmel makalesine bir göz atmak isterim .

Her optimizasyonun gelecekteki bir anti-optimizasyon olma potansiyeline sahip olduğunu unutmayın ; bu nedenle, minimumda tutulacak olası bir kod kokusu olarak kabul edilmelidir. Benim cevabım kodlama yaparken mikro-optimizasyon önemli midir? kişisel deneyimlerden somut bir örnek sağlar.


8

Bence soruyu çok dar ifade ediyorsun. Benim görüşüme göre, yararlı bir tutum, yalnızca veri yapılarında ve algoritmalarında yapılan değişikliklerin, birkaç 100 satırdan daha fazla olan kodlar üzerinde önemli performans kazanımları sağlayabileceği varsayımı altında yaşamaktır ve henüz bir karşı örnek bulamadım. bu iddia.


3
Prensipte kabul edilmiştir, ancak bir algoritma / veri yapısının performansı ile temel donanımın detayları arasındaki etkileşimi küçümsememelisiniz. Örneğin, dengeli ikili ağaçlar veri aramak / depolamak için harikadır, fakat küresel hafızanın gecikmesine bağlı olarak, bir karma tablo daha iyi olabilir .
Pedro

1
Kabul. Algoritmalar ve veri yapısı O (10) ila O (100) iyileştirme sağlayabilir. Ancak, birkaç hesaplama sınırlı problemi için (moleküler dinamik hesaplamaları, astrofizik, gerçek zamanlı görüntü ve video işleme, finans gibi) yüksek düzeyde ayarlanmış bir kritik döngü 3x ila 10x daha hızlı bir genel uygulama süresi anlamına gelebilir.
fcruz

Kötü boyutlandırılmış iç içe döngüler büyük ölçüde "üretim" kodlarında gördüm. Bunun dışında haklı olduğunu düşünüyorum.
dmckee

8

Yapmanız gereken ilk şey kodunuzu belirlemek. Sen öğrenmek istiyorsanız hangi aksi takdirde zaten yürütme Fazla zamanınızı yeme değildi Kodunuzdaki bir kısmını optimize bitebileceğini optimize etmek başlamadan önce programın parçaları yavaşlama vardır.

Linux

gprof oldukça iyidir, ancak size her bir satır için değil, her bir fonksiyon için ne kadar zaman harcandığını gösterir .

Apple OS X

Shark'ı denemek isteyebilirsin . Bu Yüklemeler> Geliştirici Araçları> chud 4.6.2, eski sürümü altında Elma Geliştirici sitesinde mevcuttur burada . CHUD ayrıca BigTop frontend, PMC Index arama aracı, Saturn fonksiyon düzeyinde profiler ve daha birçok komut gibi başka profil oluşturma araçlarını da içerir. Köpekbalığı bir komut satırı sürümü ile gelecek.


+1 Profil? Evet, bir şekilde ... Tahmin etmekten çok daha iyi, ama burada özellikle gprof ve diğer birçok profil yapan için geçerli olan sorunların bir listesi var .
Mike Dunlavey,

Shark OS X'te eski bir komut mu? Daha burada . Mountain Lion ile birlikte Instruments kullanmalı mıyım?
hhh

@hhh: Mac'ler için bir GUI profil oluşturucusuydu, ancak artık korunmuyor gibi görünüyor. Bu cevabı yazdığımdan beri bir elma makinesinde programlamadım, bu yüzden size yardımcı olamam.
Dan

1
Apple Developer sitesinde İndirmeler> Geliştirici Araçları> CHUD 4.6.2 altında bulunmaktadır. Eski sürüm burada "üreticisine başvurun", hata hakkında hiçbir fikri: maalesef bu yükleme başarılı değil - ve bir şeyleri profil her türlü içerir. Shark, Lion'dan sonra görünüşe göre Xcode'dan çıkarıldı ve daha sonra MacUpdate'te ücretsiz bir araç olduktan sonra Apple Dev sitesine geri döndü.
hhh

@ hhh: Buna cevap vermek için benden daha nitelikli görünüyorsunuz. Güncellemek için cevabımı düzenlemekten çekinmeyin veya kendinizinkini yazın.
Dan

7

Ne kadar performans alabileceğinize gelince, kodunuzun profilinden sonuçları alın ve zamanın "p" kısmını alan bir parça tanımladığınızı varsayalım. Bu parçanın performansını yalnızca "s" faktörü ile iyileştirecekseniz, toplam hızınız 1 / ((1-p) + p / s) olacaktır. Bu nedenle, hızınızı maksimum 1 / (1-p) faktörü ile artırabilirsiniz. Umarım yüksek p alanlarınız var! Bu, Amdahl’ın seri optimizasyon yasasına eşdeğerdir .


5

Kodunuzu optimize etmek dikkatli yapılmalıdır. Ayrıca zaten kodu zaten hata ayıkladığınızı varsayalım. Belirli öncelikleri yerine getirirseniz, çok zaman kazanabilirsiniz:

  1. Mümkün oldukça yüksek düzeyde optimize edilmiş (veya profesyonelce optimize edilmiş) kütüphaneler kullanın. Bazı örnekler FFTW, OpenBlas, Intel MKL, NAG kütüphaneleri vb. İçerebilir. Çok yetenekli olmadığınız sürece (GotoBLAS'ın geliştiricisi gibi) profesyonelleri yenemezsiniz.

  2. Kodunuzun hangi bölümlerinin en fazla zaman alacağını bulmak için bir profil oluşturucu kullanın (aşağıdaki listede epeydir bu konuya zaten adlandırılmıştır - Intel Tune, valgrind, gprof, gcov vb.). Nadiren çağrılan kod bölümlerini optimize etmek için zaman kaybına gerek yok.

  3. Profil oluşturucu sonuçlarından, kodunuzun en fazla zaman alan kısmına bakın. Algoritmanızın niteliğinin ne olduğunu belirleyin - CPU'ya mı bağlı yoksa belleğe mi bağlı? Her biri farklı bir optimizasyon teknikleri seti gerektirir. Çok fazla önbellek özeti alıyorsanız, bellek tıkanıklık olabilir - CPU, belleğin kullanılabilir olmasını bekleyen saat döngülerini boşa harcıyor. Döngünün sisteminizin L1 / L2 / L3 önbelleğine uygun olup olmadığını düşünün. Döngünüzdeki "if" ifadeleri varsa, profilerin dal yanlışlığı hakkında bir şey söyleyip söylemediğini kontrol edin. Sisteminizde branş yanlışlığı cezası nedir? Bu arada, şube yanlış tahmin verilerini Intel Optimizasyon Referans Kılavuzlarından [1] alabilirsiniz. Şube yanlış tahmin cezasının Intel el kitabında da göreceğiniz gibi işlemciye özgü olduğunu unutmayın.

  4. Son olarak, profiler tarafından tanımlanan sorunları ele alın. Burada zaten birkaç teknik tartışılmıştır. Optimizasyon konusunda bir dizi iyi, güvenilir, kapsamlı kaynak da mevcuttur. Sadece iki tanesini isimlendirmek için, Intel Optimizasyon Referans El Kitabı [1] ve Agner Fog [2] tarafından verilen beş optimizasyon el kitabı var. Derleyici zaten yapıyorsa, örneğin yapmanız gerekmeyebilecek bazı şeyler olduğunu unutmayın; örneğin, döngüyü açma, belleği hizalama, vb.) Derleyici belgelerini dikkatlice okuyun.

Referanslar:

[1] Intel 64 ve IA-32 Mimarileri Optimizasyon Referans El Kitabı: http://www.intel.sg/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf

[2] Agner Fog, "Yazılım Optimizasyon Kaynakları": http://www.agner.org/optimize/

  • "C ++ 'da yazılımı optimize etmek: Windows, Linux ve Mac platformları için bir optimizasyon rehberi"
  • "Assembly dilinde alt yordamları optimize etme: x86 platformları için bir optimizasyon kılavuzu"
  • "Intel, AMD ve VIA CPU'ların mikro mimarisi: Montaj programcıları ve derleyici üreticileri için bir optimizasyon kılavuzu"
  • "Talimat tabloları: Intel, AMD ve VIA CPU'lar için talimat gecikmelerinin, verimlerinin ve mikro işlem arızalarının listesi"
  • "Farklı C ++ derleyicileri ve işletim sistemleri için çağrı kuralları"

3

Burada sayılan diğerleri gibi hesaplamalı bir bilim insanı değilim (bu yüzden yanılıyor olabilirim :)) ama bugünlerde standart lib'ler kullandığımız sürece seri performans için çok fazla zaman harcamak için çok az nokta var. Kodu daha ölçeklendirilebilir hale getirmek için ek zaman / çaba harcamak daha faydalı olabilir.

Her durumda, burada performansın nasıl geliştirildiğine (yapılandırılmamış FE problemleri için) iki örnektir (eğer zaten okumadıysanız).

Seri : Özet ve ilgili metnin 2. yarısına bakınız.

Paralel : Özel olarak başlatma aşaması, sn 4.2.


3

Bu belki de bir cevaptan çok bir meta-cevaptır ...

Derleyicinizle yakın bir aşinalık geliştirmelisiniz. El kitabını okuyarak ve seçenekleri deneyerek bunu en verimli şekilde elde edebilirsiniz.

@Pedro'nun dağıtdığı iyi tavsiyelerin çoğu program yerine derlemeyi ayarlayarak uygulanabilir.


Son noktaya katılmıyorum. Derleyicinizin neler yapabileceğini bilmek bir şeydir, ancak kodunuzu yazmak, derleyicinizin aslında onunla bir şeyler yapabilmesi için tamamen farklı bir sorundur. Verilerinizi sizin için sıralayan, gerektiğinde daha düşük bir hassasiyet kullanan veya en içteki döngülerinizi az sayıda dallara sahip olacak veya hiç dalmayacak şekilde yeniden yazacak hiçbir derleyici bayrağı yoktur. Derleyicinizi bilmek iyi bir şeydir, ancak yalnızca daha iyi kod yazmanıza yardımcı olur, kodunuzu daha iyi yapmaz.
Pedro

1

(Linux) bir programı profilleme bir kolay yolu kullanmaktır perfiçinde statmod. En basit yol, aynen böyle çalıştırılması.

perf stat ./my_program args ...

ve size birçok faydalı performans istatistiği verecektir:

Performance counter stats for './simd_test1':

     3884.559489 task-clock                #    1.000 CPUs utilized
              18 context-switches          #    0.005 K/sec
               0 cpu-migrations            #    0.000 K/sec
             383 page-faults               #    0.099 K/sec
  10,911,904,779 cycles                    #    2.809 GHz
 <not supported> stalled-cycles-frontend
 <not supported> stalled-cycles-backend
  14,346,983,161 instructions              #    1.31  insns per cycle
   2,143,017,630 branches                  #  551.676 M/sec
          28,892 branch-misses             #    0.00% of all branches

     3.885986246 seconds time elapsed

Bazen D-önbellek yüklerini ve özlüyor da listeler. Çok fazla önbellek özlediğini görürseniz, programınız belleği yoğun tutar ve önbellekleri iyi kullanmaz. Bu günlerde, CPU'lar bellek bant genişliğinden daha hızlı oluyor ve genellikle sorun her zaman belleğe erişiyor.

Ayrıca perf record ./my_program; perf reportprofil yapmanın kolay bir yolunu deneyebilirsiniz . Daha fazla bilgi için man sayfalarını okuyun.

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.