2 puan yazan GN⁺ 2025-02-23 | Henüz yorum yok. | WhatsApp'ta paylaş
  • FFmpeg'in ilk assembly dersi, multimedya işlemede elle yazılmış SIMD assembly'nin neden gerekli olduğunu ve FFmpeg tarzı fonksiyon yazım kurallarını giriş seviyesinde özetliyor
  • Hedef okur kitlesinin C pointer'larını ve lise düzeyinde skaler-vektör ile toplama-çarpma kavramlarını biliyor olması gerekiyor; ders x86 64-bit ve Intel sözdizimi temelinde ilerliyor
  • FFmpeg içinde assembly function, SIMD ve vectorise neredeyse aynı anlamda kullanılıyor; birden fazla veri öğesini aynı anda işleme yaklaşımı görüntü, video ve ses işleme için çok uygun
  • Performans açısından manuel assembly tercih ediliyor; intrinsics genelde %10~15 daha yavaş kalıyor ve dav1d'nin otomatik vektörleştirmesi yaklaşık 2x, elle yazılmış sürüm ise 8x'e kadar çıkabilen örneklerle karşılaştırılıyor
  • İlk örnek fonksiyon x86inc.asm, INIT_XMM sse2, cglobal, movu, paddb, RET kullanarak iki uint8_t buffer'ının 16 baytlık değerlerini SIMD ile topladıktan sonra sonucu ilk buffer'a geri yazıyor

Dersin amacı ve önkoşul bilgiler

  • FFmpeg Assembly Language Lesson One, FFmpeg'de assembly dilinin nasıl yazıldığının temellerini ele alan ve bilgisayarın içinde gerçekte neler olduğunu anlamaya yardımcı olan bir giriş dersidir
  • Gerekli bilgiler şunlardır
    • C dili, özellikle pointer'lar
    • C bilmiyorsanız The C Programming Language öğrenmeniz önerilir
    • Lise matematiği düzeyinde skaler ve vektör, toplama ve çarpma kavramları

Assembly dili ve SIMD

  • Assembly dili, CPU'nun işlediği komutlara doğrudan karşılık gelen kodun insanlar tarafından okunabilir biçimde yazıldığı bir programlama dilidir
  • İnsan tarafından okunabilen assembly kodu, assembler'dan geçerek CPU'nun anlayabildiği makine kodu (machine code) ikili verisine dönüştürülür
  • FFmpeg'in assembly kodu çoğunlukla SIMD (Single Instruction Multiple Data) biçimindedir
    • Tek bir komut aynı anda birden fazla veri öğesi üzerinde çalışır
    • Buna vektör programlama da denir
    • Genel skaler programlama ise her seferinde tek bir veri öğesini işler
  • SIMD, bellekte art arda yerleştirilmiş çok sayıdaki veriyi işleyen görüntü, video ve ses işleme için çok uygundur
  • FFmpeg'de şu ifadeler neredeyse aynı anlamda kullanılır
    • assembly function
    • SIMD
    • vectorise
    • Yani assembly fonksiyonunu elle yazıp birden fazla veri öğesini aynı anda işlemek

FFmpeg neden assembly'yi doğrudan kullanıyor

  • Temel amaç multimedya işleme hızını artırmaktır
    • Assembly kodu yazarak 10x ve üzeri hızlanma sık görülür
    • Gerçek zamanlı video oynatımında takılmayı azaltmak için önemlidir
    • Enerji tüketimini düşürüp pil ömrünü uzatabilir
  • Video kodlama ve kod çözme fonksiyonları hem son kullanıcı tarafında hem veri merkezlerinde çok yaygın kullanıldığı için, küçük iyileştirmeler bile hızla birikir
  • FFmpeg, intrinsics yerine elle yazılmış assembly kullanır
    • intrinsics, assembly komutlarına karşılık gelen C benzeri fonksiyonlardır
    • Genelde elle yazılmış assembly'den %10~15 daha yavaştır
    • Bu oran derleyiciye göre değişebilir ve intrinsics savunucuları buna katılmayabilir
    • Hungarian Notation kullanımı nedeniyle okunmasının zor olduğu yönünde görüşler de vardır
  • inline assembly, FFmpeg'in bazı eski sürüm kodlarında veya Linux Kernel gibi projelerde hâlâ bulunabilir
    • Ayrı bir dosyada değil, doğrudan C kodunun içinde assembly yazma yöntemidir
    • FFmpeg gibi projelerde bunun okunmasının zor olduğu, derleyici desteğinin geniş olmadığı ve bakımının güç olduğu görüşü baskındır
  • Yalnızca derleyicinin otomatik vektörleştirmesinin yeterli olduğu görüşünün öğrenme amacı açısından göz ardı edilmesi önerilir
    • dav1d projesi için yakın tarihli testlerde otomatik vektörleştirme yaklaşık 2x hızlanma göstermiştir
    • Elle yazılmış sürüm ise 8x'e kadar çıkabilmiştir

Sözdizimi kapsamı ve başvuru kaynakları

  • Ders x86 64-bit assembly üzerine odaklanır
    • Buna amd64 da denir ve Intel CPU'larda da çalışır
    • ARM, RISC-V gibi diğer CPU'lara yönelik assembly için ileride genişleme ihtimali vardır
  • x86 assembly sözdiziminde AT&T ve Intel bulunur
    • AT&T sözdizimi daha eskidir ve Intel sözdizimine göre daha zor okunur olarak değerlendirilir
    • Derste Intel sözdizimi kullanılır
  • Genel kitaplar veya Stack Overflow gibi çevrimiçi kaynaklar, FFmpeg assembly'si için özellikle çok faydalı olmayabilir
    • Çünkü elde yazılmış Intel sözdizimli assembly kullanılır
    • Çevrimiçi kaynakların çoğu işletim sistemi programlama, donanım programlama veya SIMD dışı koda odaklanır
    • FFmpeg assembly'si, yüksek performanslı görüntü işleme için özelleşmiş kendine özgü bir yaklaşımdır
  • The Art of 64-bit assembly'nin son bölümlerindeki SIMD komutlarını ve çalışma biçimini görselleştiren diyagramlar faydalı olabilir
  • Sorular için bir Discord sunucusu sağlanmıştır

Register temel kavramı

  • Register, CPU içinde verinin işlendiği alandır
  • CPU bellekte doğrudan işlem yapmaz; veriyi register'a yükler, işler ve ardından tekrar belleğe yazar
  • Assembly'de genel olarak bir bellek konumundan başka bir bellek konumuna veri doğrudan kopyalanamaz; önce register üzerinden geçmesi gerekir

Genel amaçlı register'lar

  • GPR (General Purpose Register), veri veya bellek adresi tutabilen genel amaçlı register'dır
    • Burada en fazla 64-bit değer tutabilir
    • Pointer da tutabilir
    • Toplama, çarpma, shift gibi işlemler yapılabilir
  • Birçok assembly kitabı GPR'lerin ayrıntılarını ve tarihsel arka planını uzun uzun anlatır
  • FFmpeg'in assembly kodunda GPR'ler çoğunlukla iskele (scaffolding) gibi kullanılır; karmaşıklığın büyük bölümü ya gerekli değildir ya da soyutlanmıştır

Vektör register'ları ve veri boyutları

  • Vektör register'ları birden fazla veri öğesi tutar
  • x86'deki başlıca vektör register'ları şunlardır
    • mm: MMX register'ı, 64-bit boyutunda, tarihsel öneme sahip ve artık pek kullanılmıyor
    • xmm: XMM register'ı, 128-bit boyutunda, yaygın olarak kullanılabilir
    • ymm: YMM register'ı, 256-bit boyutunda, kullanırken bazı ek karmaşıklıklar içerir
    • zmm: ZMM register'ı, 512-bit boyutunda, kullanılabilirliği daha sınırlıdır
  • Video sıkıştırma ve açma işlemlerindeki hesaplamaların çoğu tamsayı tabanlıdır, bu yüzden ders de tamsayılara odaklanır
  • Tek bir xmm register'ı olan 128-bit şu şekillerde yorumlanabilir
    • 16 byte, her biri 8-bit
    • 8 word, her biri 16-bit
    • 4 doubleword, her biri 32-bit
    • 2 quadword, her biri 64-bit
  • Kısaltmalar ileride önemli olacaktır
    • byte: 8-bit veri
    • word: 16-bit veri
    • doubleword: 32-bit veri
    • quadword: 64-bit veri
    • double quadword: 128-bit veri

x86inc.asm'nin rolü

  • x86inc.asm, FFmpeg, x264 ve dav1d'de kullanılan hafif bir soyutlama katmanıdır
  • Assembly programcısının kodu daha kolay yazabilmesi için çeşitli özellikler sağlar
  • Başlarda önemli özelliklerden biri, GPR'lere r0, r1, r2 gibi etiketler vermesidir
    • Gerçek register adlarını ezberlemeniz gerekmez
    • FFmpeg'de GPR'ler çoğunlukla iskele görevi gördüğü için yazım yükünü azaltır

Basit bir skaler asm örneği

mov r0q, 3
inc r0q
dec r0q
imul r0q, 5
  • İlk satır, immediate value 3 değerini r0 register'ına quadword olarak yazar
    • Immediate value, bellekten alınan bir değer değil, assembly kodunun içinde saklanan değerdir
  • Intel sözdiziminde sağdaki kaynak operand soldaki hedef operand'a aktarılır
    • Bunu r0q = 3 gibi okuyabilirsiniz
    • Sıralama memcpy davranışına benzer
  • r0q içindeki q son eki, register'ın quadword olarak kullanıldığını gösterir
  • Sonraki işlemler şöyledir
    • inc ile değer 4 olur
    • dec ile değer yeniden 3 olur
    • imul ile 5 çarpılır ve sonuçta r0q 15 olur
  • mov, inc gibi insanlar tarafından okunabilen komutlara mnemonic denir
    • Assembler bunları makine koduna dönüştürür
    • Büyük harf MOV, INC ile küçük harf mov, inc aynıdır
    • FFmpeg'de mnemonic'ler küçük harfle yazılır, büyük harfler ise makrolar için bırakılır

İlk SIMD fonksiyon örneği

%include "x86inc.asm"
SECTION .text
;static void add_values(uint8_t *src, const uint8_t * src2)
INIT_XMM sse2
cglobal add_values, 2, 2, 2, src, src2
movu m0, [srcq]
movu m1, [src2q]
paddb m0, m1
movu [srcq], m0
RET
  • Bu fonksiyon src ve src2 verilerini SIMD ile toplar ve sonucu tekrar src konumuna yazar
  • %include "x86inc.asm", x264, FFmpeg ve dav1d topluluklarının geliştirdiği yardımcıları, önceden tanımlı adları ve makroları içerir
  • SECTION .text, yürütülecek kodun yer aldığı bölümü gösterir
    • Sabit veriler .data bölümüne konabilir
  • ;static void add_values(uint8_t *src, const uint8_t * src2), C fonksiyon argümanı biçimini gösteren bir yorumdur
    • Assembly'de noktalı virgül ;, C'deki // gibi yorum işlevi görür
  • INIT_XMM sse2, XMM register'larının ve sse2 komut kümesinin kullanılacağını belirtir
    • Çünkü paddb, sse2 komutudur
  • cglobal add_values, 2, 2, 2, src, src2, C fonksiyonu add_values'i tanımlar
    • Fonksiyonun 2 argümanı vardır
    • Fonksiyonda kullanılacak GPR sayısı, argümanlar dahil 2'dir
    • Kullanılacak XMM register sayısı 2'dir
    • Son iki öğe, fonksiyon argümanı etiketleri olan src ve src2'dir
    • Eski kodlarda argüman etiketleri olmadan doğrudan r0, r1 gibi GPR'ler kullanılabilir

load, packed add, store

movu m0, [srcq]
movu m1, [src2q]
  • movu, movdqu'nun kısaltılmış biçimidir ve move double quad unaligned anlamına gelir
  • Alignment ileriki derslerde ele alınacaktır; burada bunu [srcq] içinden 128-bit taşıyan bir komut olarak düşünebilirsiniz
  • mov içindeki köşeli parantezler adresin dereference edildiğini gösterir
    • Bu, C'deki *src ile benzer bir kavramdır
    • Bu işlem bir load'dur
  • q son eki pointer boyutunu gösterir
    • 64-bit sistemde bu, C pointer boyutu olan 8 bayttır
    • x86asm, 32-bit sistemlerde 32-bit kullanır
    • Gerçek load işlemi ise 128-bit'tir
  • Vektör register'ları xmm0 gibi tam adlar yerine soyutlanmış m0 ile gösterilir
    • Bu, ileride aynı kodu birden çok SIMD register boyutuna uyarlama yaklaşımıyla bağlantılıdır
paddb m0, m1
  • paddb, her iki register'daki byte öğelerini birbirine ekler
  • p öneki packed anlamına gelir ve vektör komutlarını skaler komutlardan ayırmak için kullanılır
  • b son eki byte düzeyinde toplama yapıldığını gösterir
  • 16 byte içeren iki register toplandığında, her konumdaki karşılık gelen öğeler a+q, b+r, c+s şeklinde toplanır
movu [srcq], m0
RET
  • movu [srcq], m0, sonuç verisini srcq pointer'ının gösterdiği adrese geri yazar
    • Bu işlem bir store'dur
  • RET, fonksiyonun döndüğünü gösteren bir makrodur
  • FFmpeg'deki assembly fonksiyonlarının neredeyse tamamı bir değer döndürmekten çok, argüman olarak aldığı veriyi değiştirir
  • Ödevlerde, kullanılabilir assembly fonksiyonları için function pointer oluşturma ve bunları kullanma yaklaşımı devam eder

Henüz yorum yok.

Henüz yorum yok.