- 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
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.