- FFmpeg geliştiricileri, elle yazılmış assembly kodu sayesinde en fazla 100 kata varan performans artışı duyurdu
- Bu yama, tüm programda değil yalnızca belirli bir işlevde hız iyileştirmesi sağlıyor
- Güncel CPU'larda AVX512 desteği olduğunda 100 kat, AVX2 desteğinde ise %64 performans artışı görüldü
- Söz konusu iyileştirme, çoğunlukla çok bilinmeyen bir filtreye uygulandı
- Derleyicinin otomatik optimizasyonu ile elle yazılmış assembly kodu arasındaki performans farkı hâlâ ortaya çıkıyor
FFmpeg performans artışı: 100 kat hızlanma gerçekte ne anlama geliyor?
Son yama ve başlıca iyileştirmeler
- FFmpeg proje geliştiricileri, elle yazılmış assembly kodu uygulamanın ardından, açık kaynaklı çapraz platform medya dönüştürme aracında "100 kat hız artışı" sonucunu öne çıkardı
- Ancak geliştiriciler, bu iddianın FFmpeg'in tamamı için değil tek bir işlev için geçerli olduğunu açıkça belirtti
- Bu olağanüstü optimizasyon
rangedetect8_avx512 işlevinde yapıldı; güncel AVX512 destekli işlemcilerde en fazla 100 kat, AVX2 yolunda ise yaklaşık %64 performans artışı sağlandı
- Bu özellik, pek bilinmeyen bir filtreye uygulanmış durumda; daha önce geliştirme önceliği taşımıyordu, ancak bu kez SIMD (tek komut çoklu veri) yaklaşımıyla paralel işleme optimizasyonu gerçekleştirildi
Geliştiricilerin açıklaması ve teknik arka plan
- FFmpeg geliştiricileri, Twitter üzerinden "100 kat hızlanan şey tek bu işlev; FFmpeg'in tamamı değil" diyerek durumu net biçimde açıkladı
- Ek açıklamada, ilgili özelliğin sisteme bağlı olarak %100'e kadar hız artışı sağlayabileceği belirtildi
- Bu performans artışı, SIMD teknolojisi temelinde modern çiplerde paralel işleme verimliliğinin büyük ölçüde artırılmasının sonucu
Assembly dilini doğrudan yazmanın önemi
Geçmişten bugüne optimizasyon yaklaşımı
- 1980'ler ve 1990'larda, sınırlı donanım koşullarında elle yazılmış assembly kodu oyunlar ve çeşitli yazılımlarda hız artışının temel araçlarından biriydi
- Günümüzde çoğu modern derleyici üst düzey dil kodunu assembly'ye dönüştürüyor; ancak derleyicilerin register ataması gibi optimizasyon sınırlamaları nedeniyle elle yazılmış assembly kodu hâlâ daha yüksek performans gösterebiliyor
- FFmpeg, bu optimizasyon felsefesini sürdüren az sayıdaki projeden biri ve kendi assembly eğitimini de sunuyor
FFmpeg'in ekosistem üzerindeki etkisi
- FFmpeg'in kütüphaneleri ve araçları, Linux, Mac OS X, Microsoft Windows, BSD ve Solaris gibi çeşitli ortamlarda çalışıyor
- VLC gibi popüler video oynatıcılarda da FFmpeg'in
libavcodec ve libavformat kütüphaneleri kullanılıyor
- Bu da FFmpeg'in, geniş açık kaynak medya kodlama/kod çözme ekosistemi içinde ne kadar önemli bir teknik konuma sahip olduğunu gösteriyor
Sonuç
- Bu performans artışı, tüm çekirdek işlevler yerine yalnızca bazı işlevlerle sınırlı olsa da performans sınırlarını zorlayan bir örnek sunuyor
- Modern donanıma özel optimizasyon ile açık kaynak ruhunun birleşimi, medya işleme alanında teknik bir örnek oluşturmaya devam ediyor
3 yorum
Bu, geçmişte de bugün de geçerli.
Benzer şekilde, bir codec kütüphanesini ARM'a portlarken önce SSE ile yazılmış çekirdekleri tek tek çözerek ilerlemiştim; hepsini çözüp geriye yalnızca skaler sürüm kaldığında gerçek dünya benchmark'ı çalıştırdığımda performans farkı anlamlı düzeydeydi.
GCC'den daha optimize kod yazabilen mühendisler olması... gerçekten etkileyici.
Hacker News yorumu
10 yıl boyunca HEVC gibi şeylerde SIMD optimizasyonu yaparken assembly sürümüyle normal C sürümünü karşılaştırmak neredeyse bir şakaya dönüşmüştü, çünkü 100 kat fark gibi devasa sayılar çıkıyordu. Aslında bu tür sonuçlar başlangıçta verimliliğin aşırı düşük olduğunu gösterir. Gerçek kullanımda ortam, mikrobenchmarklardaki gibi önbelleğin dolu olduğu halde aynı fonksiyonun yüzbinlerce kez art arda çağrılması değildir. Bunun yerine gerçek durumda başka birçok işin arasında sadece bir kez çağrılabilir. Önbellek etkisini azaltmak için çok büyük bir test bellek alanı oluşturup test etmek gerekir, ama bunu gerçekten yapıyorlar mı emin değilim.
FFmpeg gibi video dönüştürme yazılımlarının da ayrıca "makro benchmark" yapıp yapmadığını merak ediyorum. Uzun süre boyunca performans, kalite, CPU kullanımı gibi şeyleri çeşitli videolar ve dönüştürme kombinasyonlarıyla ölçüyor gibi görünüyorlar ama bunun için özel ve tutarlı donanım gerekir diye düşünüyorum.
Biraz farklı yönde bir soru olacak ama, SIMD konusunda deneyimli görünüyorsun, bu yüzden sormak istiyorum: ISPC kullandın mı ve hakkında ne düşünüyorsun? Günümüzde bile hâlâ doğrudan SIMD kodu yazmak gerekmesi ve genel derleyicilerin otomatik vektörleştirmede zayıf kalması bana biraz absürt geliyor. Bu durum, GPU kernel'larında eskiden beri otomatik vektörleştirmenin hep yapılabilmesiyle tezat oluşturuyor.
Bence ffmpeg'in kendisi de mikrobenchmark'tan çok farklı değil. Özünde
while (read(buf)) write(transform(buf))gibi bir yapısı var.Ne yazık ki önbellek sorununun dışında da geliştiriciler bazen %100 hız artışı diyor ama gerçekte bu çok sınırlı bir filtre için geçerli oluyor. Yine de bilgiyi oldukça şeffaf aktarma eğilimindeler.
"Başlangıçta verimsizdi" yorumuna karşılık, bence hangi sayıların çıktığı ve sonucun kendisi önemli.
Haberde bazı yerlerde 100 kat, bazı yerlerde ise %100 hız artışı dendiği için kafa karışıklığı var. Örneğin "‘rangedetect8_avx512’ performansı %100.73 arttı" denirken ekran görüntüsü 100.73 katı gösteriyor. 100 kat demek %9900 artış demektir; %100 hız artışı ise 2 kat daha hızlı demektir. Gerçekte hangisinin doğru olduğunu merak ediyorum.
Ekran görüntüsünde görüldüğü gibi kesin olarak 100 kat (ya da 100.73 kat) doğru; bu da %9973 hız artışı anlamına geliyor. Haberin gövdesinde yüzde işareti yanlış kullanılmış gibi görünüyor.
Tek bir fonksiyon bazında 100 kat, tüm filtre bazında %100 (2 kat).
ffmpeg tarafının iddiası 100 kat. %100 ifadesi haberdeki bir yazım hatası gibi duruyor.
Fonksiyon adının
8ile ilgili olması ve 8 bit değerler üzerinde çalışması nedeniyle, önceki uygulama skalerse AVX512 ile aynı anda 128 öğe işlenebileceği için 100 kat hızlanma mümkün görünüyor.ffmpeg'in assembly yazımıyla ilgili resmi kılavuzu da var; referans olması için bırakıyorum: https://news.ycombinator.com/item?id=43140614
Haberde
rangedetect8_avx512'in gerçekte hangi durumda kullanıldığı ve tüm dönüştürme sürecinde gerçek zamanlı performansı ne kadar iyileştirdiği belirsiz görünüyor. Bunun gerçekten fark edilir, pratik bir anlamı olup olmadığını merak ediyorum.Eskiden video sinyalinin analog olduğu dönemde kontrol sinyalleri bant içinde kodlanarak işlenirdi. DVD sonrasında da dijital sinyal analog olarak çıktığı için renklerde 16'nın altındaki değerler (0~255 içinde) "siyahın da altındaki" sinyal olarak kullanıldı; BluRay ve HDMI da aynı şekilde. Son dönemde ise tüm 0~255 aralığını kullanmaya doğru bir geçiş var. Ama hâlâ sinyal aralığı ayrımı düzgün yapılmadığı için görüntünün kararıp bozulduğu durumlar sık görülüyor. Bu fonksiyon piksel değerlerinin 16~255 mi yoksa 0~255 mi olduğunu saptamak için kullanılıyor gibi görünüyor. Eğer gerçekten 16~255 olduğu kesinse, kodlama sırasında bilgi tasarrufu sağlanabilir. Ama bu kısmı tahmin ediyorum. Ben de işim gereği videoyla uğraşıyorum ama siyah seviyesi işini hâlâ sürekli yanlış yapıyor olmam utanç verici.
Bu filtre dönüştürme sürecinde kullanılmıyor; renk aralığı ya da alfanın premultiplied olup olmadığı gibi metadata amaçlı bilgileri belirlemek için kullanılıyor. Söz konusu fonksiyon da bunların içinde renk aralığını tespit etmeye karşılık geliyor.
İlginç bir deneyimi paylaşmak istiyorum: Assembly kodu yazdığım tek neden de SIMD olmuştu. Yakın zamanda bununla ilgili konuşurken, o dönemde neden assembly yazmak zorunda kaldığımı unutmuş olduğumu fark ettim. Aslında mesele, derleyicinin aliasing sorunu yüzünden istediğim optimizasyonu yapamamasıydı. Veriye başka şekillerde erişilmeyeceğini açıkça belirtip standart dışı bir anahtar sözcük kullanınca derleyici otomatik olarak SIMD komutlarını kullandı. Sonuçta elle yazdığım assembly'yi kaldırdım.
Bu optimizasyonun yalnızca x86/x86-64'e (AVX2, AVX512) uygulanması biraz ironik. Uzun süre herkes x86 kullandığı için SIMD optimizasyonunun yaygın uygulanma şansı vardı ama yeni genişletilmiş mimariler gerçekten zayıftı ya da uyumlulukları yetersizdi. Şimdi x86 SIMD nihayet iyi bir noktaya gelmiş olsa da, x86 artık standart olmadığı için ona bağımlı olmak zorlaştı.
Açıkçası assembly'nin optimize edilmiş C'den daha hızlı olması beni şaşırttı. Günümüz derleyicileri o kadar iyi ki, elle assembly yazınca performansın ancak çok az artacağını sanıyordum; ama belli ki yanılmışım. Bir gün assembly'yi kesin olarak öğrenmem gerektiğine karar verdim.
İlgili yamalara bakınca mevcut baseline (
ff_detect_range_c) son derece genel bir skaler C kodu ve hız artışı aynı işlemin AVX-512 sürümünden (ff_detect_rangeb_avx512) geliyor. FFmpeg geliştiricileri vektör genişliğinden bağımsız makrolarla assembly'yi elle yazmayı tercih ediyor ama Intel intrinsics ile de neredeyse aynı şekilde ifade etmek mümkün görünüyor (sonuçta esas fark register allocation düzeyinde, yani pratik fark çok büyük değil). Hız farkının özü, vektörleştirmenin ne kadar iyi yapıldığı. Modern derleyicilerle trivial olmayan döngülerin vektörleştirilmesi neredeyse imkânsız ve onda bile ancakgcc -O3gibi seçenekler verilirse deneniyor. Bu yüzden 8 bit gibi küçük birimlerle çalışan işlemlerde AVX/AVX2/AVX-512 ile doğrudan vektörleştirme yapmak en az onlarca kat performans farkı yaratıyor. Modern CPU'larda aşırı optimize edilmiş skaler kodu bile derleyiciden daha hızlı yazmanın mümkün olduğu durumlar var ama bunlar çok nadir ve çok dikkat gerektiriyor (dependency chain ve execution port yükü gibi nedenlerle). Benim gerçekten %40 hızlanma gördüğüm örnekler de oldu. İlgili bağlantılar: baseline C, AVX512 sürümüDaha düşük seviye optimizasyona biraz daha yaklaştığınızda, C derleyicisinin aniden garip davrandığı bir örneği bir saat içinde görürsünüz. Kod gerçekten aşırı sık çağrılıyorsa, optimizasyon sorunları pratikte gerçekten büyük önem kazanıyor. Örnek: https://stackoverflow.com/questions/71343461/how-does-gcc-not-clang-make-this-optimization-deciding-that-a-store-to-one-str
Sadece SIMD intrinsics seviyesine inmek bile derleyicinin otomatik ürettiğinden çok daha kolay şekilde yüksek performans elde etmeyi sağlayabilir. Bununla ilgili birkaç parçaya ayrılmış bir rehber bile yazmıştım: https://scallywag.software/vim/blog/simd-perlin-noise-i
Neredeyse tüm C/C++ kütüphanelerinin performans açısından kritik bölümlerinde elle yazılmış assembly kullanılır (
strlengibi basit fonksiyonlar da buna dahil). Derleyiciler çoğu zaman yeterince iyi sonuç veriyor ama bu düzeyde yatırım yapmaya değecek kadar kritik yazılım sayısı az.Gerçek performans artışı assembly'den değil, AVX512'den geliyor. Bu kernel o kadar basit ki, AVX512 intrinsics ile yazıldığında C kodu ile assembly arasında neredeyse hiç fark kalmaz. 100 kat hız farkının nedeni şu: a)
min/max, SIMD'de tek komutta yapılırken skaler taraftacmp+cmovolarak ayrılıyor b) 8 bit hassasiyet nedeniyle her AVX512 komutu aynı anda 64 öğe işliyor. Sonuç olarak L1 ve L2 önbellek bant genişliği bile doyurulabiliyor (Zen 5 için 128B, 64B/cycle). Ancak tüm bir kare işleniyorsa, bellek boyutu nedeniyle L3'e gitmek gerekebilir ve bu durumda kazanç yarıya iner; L3'e bile sığmıyorsa kazanç daha da küçülür.Sound Open Firmware (SOF) aklıma geldi; bu da gcc ile ticari Cadence XCC derleyicisi (Xtensa HiFi SIMD intrinsics desteğiyle) arasında seçim yapılarak derlenebiliyor: https://thesofproject.github.io/latest/introduction/index.html#toolchain-faq
100x, %100x değil.