2 puan yazan GN⁺ 2025-02-23 | 1 yorum | 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

1 yorum

 
GN⁺ 2025-02-23
Hacker News yorumları
  • Aynı konudaki başka bir kaynak: https://blogs.gnome.org/rbultje/2017/07/14/writing-x86-simd-...

    Buradaki yorumlara bakınca elle yazılmış SIMD’nin yararlılığı konusunda görüşlerin “tamamen belirsiz”den “görev açısından kritik”e kadar ayrıldığını görüyorum; “tamamen belirsiz” tarafı daha kalabalık göründüğü için görev açısından kritik tarafını biraz açıklayayım.
    FFmpeg kullanım sıklığı nedeniyle bariz bir örnek, ama genel amaçlı üretim AV1 kod çözücüsü olan dav1d’nin elle yazılmış SIMD’nin etkisini nicelleştirmek için daha kolay bir örnek olduğunu düşünüyorum.
    dav1d, büyük tarayıcılardan Android işletim sistemine kadar neredeyse her yerde kullanılıyor ve libgav1’in yerini aldı; başarısının büyük nedenlerinden biri muazzam hızı, bu da kod tabanının önemli bir kısmının elle yazılmış SIMD olmasından kaynaklanıyor.
    Zig gibi dillerin yerleşik SIMD desteğine sahip olması güzel, ancak potansiyel performans farkının araştırmaya değer olduğu bazı kullanım alanlarında doğrudan elle yazmak gerekiyor. dav1d’deki bazı kod satırları günde trilyonlarca kez çalıştırılıyor; bu yüzden olabildiğince hızlı olmalı ve elle yazılmış SIMD ile derleyici tarafından üretilen SIMD arasındaki fark bazı durumlarda %50’ye kadar çıkabildiğinden bu önemli.
    Ben de benzer şekilde çok sık çalıştırılacak kodlar yazma işine bir ölçüde dahil olduğum için, bu tür tekniklerin kaybolmaması adına FFmpeg assembly dili okulu gibi kaynakların oldukça önemli olduğunu düşünüyorum.

    • dav1d’nin ilginç yönlerinden biri, assembly ile yazılmış olduğu için kendi çağrı sözleşmesini kullanabilmesi.
      Çağrı sözleşmesi yöntemden yönteme değişebildiğinden, genel platform çağrı sözleşmesine uyan bir derleyicinin ürettiği koda göre yığına yazma ve yığından okuma çok daha az oluyor.
    • Ben de görev açısından kritik tarafındayım, ama ilginç bir karşı örnek de var. Küçük ayrıntılara odaklanınca veya tesadüfi karmaşıklığa saplanınca algoritmik optimizasyon daha az görünür hale gelebilir; platforma özgü kod çok fazlalaşıp değişiklik sürtünmesi artarsa yerel minimumdan çıkmak zorlaşır.
      Örneğin yeni matris çarpımımız, LLM çıkarımı için kullanılan ünlü bir kütüphaneden daha hızlı; hatta karşı taraf AMX kullanıp biz AVX512BF16 kullandığımızda bile böyle olabiliyor. Nedeni iş parçacığı darboğazı gibi görünüyor olabilir ya da başka bir şey de olabilir; ama araya JIT girince bunu anlamak zorlaşıyor.
      Platforma özgü çekirdekleri doğrudan elle yazmak zorunda kalsaydık bu sonuç ortaya çıkmazdı. Bir gündeki zaman sınırlı ve Highway ile tek bir uygulama yazabildiğimiz için, yeni çekirdek türleri ve blok boyutlarının yanı sıra paralelleştirme stratejisini ve onun parametrelerini seçen otomatik ayarlayıcıyı da içeren daha geniş bir tasarım alanını keşfedebildik.
      İkinci aşamada bazı kısımları elle ayarlamak mümkün olabilir, ancak yazmaç tahsisini ve çağrı sözleşmesini mikro düzeyde optimize etmeden önce daha geniş keşfin yapılmasını isterim.
    • Zig’in önemsiz aritmetik işlem aşırı yüklemelerinin ötesinde yerleşik SIMD desteği olarak ne sunduğunu merak ediyorum. SIMD’nin yararlılığının %90’ı bu tür basit işlemlerin dışında.
      Zig’i seviyorum, ama çoğu durumda C/C++’ta olduğu gibi CPU’ya özgü yerleşik fonksiyonları kullanmak gerektiğini anlıyorum.
      GCC ve Clang, vector_size özniteliğini ve bu “vektörleştirilmiş” tiplerin aritmetik operatör aşırı yüklemelerini destekliyor; bunun dışında çok daha fazlası da var. Aslında _mm256_mul_ps gibi intrinsic’ler de #define _mm256_mul_ps(a,b) (__m256)((v8sf)(a) * (v8sf)(b)) gibi uygulanıyor.
      Bu özelliklerin yararlılığı, Zig’de mümkün olandan çok daha büyük.
    • Doğru bir tespit. Karşı taraf olan encoder tarafında da benzer nedenlerle elle yazılmış SIMD çok yoğun kullanılıyor.
      Encoder tarafında özellikle döngüleri ve load’ları erken aşamada ortadan kaldırabilmek için problemi “yapılandırmak” gereken durumlar sık görülüyor; derleyiciler bu tür otomatik vektörleştirme kodu üretemiyor.
  • Eskiden çekirdek işlevlerin SIMD sürümlerini epey çok yazardık, ama artık neredeyse hiç yapmıyoruz. Denemeye değer yöntem, ilgili kodu ayırıp harika Compiler Explorer’da [0] çalıştırmak
    Sonra üretilen koda bakmak yeterli
    Günümüzde otomatik vektörleştirme, çoğu zaman işlevlerin SIMD sürümlerini oldukça iyi üretiyor; çoğu durumda derleyiciye sadece “ipucu” vermek gerekiyor. Örneğin hizalamayı açıkça belirtmek ya da vektör kaynak/hedef tiplerini doğrudan sağlamak gibi
    Derleyicinin neler yapabileceğini düşünerek C kodunu “şekillendirirseniz” çok şey başarabilirsiniz. Daha fazla ara değişken kullanmak, istenen işlemi gerçekten küçük parçalara ayırmak gibi
    En kötü durumda derleyici yeterince akıllı olmasa bile, üretilen assembly’yi temel alıp kalıp kodu baştan yazmadan düzenleyebilirsiniz
    Çoğu durumda sonuçtaki C işlevi, elle yazdığım kadar iyi ya da daha iyi vektörleştiriliyor; pek çok başka durumda da “yeterince yakın” olduğu için büyük fark yaratmıyor. Ayrıca bu kodun, açık bir sürüm olmadan WASM veya NEON gibi hedeflerde de iyi vektörleştirilme olasılığı yüksek
    [0] https://godbolt.org/

    • Biz de buna biraz benzer bir şey yaptık. Çok nadiren ayrılmış kısımlarda, örneğin GPU sürücüsünde desteklenmeyen ya da GPU işi başlatmaya değmeyecek kadar küçük görüntü yükleme/indirme ve dönüştürme gibi durumlarda, önce C ile yazıp istenen kodun üretilmesi için hizalama veya izin verilen pointer aliasing gibi derleyici açıklamaları kullandık
      GCC ve Clang’in ikisi de bazı vektör uzantılarını destekliyor; böylece dağınık load/store, shuffle, tek bir register’daki eleman maskeleme gibi “saf” C ile hem insan tarafından okunabilir hem de derleyici sürümleri arasında her zaman beklenen kodu üretecek şekilde açıkça ifade edilmesi zor şeyleri bir ölçüde taşınabilir biçimde uygulayabiliyorsunuz
      Ama başka derleyicileri ve platformları da desteklemek gerektiği için, gerçek build’de sonunda o kaynak dosyadan üretilen assembly’yi alıp kullanıyoruz
    • Karşı örnek olarak, derleyicilerin iyi otomatik vektörleştiremediği basit durumlarla düzenli olarak karşılaşıyorum: https://gcc.godbolt.org/z/rjEqzf1hh
      Bu, işaretsiz bayt doygun toplaması. x86-64 ve ARM64’ün ikisinde de PADDUSB ve UQADD.16B adlı tek bir komutla doğrudan destekleniyor
      Ama tüm derleyiciler sezgisel açıklamadan kötü sonuç çıkarıyor; ya vektörleştirmeyi başaramıyor ya da gerekenden büyük ve yavaş vektörleştirilmiş kod üretiyor
      Üstelik bu temel ve basit bir vektörleştirme ilkel işlemi. Yuvarlamalı daraltan doygun sağa kaydırma (UQRSHRN) gibi daha karmaşık komutları derleyiciye kullandırmak ise zor ya da neredeyse imkânsız
    • Sorun, derleyici çıktısını inceleyip beklentiyle karşılaştırmak zorunda olmanız. Kendi yazacağınız kodla eşleşene kadar biraz kurcalamanız gerekebilir
      Genellikle doğrudan yazmak daha hızlı oluyor
    • Benim deneyimime göre otomatik vektörleştirme, her türlü koşulda sessizce başarısız olabilen kırılgan bir optimizasyon; bu yüzden ona güvenmek istemem
    • Genel durumda derleyicinin 16 bayttan daha geniş pshufb üretmesini nasıl sağlayacağımı bilmiyorum
      16 bayt genişliğinde bile pshufb’nin gerçek tanımını yazarsanız pshufb elde etmiyorsunuz; tanımsız davranış içeren sürümü yazarsanız pshufb çıkıyor
  • Bu dersin yazarı benim
    İstediğinizi sorabilirsiniz

    • Bir ARM Mac kullanıcısı olarak merak ediyorum: böyle optimizasyon kodlarının tüm platformlarda aynı şekilde çalışmasını sağlamak ne kadar emek istiyor? Çok kapsamlı testler ve alternatif algoritmalar olduğunu tahmin ediyorum
      Bu kadar assembly varsa FFmpeg’in Mac’imde çalışması bana mucize gibi görünüyor. Elle mi port edildi?
    • Assembly’yi zar zor okuyabilen, ama bir fikri assembly’ye parçalayarak yazma hissi henüz olmayan biri olarak bir sorum var. Bunu öğrenmek veya geliştirmek için önerebileceğiniz bir yol var mı?
      Hangi noktada “bu, assembly ile yazılırsa hızlanabilir” diye fark edildiğini merak ediyorum. Assembly’ye çevrilirse gerçekten hızlı olacak bir işlev bulduysanız nasıl yazıyorsunuz? Derleyicinin assembly’ye çevirdiği çıktıyı alıp onun üzerinden mi başlıyorsunuz, yoksa sıfırdan mı yazıyorsunuz? Bunun önemi var mı?
    • ARM SVE veya RISC-V V uzantısı gibi değişken genişlikli SIMD komut setleri hakkında ne düşünüyorsunuz, merak ediyorum
      Geliştirici kullanılabilirliği ve kod performansı geleneksel SIMD ile nasıl karşılaştırılıyor? Programlanması gereken SIMD komut seti türlerinin azaldığı bir dünyaya mı gidiyoruz?
    • FFmpeg, Windows’ta assembly işlevleri için SEH tablolarını nasıl üretiyor? Bunu x86asm.inc mi hallediyor, yoksa umursanmıyor mu, merak ediyorum
    • 90’larda x86 optimizasyon kodunu profesyonel olarak yazmış biri olarak soruyorum: 2025’te hâlâ elle yapmak mı gerekiyor?
      Sadece testleri yazıp bir LLM’in 10.000 algoritma denemesine ve sonuçları profil çıkarmasına izin veremez miyiz?
      Yoksa 10.000 rastgele seed verilse bile LLM’in optimum çözümü bulması zor mu?
      Sadece meraktan soruyorum. x86’ı elle optimize etmek kolay değil. Düşünmek için tüm register’ları kafanızda yerli yerine oturtup kombinasyonları tartmanız, her komut kombinasyonunun ne kadar süreceğini de bilmeniz gerekiyor. Ayrıca bazı komutlarda insanın hesaba katması zor tuhaf istisna durumları var; bu yüzden çok daha uzun sürebiliyor ya da çok daha hızlı olabiliyor
  • Bunu gerçekten denemiş olanlara merak ediyorum. Assembly öğrenmenin ya da uygulamanın LISP veya RISC-V gibi bir keyfi var mı, yoksa belirli sistem işleri yüzünden COBOL öğrenmek gibi, başka bir şey yapmak için mi öğreniliyor?
    Her zaman ilgimi çekti ama günlük işimde derinlemesine girmek için belirgin bir nedenim yok. Sırf eğlence için zaman ayırmaya değer mi merak ediyorum

    • Daha fazlasını öğrenmek istediğim için bu öğreticinin ilk 27 bölümünü yaptım ve gerçekten keyif aldım: https://mariokartwii.com/armv8/
      Artık assembly kodlamayı epey seviyorum. Öğreticiden sonra, C’den çağrılabilen bir dizi kütüphanesi yapmam dışında pek bir şey yapmadım
      Bu seviyede geriye çok az sihir kalıyor; sanırım eğlenceli olan da bu. Gerçekten ne olması gerektiğini tam olarak söylüyorsunuz ve gördüğünüz şey çoğunlukla doğrudan sonuca dönüşüyor
      Linking’i de çok daha iyi anladım; üst düzeyde bildiğim ama ayrıntıları bulanık kalan şeyleri kavramama da yardımcı oldu
      Şimdi bu FFmpeg öğreticisine de bakmak istiyorum. Çünkü bu ARM değil, x86
    • Tek bir assembly dili bile öğrenmek çok tatmin edici. Çünkü gerçek programlamanın en ilkel biçimiyle temas kurmanızı sağlar
      Turing makinesi veya lambda hesabı gibi daha basit teorik modeller de var, ama programcıların gerçekten uğraştığı mimarilerde bir ölçüde hoşgörülü özellikler bulunuyor
      Korkulacak bir şey değil. Assembly karmaşık olmaktan çok uzun uzadıyadır. Yaptığınız her işte sayısız load ve store, load ve store tekrar eder
      Biraz makro ve derleme zamanı denetimi eklerseniz ya da “assembly parçaları çalıştıran” bir yorumlayıcıyı etkileşimli geliştirme ve betik yazmayı mümkün kılan bir Forth sistemi bağlamına koyarsanız, C’den de çok uzak olmaz ve derleyicinin sihrini ortadan kaldırır
      Retro bir yaklaşımla başlamayı da öneririm. Bir emülatör içindeki 8 bitlik makine, çalışma modelini küçük ve iyi belgelenmiş bir alana hapseder; kısıtlar sayesinde daha çok işi assembly ile yapmayı düşünmek değerli hale gelir. 32 bit sonrası mimarilerde kaynaklar bol olduğunda bu pek sık olmaz
      İş olarak assembly geliştirenlerin daha spesifik tercihleri olacaktır, ama yeni başlayanlar için en gerekli şey iyi dokümantasyon ve örnekleri olan bir ortamdır. Rosetta Code’da öğrenme için kullanılabilecek iyi assembly örnekleri var
    • İlginç yanlarından biri, sandığınızdan daha yüksek seviyeli olması. Çünkü gerçek çipler dal tahmini ve pipelining gibi işler yapıyor; insan bunları ancak çok az kontrol edebiliyor
      Üniversitedeki bir derste, belirli bir iş için en yüksek performanslı assembly programını kimin yazacağını yarıştığımızı hatırlıyorum. Herkes en iyi performansı sıkıştırıp çıkarmak ve kötü dal tahmininden kaçınmak için çeşitli loop unrolling varyasyonları deniyordu
      Teslimden önceki gece Ballmer Peak’e ulaştım mı bilmiyorum, ama başkalarının çoğunun kaçırdığı bir düzenlemeyi deneyerek kıl payı kazandım
      Ayrıca https://github.com/chrislgarry/Apollo-11’e bakıp “Bu Unix sistemiymiş, ben bunu biliyorum!” diye şaka yapabilmenin müthiş bir keyfi de var. İnsanlığı Ay’a götüren dili okuyabilmenin şaşkınlığı hiç kaybolmuyor
      Kısa cevap: evet
    • Assembly öğrenmek benim için derin anlam taşıyordu. 30 yıllık kodlama hayatımda fiilen hiç kullanmadım, ama transistörden yüksek seviyeli programlamaya kadar resmi tamamladı
      Transistörlerin, mantık kapılarının, CPU mimarisinin ve yüksek seviyeli programlamanın nasıl birbirine geçtiğini anladığınız an, işinizde assembly hiç kullanmayacak olsanız bile harcanan emeğe değer
    • Son yaklaşık 25 yıldır, eğlenceli olduğu için assembly’ye derinlemesine dalmış durumdayım
      Ara sıra işe de yarıyor, ama asıl keyif son bayta kadar her şeyi yerine oturtmaktan, onlarca yıldır kimsenin bakmadığı ikili dosyaları kurcalamaktan ya da eskiden imkânsız olan emülatörler yapmaktan geliyor
      İlk başladığım zamanki gibi hâlâ sihir hissettiren birkaç alandan biri
  • Kişisel olarak intrinsic’ler yerine doğrudan assembly yazmanın çok büyük bir değeri olduğunu düşünmüyorum, ama assembly okumak gerçekten çok faydalı oldu
    Compiler Explorer’ı (https://godbolt.org/) sık kullanarak üretilen assembly’ye baktım ve derleyicinin performans optimizasyonu sırasında yaptığı optimizasyonları anladım

    • Bu söylediğin yazının içeriğiyle doğrudan çelişiyor
      “Multimedya işlemesini hızlandırmak içindir. Assembly kodu yazınca 10 kat veya daha fazla hız artışı elde etmek çok yaygındır; videoyu takılmadan gerçek zamanlı oynatmaya çalışırken bu özellikle önemlidir”
  • K&R referansını görmek hoşuma gitti. C’yi ve genel olarak programlamayı öğrenmek için aldığım kitaptı
    Başta ilk dilim olarak C++ denemiştim, ama içeride neler olup bittiğini sürekli merak ettiğim için fazla soyut gelmişti ve öğrenmesi zordu

  • Bu kaynak tam kararında. 386 döneminin x86 assembly’sini biliyordum, ama daha gelişmiş işlemcilerde fazla karmaşık gelmişti
    Modern CPU’lardaki SIMD hakkında daha fazla şey öğrenmek istiyordum; harika bir kaynak gibi görünüyor

  • q sonekinin işaretçi boyutunu gösterdiğine dikkat edin *(*yani C’de 64 bit sistemde *sizeof(*src) == 8 anlamına gelir ve x86asm, 32 bit sistemde 32 bit kullanacak kadar akıllıdır) ancak gerçek load 128 bittir” cümlesi kafa karıştırıcı
    i.e bence i.e., olmalı ve *(* ne anlama geliyor? Sadece açılış parantezi olması gerekmiyor mu?
    Hangi bağlamda *sizeof(*src) geçerli sayılabilir onu da bilmiyorum. Bildiğim kadarıyla sizeof bir işaretçi üretmez
    Cümlenin içine rastgele yıldızlar serpiştirilmiş gibi ya da italik anlamındaki yıldızlarla C sözdizimini karıştırmaya çalışırken bozulmuş gibi duruyor

    • Evet. Markdown’ın kendisinde ya da özgün materyali Markdown’a dönüştürme sürecinde bir şeyler ters gitmiş gibi görünüyor
    • İlk iki yıldız sanki bir dipnot çifti gibi kullanılmış
    • İşaretçinin boyutunu döndürmüyor mu? Sadece mimari farklarını ele almak için kullanılmış gibi
  • Bölme biçimi umurumda değil; sadece bu rehberin gerçekten iyi olduğunu söylemek istiyorum
    Eskiden çok düşük seviye konularla ilgilenirken böyle bir kaynak olsaydı keşke

  • Assembly’nin C’den 10 kat hızlı olduğu söyleniyor, öyle mi? Bir noktada kesinlikle doğru olmuş olabilir, ama hâlâ öyle mi? Derleyiciler gerçekten elle yazılmış assembly’ye yaklaşamayacak kadar yerinde mi saydı?

    • Intrinsic kullanan C, saf assembly performansına çok yaklaşabilir
      FFmpeg geliştiricileri intrinsic’lere karşı biraz kötü şöhretli biçimde mesafeli duruyor; yanlış hatırlamıyorsam, eşdeğer assembly kadar iyi performans verse bile kod tabanında buna izin vermiyorlar. Yine de yazının kendi tahminine göre bile intrinsic ile assembly arasındaki fark kabaca %10-15 düzeyinde
      Titizlikle optimize edilmiş assembly ile safça yazılmış C’yi karşılaştırırsanız, vektörleştirmenin mümkün olduğu ama derleyicinin bundan yararlanamadığı durumlarda 10 kat fark görebilirsiniz. Bu yaygın bir durum; otomatik vektörleştirme, basit örneklerin ötesine geçince hâlâ genel olarak pek iyi değil
      Ancak bir uzmanın yazdığı kodun safça yazılmış kodu ezip geçmesi şaşırtıcı değil
    • Bu, derleyicilerin yerinde saymasıyla ilgili bir sorun değil. Derleyici, assembly yazarının tasarımda yararlandığı bilgileri bilemez
      Daha basit söylemek gerekirse, bir C derleyicisi yalnızca sıradan bir C uygulamasına bakarak belirli bir matematiğin daha verimli SIMD intrinsic’leriyle ifade edilmek istendiğini çıkaramaz. Çünkü yazarın yapmaya çalıştığı matematiksel niyete erişimi yoktur
      Hedefe özgü değerlendirmeler de var. Derleyici kaçınılmaz olarak genel amaçlı bir derleyicidir. Kaynaklar, örneğin register tahsisi gibi problemler NP-tamdır ve sırt çantası problemine denktir. Çok az kişi derleyicinin mutlak anlamda en iyi assembly üretimini bulmak için saatler harcamasını ister. Bu optimumluğun statik olarak bilinip bilinemeyeceği de ayrı bir konu
    • Bu, CPU’yu tamamen kullanmak için mümkün olan her türlü numarayı kullanan yüksek derecede vektörleştirilmiş kod ile ilgili
      Derleyiciler genel kodda akıllıdır, ama codec’ler genel kod değildir. FFmpeg programcısı değilim, ancak sesle çalışmış bir geçmişim var
    • C derleyicileri otomatik vektörleştirme konusunda hâlâ oldukça zayıf
      SIMD’nin uygulanabildiği problemlerde, saf skaler uygulamaya göre 2 kattan 16 kata kadar hızlanmayı makul biçimde bekleyebilirsiniz
    • Muhtemelen çok niş şeyler vardır
      Ben C’den 10 kat daha iyi assembly yazamam, ama kimsenin yazamayacağını da varsaymam