- FFmpeg का पहला असेंबली लेक्चर मल्टीमीडिया प्रोसेसिंग में हाथ से लिखी SIMD assembly की जरूरत क्यों होती है और FFmpeg-स्टाइल function लिखने की conventions को introductory level पर समझाता है
- लक्षित पाठकों को C pointers और हाई-स्कूल स्तर के scalar/vector, addition/multiplication concepts पता होने चाहिए; लेक्चर x86 64-bit और Intel syntax के आधार पर चलता है
- FFmpeg में assembly function, SIMD, vectorise लगभग एक ही अर्थ में इस्तेमाल होते हैं, और कई data elements को एक साथ process करने का तरीका image/video/audio processing के लिए अच्छा fit है
- Performance के लिहाज से manual assembly को prefer किया जाता है; intrinsics आमतौर पर 10–15% धीमे होते हैं, और dav1d में auto-vectorization के लगभग 2x बनाम manually written version के 8x तक पहुंचने के examples की तुलना की गई है
- पहला example function
x86inc.asm, INIT_XMM sse2, cglobal, movu, paddb, RET का इस्तेमाल करके दो uint8_t buffers के 16-byte values को SIMD से add करता है और फिर उन्हें पहले buffer में store करता है
लेक्चर का उद्देश्य और आवश्यक पूर्व-ज्ञान
- FFmpeg Assembly Language Lesson One FFmpeg में assembly language किस तरह लिखी जाती है, इसकी basics को cover करता है और computer के अंदर असल में क्या होता है, इसे समझने में मदद करने वाला introductory lecture है
- आवश्यक ज्ञान इस प्रकार है
- C language, खासकर pointers
- अगर C नहीं जानते, तो The C Programming Language पढ़ने की सलाह दी जाती है
- हाई-स्कूल math level के scalar और vector, addition, multiplication concepts
असेंबली भाषा और SIMD
- Assembly language एक programming language है जिसमें CPU द्वारा process किए जाने वाले instructions के सीधे equivalent code को human-readable तरीके से लिखा जाता है
- Human-readable assembly code assembler से होकर CPU को समझ आने वाले machine code binary data में बदलता है
- FFmpeg का assembly code ज्यादातर SIMD(Single Instruction Multiple Data) form में होता है
- एक instruction कई data elements पर एक साथ काम करता है
- इसे vector programming भी कहा जाता है
- सामान्य scalar programming एक बार में एक data element process करती है
- SIMD memory में sequentially रखे गए बहुत सारे data को handle करने वाली image, video, audio processing के लिए अच्छी तरह suited है
- FFmpeg में ये expressions लगभग एक ही अर्थ में इस्तेमाल होते हैं
assembly function
SIMD
vectorise
- इसका मतलब है हाथ से assembly function लिखकर कई data elements को एक साथ process करना
FFmpeg सीधे assembly क्यों लिखता है
- मुख्य उद्देश्य multimedia processing speed बढ़ाना है
- Assembly code लिखने से 10x से ज्यादा speed improvement आम है
- Real-time video playback में stutter कम करने के लिए यह अहम है
- Energy use घटा सकता है और battery life बढ़ा सकता है
- Video encoding और decoding functions end users और datacenters में बहुत ज्यादा इस्तेमाल होते हैं, इसलिए छोटे improvements भी जल्दी accumulate हो जाते हैं
- FFmpeg intrinsics की जगह हाथ से लिखी assembly इस्तेमाल करता है
- Intrinsics assembly instructions से match करने वाले C-like functions होते हैं
- ये आमतौर पर हाथ से लिखी assembly से 10–15% धीमे होते हैं
- यह figure compiler के हिसाब से बदल सकता है, और intrinsics समर्थक इससे सहमत न हों
- Hungarian Notation के इस्तेमाल की वजह से इन्हें पढ़ना मुश्किल लगता है, ऐसी राय भी है
- inline assembly FFmpeg के कुछ पुराने code या Linux Kernel जैसे projects में बची हो सकती है
- इसमें अलग file के बजाय C code के अंदर ही assembly सीधे लिखी जाती है
- FFmpeg जैसे projects में इसे पढ़ना मुश्किल, compiler support कम व्यापक और maintain करना कठिन माना जाता है
- यह राय कि compiler auto-vectorization ही काफी है, learning purpose के लिए ignore करने की सलाह दी जाती है
- dav1d project के हालिया tests में auto-vectorization ने लगभग 2x speed improvement दिखाया
- हाथ से लिखा version 8x तक पहुंच सकता था
Syntax scope और reference materials
- लेक्चर x86 64-bit assembly पर केंद्रित है
- इसे amd64 भी कहा जाता है और यह Intel CPU पर भी चलता है
- ARM, RISC-V जैसे दूसरे CPUs के लिए assembly आगे expand होने की संभावना रखती है
- x86 assembly syntax में AT&T और Intel होते हैं
- AT&T syntax ज्यादा पुराना है और Intel syntax की तुलना में पढ़ने में कठिन माना जाता है
- लेक्चर Intel syntax का इस्तेमाल करता है
- सामान्य books या Stack Overflow जैसे online materials FFmpeg assembly reference के लिए खास उपयोगी न हों
- क्योंकि यह हाथ से लिखी Intel syntax assembly इस्तेमाल करता है
- कई online materials operating system programming, hardware programming, non-SIMD code पर focus करते हैं
- FFmpeg assembly high-performance image processing के लिए specialized एक अनोखा approach है
- The Art of 64-bit assembly के बाद के हिस्से में SIMD instructions और उनके behaviour को visualize करने वाले diagrams मददगार हो सकते हैं
- सवालों के लिए Discord server उपलब्ध है
Registers के basic concepts
- Register CPU के अंदर data process करने वाला area है
- CPU memory पर सीधे operation नहीं करता; data को registers में load करता है, फिर process करता है और वापस memory में लिखता है
- Assembly में आम तौर पर एक memory location से दूसरी memory location में data सीधे copy नहीं किया जा सकता; पहले register से होकर जाना पड़ता है
General-purpose registers
- GPR(General Purpose Register) ऐसा general-purpose register है जो data या memory address रख सकता है
- यहां यह अधिकतम 64-bit value रख सकता है
- Pointer भी रख सकता है
- Addition, multiplication, shift जैसे operations possible हैं
- कई assembly books GPR की details और historical background पर लंबी चर्चा करती हैं
- FFmpeg के assembly code में GPR मुख्य रूप से scaffolding की तरह इस्तेमाल होता है, और complexity का बड़ा हिस्सा जरूरी नहीं होता या abstracted होता है
Vector registers और data size
- Vector register कई data elements रखता है
- x86 के मुख्य vector registers ये हैं
mm: MMX register, 64-bit size, historical और अब ज्यादा इस्तेमाल नहीं होता
xmm: XMM register, 128-bit size, व्यापक रूप से available
ymm: YMM register, 256-bit size, इस्तेमाल करते समय कुछ complexity होती है
zmm: ZMM register, 512-bit size, availability सीमित है
- Video compression और decompression की ज्यादातर calculations integer-based होती हैं, इसलिए लेक्चर भी integers पर focus करता है
- एक
xmm register 128-bit को इस तरह interpret किया जा सकता है
- 16 bytes, प्रत्येक 8-bit
- 8 words, प्रत्येक 16-bit
- 4 doublewords, प्रत्येक 32-bit
- 2 quadwords, प्रत्येक 64-bit
- Abbreviations आगे महत्वपूर्ण रूप से इस्तेमाल होंगे
- byte: 8-bit data
- word: 16-bit data
- doubleword: 32-bit data
- quadword: 64-bit data
- double quadword: 128-bit data
x86inc.asm की भूमिका
x86inc.asm FFmpeg, x264, dav1d में इस्तेमाल होने वाली lightweight abstraction layer है
- यह assembly programmers को code आसानी से लिखने में मदद करने वाले कई features देती है
- शुरुआत में महत्वपूर्ण features में से एक GPRs को
r0, r1, r2 जैसे labels देना है
- असली register names याद रखने की जरूरत नहीं पड़ती
- FFmpeg में GPR मुख्य रूप से scaffolding role निभाते हैं, इसलिए लिखने का बोझ कम होता है
सरल scalar asm example
mov r0q, 3
inc r0q
dec r0q
imul r0q, 5
- पहली line immediate value
3 को r0 register में quadword के रूप में store करती है
- Immediate value memory से लाई गई value नहीं, बल्कि assembly code में ही stored value होती है
- Intel syntax में दाईं ओर का source operand बाईं ओर के destination operand में जाता है
- इसे
r0q = 3 की तरह पढ़ सकते हैं
- यह
memcpy के behaviour जैसे order में है
r0q का q suffix बताता है कि register quadword के रूप में इस्तेमाल हो रहा है
- आगे का behaviour इस प्रकार है
inc से value 4 हो जाती है
dec से value फिर 3 हो जाती है
imul से 5 से multiply होकर अंत में r0q 15 हो जाता है
mov, inc जैसे human-readable instructions को mnemonic कहा जाता है
- Assembler इन्हें machine code में बदलता है
- Uppercase
MOV, INC और lowercase mov, inc समान हैं
- FFmpeg में mnemonics lowercase में लिखे जाते हैं, और uppercase macros के लिए छोड़े जाते हैं
पहला SIMD function example
%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
- यह function
src और src2 के data को SIMD से add करता है और result को फिर src location पर store करता है
%include "x86inc.asm" x264, FFmpeg, dav1d community द्वारा विकसित helpers, predefined names और macros को include करता है
SECTION .text executable code वाली section को दर्शाता है
- Constant data को
.data section में रखा जा सकता है
;static void add_values(uint8_t *src, const uint8_t * src2) C function argument form दिखाने वाली comment है
- Assembly में semicolon
; C के // की तरह comment का काम करता है
INIT_XMM sse2 XMM registers और sse2 instruction set इस्तेमाल करने को set करता है
- क्योंकि
paddb sse2 instruction है
cglobal add_values, 2, 2, 2, src, src2 C function add_values define करता है
- Function arguments 2 हैं
- Function में इस्तेमाल होने वाले GPRs arguments सहित 2 हैं
- इस्तेमाल होने वाले XMM registers की संख्या 2 है
- आखिरी दो items function argument labels
src, src2 हैं
- पुराना code argument labels के बिना
r0, r1 जैसे GPRs सीधे इस्तेमाल कर सकता है
load, packed add, store
movu m0, [srcq]
movu m1, [src2q]
movu movdqu का short form है और इसका मतलब move double quad unaligned है
- Alignment आगे के lectures में cover होगा; यहां इसे
[srcq] से 128-bit move करने वाला instruction समझ सकते हैं
mov में square brackets का मतलब address को dereference करना है
- C में
*src जैसा concept है
- यह operation load है
q suffix pointer size दिखाता है
- 64-bit system में यह C pointer size, यानी 8 bytes को दर्शाता है
- x86asm 32-bit system में 32-bit इस्तेमाल करता है
- असली load 128-bit है
- Vector registers को
xmm0 जैसे full name के बजाय abstracted m0 से refer किया जाता है
- आगे के lectures में यह उसी code को कई SIMD register sizes के लिए adaptable बनाने के तरीके से जुड़ता है
paddb m0, m1
paddb हर register के byte elements को आपस में add करता है
p prefix का मतलब packed है, और यह vector instructions तथा scalar instructions को अलग बताने के लिए इस्तेमाल होता है
b suffix byte-level addition दिखाता है
- 16 bytes वाले दो registers add करने पर हर position के corresponding elements, जैसे
a+q, b+r, c+s, add होते हैं
movu [srcq], m0
RET
movu [srcq], m0 result data को srcq pointer जिस address की ओर point करता है, वहां वापस लिखता है
RET function return होने को दर्शाने वाला macro है
- FFmpeg के लगभग सभी assembly functions value return करने के बजाय arguments के रूप में मिले data को modify करते हैं
- Assignments में available assembly functions के लिए function pointer बनाने और उसका इस्तेमाल करने का तरीका आगे आएगा
1 टिप्पणियां
Hacker News की राय
इसी विषय पर एक और संसाधन: https://blogs.gnome.org/rbultje/2017/07/14/writing-x86-simd-...
यहां टिप्पणियों को देखें तो हाथ से लिखे SIMD की उपयोगिता पर राय “पूरी तरह अस्पष्ट” से लेकर “mission critical” तक बंटी है, और “पूरी तरह अस्पष्ट” वाला पक्ष ज़्यादा दिखता है, इसलिए mission critical पक्ष को थोड़ा समझाता हूं
FFmpeg अपनी usage frequency की वजह से एक स्पष्ट उदाहरण है, लेकिन मुझे लगता है कि general-purpose production AV1 decoder dav1d हाथ से लिखे SIMD के असर को quantify करने के लिए ज़्यादा आसान उदाहरण है
dav1d बड़े browsers से लेकर Android operating system तक लगभग everywhere इस्तेमाल होता है और इसने libgav1 की जगह ली है; इसकी सफलता की बड़ी वजह इसकी जबरदस्त speed है, जो इसलिए संभव है क्योंकि codebase का बड़ा हिस्सा हाथ से लिखा SIMD है
Zig जैसी भाषाओं में built-in SIMD support होना अच्छी बात है, लेकिन कुछ use cases में, जहां संभावित performance difference जांचने लायक हो, सीधे हाथ से लिखना ज़रूरी हो जाता है। dav1d की कुछ code lines दिन में खरबों बार execute होती हैं, इसलिए उन्हें जितना हो सके तेज़ होना चाहिए, और हाथ से लिखे SIMD और compiler-generated SIMD के बीच फर्क कुछ मामलों में 50% तक हो सकता है, इसलिए यह महत्वपूर्ण है
मैं भी कुछ हद तक ऐसे code पर काम से जुड़ा हूं जो इसी तरह बहुत ज़्यादा execute होगा, इसलिए मुझे लगता है कि अगर ऐसी तकनीकें गायब न हों तो FFmpeg Assembly Language School जैसे संसाधन काफी महत्वपूर्ण हैं
हर method का calling convention अलग भी हो सकता है, इसलिए सामान्य platform calling convention का पालन करने वाले compiler द्वारा generate किए गए code की तुलना में stack stores और loads बहुत कम होते हैं
उदाहरण के लिए, हमारा नया matrix multiplication LLM inference के लिए मशहूर libraries से तेज़ है, और ऐसा तब भी होता है जब सामने वाला AMX इस्तेमाल करता है और हम AVX512BF16 इस्तेमाल करते हैं। वजह शायद threading bottleneck हो सकती है या कुछ और भी, लेकिन JIT बीच में हो तो समझना मुश्किल हो जाता है
अगर हमें platform-specific kernels खुद लिखने पड़ते, तो यह नतीजा नहीं आता। दिन में समय सीमित होता है, और Highway से single implementation लिखने की वजह से हम नए kernel types और block sizes के साथ-साथ parallelization strategies और उनके parameters चुनने वाले auto-tuner सहित ज़्यादा व्यापक design space explore कर पाए
दूसरे चरण में कुछ हिस्सों को हाथ से tune किया जा सकता है, लेकिन register allocation और calling convention को micro-optimize करने से पहले मैं चाहूंगा कि पहले व्यापक exploration हो
मुझे Zig पसंद है, लेकिन मेरी समझ है कि ज्यादातर मामलों में C/C++ की तरह CPU-specific built-in functions का इस्तेमाल करना पड़ता है
GCC और Clang
vector_sizeattribute और उस “vectorized” type के arithmetic operator overloads को support करते हैं, और इसके अलावा भी बहुत कुछ है। वास्तव में_mm256_mul_psजैसे intrinsics भी#define _mm256_mul_ps(a,b) (__m256)((v8sf)(a) * (v8sf)(b))जैसे implement होते हैंइन features की उपयोगिता Zig में संभव चीज़ों से कहीं ज़्यादा है
encoder side में खास तौर पर अक्सर problem को इस तरह “structure” करना पड़ता है कि loops और loads को जल्दी हटाया जा सके, लेकिन compiler उस तरह का auto-vectorized code generate नहीं कर पाता
पहले हम core functions के SIMD versions काफी बनाते थे, लेकिन अब लगभग नहीं करते। आज़माने लायक तरीका है उस code को अलग करके शानदार Compiler Explorer [0] में चलाना
फिर generated code को देखना
आजकल auto-vectorization अक्सर function के SIMD version काफी अच्छे से बना देती है, और कई बार compiler को बस “hint” देना होता है। जैसे alignment को स्पष्ट करना, या vector source/target types सीधे देना
compiler क्या कर सकता है, यह सोचते हुए C code को “style” करें तो बहुत कुछ किया जा सकता है। ज्यादा intermediate variables इस्तेमाल करना, और जिस operation को चाहते हैं उसे वाकई छोटे-छोटे हिस्सों में तोड़ना
सबसे खराब स्थिति में भी, अगर compiler पर्याप्त smart नहीं है, तो generated assembly के आधार पर boilerplate खुद लिखे बिना उसमें बदलाव कर सकते हैं
ज्यादातर मामलों में resulting C function मेरे हाथ से लिखे version जितना या उससे बेहतर vectorize होता है, और कई अन्य मामलों में भी “काफी करीब” होता है, इसलिए बड़ा फर्क नहीं पड़ता। साथ ही उस code के explicit versions के बिना भी WASM या NEON वगैरह में अच्छे से vectorize होने की संभावना रहती है
[0] https://godbolt.org/
GCC और Clang दोनों कुछ vector extensions support करते हैं, जिससे scattered loads/stores, shuffles, और single register के elements masking जैसी चीजें कुछ हद तक portable तरीके से implement की जा सकती हैं—ऐसी चीजें जिन्हें “pure” C में इंसान के लिए readable और compiler versions के बीच हमेशा expected code बनाने लायक स्पष्ट रूप से express करना मुश्किल है
लेकिन दूसरे compilers और platforms भी support करने थे, इसलिए actual build में अंततः उस source file से generated assembly लेकर इस्तेमाल करनी पड़ती है
यह unsigned byte saturating addition है। x86-64 और ARM64 दोनों में यह PADDUSB और UQADD.16B नाम के single instruction से सीधे supported है
लेकिन सभी compilers intuitive description से गड़बड़ कर देते हैं, या तो vectorize करने में fail होते हैं या जरूरत से ज्यादा बड़ा और slow vectorized code generate करते हैं
यह इतना basic और simple vectorization primitive operation होने के बावजूद ऐसा है। rounding narrowing saturating right shift (UQRSHRN) जैसे ज्यादा complex instruction का compiler से इस्तेमाल कराना मुश्किल या लगभग असंभव है
आम तौर पर सीधे खुद लिखना ज्यादा तेज होता है
pshufbgenerate कराने का तरीका मुझे नहीं पता16-byte width के case में भी, अगर
pshufbकी actual definition इस्तेमाल करें तोpshufbनहीं मिलता, और undefined behavior वाली version इस्तेमाल करें तोpshufbनिकलता हैइस lecture का author मैं हूँ
कुछ भी पूछ सकते हैं
अगर assembly इतनी ज्यादा है, तो FFmpeg का मेरे Mac पर चलना किसी चमत्कार जैसा लगता है। क्या इसे हाथ से port किया गया है?
किस point पर यह अहसास होता है कि “यह assembly लिखने पर तेज हो सकता है”? अगर कोई ऐसा function मिल जाए जो assembly में बनाने पर सचमुच तेज होगा, तो उसे लिखते कैसे हैं? क्या compiler द्वारा assembly में बदले output को लेकर शुरुआत करते हैं, या शुरू से लिखते हैं? क्या इससे फर्क पड़ता है?
developer usability और code performance पारंपरिक SIMD से कैसे compare होते हैं? क्या हम ऐसी दुनिया की ओर जा रहे हैं जहाँ program करने के लिए SIMD instruction sets की variety घटेगी?
x86asm.incइसे handle करता है, या इसकी परवाह नहीं की जाती?क्या सिर्फ tests लिखकर LLM से 10,000 algorithms try करवाकर results profile नहीं करवा सकते?
या 10,000 random seeds देने पर भी LLM के लिए optimum ढूँढना मुश्किल है?
बस जिज्ञासा में पूछ रहा हूँ। x86 को हाथ से optimize करना आसान नहीं है। सोचने के लिए सारे registers को दिमाग में फिट करके combinations देखने पड़ते हैं, और यह भी जानना पड़ता है कि हर instruction combination में कितना समय लगेगा। साथ ही कुछ instructions में अजीब edge cases होते हैं जिन्हें इंसान के लिए ध्यान में रखना मुश्किल है, जिनसे वे बहुत ज्यादा time ले सकते हैं या बहुत तेज हो सकते हैं
जिन्होंने इसे सच में किया है, उनसे जानना चाहता हूँ। असेंबली सीखने या implement करने में LISP या RISC-V जैसी कोई मज़ेदार बात है, या यह COBOL सीखने की तरह है—किसी खास system task की वजह से, कुछ और करने के लिए सीखी जाने वाली चीज़?
दिलचस्पी हमेशा रही है, लेकिन रोज़मर्रा के काम में इसमें गहराई से जाने की कोई खास वजह नहीं मिलती। सोच रहा हूँ कि क्या मज़े के लिए इसमें समय लगाना worth it है
अब मुझे assembly coding काफी पसंद है। tutorial के बाद मैंने ज्यादा कुछ नहीं किया, बस एक array library बनाई जिसे C से call किया जा सकता है
मुझे लगता है इस level पर बची हुई magic बहुत कम होती है, इसलिए यह मज़ेदार है। आप सच में ठीक-ठीक बताते हैं कि क्या होना चाहिए, और जो दिखता है वही आम तौर पर result बनता है
linking भी अब बहुत बेहतर समझ में आती है, और जिन चीज़ों को high level पर जानता था लेकिन details धुंधली थीं, उन्हें समझने में भी मदद मिली
अब यह FFmpeg tutorial भी देखना चाहता हूँ। यह ARM नहीं बल्कि x86 है, इसलिए
Turing machine या lambda calculus जैसे और भी सरल theoretical models हैं, लेकिन programmers जिन architectures के साथ सच में काम करते हैं, उनमें कुछ हद तक उदारता होती है
इससे डरने की जरूरत नहीं। assembly जटिल से ज्यादा लंबी-चौड़ी होती है। हर काम में load और store, load और store, अनगिनत बार दोहराया जाता है
अगर इसमें थोड़े macros और build-time checks जोड़ दें, या इसे किसी Forth system के context में रखें जहाँ “assembly के टुकड़े चलाने” वाला interpreter interactive development और scripting संभव बनाता है, तो यह C से बहुत दूर नहीं रहती और compiler की magic हटा देती है
retro तरीके से approach करना भी recommend करूँगा। emulator के अंदर 8-bit machine आपका working model एक छोटे और अच्छी तरह documented दायरे में सीमित कर देती है, और constraints की वजह से assembly में ज्यादा काम करने की सोच valuable हो जाती है। 32-bit के बाद की architectures में resources भरपूर होने पर ऐसा कम ही होता है
जो लोग काम के तौर पर assembly develop करते हैं, उनकी preferences ज्यादा specific होंगी, लेकिन beginners के लिए अच्छे docs और examples वाला environment सबसे जरूरी है। Rosetta Code में सीखने के लिए अच्छे assembly examples हैं
university class में याद है कि किसी specific task के लिए सबसे fast assembly program कौन बनाता है, इस पर competition हुआ था। सबने best performance निकालने और खराब branch prediction से बचने के लिए loop unrolling के कई variants आज़माए
submission से पिछली रात मैं Ballmer Peak पर पहुँचा था या नहीं, पता नहीं, लेकिन मैंने ऐसा configuration try किया जिसे बाकी ज्यादातर लोग miss कर गए थे और बहुत मामूली अंतर से जीत गया
साथ ही https://github.com/chrislgarry/Apollo-11 देखकर “यह तो Unix system है, मैं इसे जानता हूँ!” जैसा मज़ाक कर पाने में भी जबरदस्त आनंद है। जिस language से मानव चाँद पर गया, उसे पढ़ पाने का आश्चर्य कभी खत्म नहीं होता
छोटा जवाब: हाँ
transistors, logic gates, CPU architecture और high-level programming कैसे आपस में फिट होते हैं—यह समझने का पल, भले ही आप professionally assembly कभी न इस्तेमाल करें, लगाई गई मेहनत के लायक है
कभी-कभी यह उपयोगी भी होती है, लेकिन आखिरी byte तक सब कुछ सही जगह रखना, दशकों से किसी ने न देखी हुई binary में झाँकना, या ऐसे emulators बनाना जो पहले impossible थे—इनसे मिलने वाला आनंद बहुत बड़ा है
यह उन गिने-चुने fields में से है जहाँ मुझे आज भी वैसी ही magic महसूस होती है जैसी शुरुआत में होती थी
निजी तौर पर मुझे intrinsics की जगह सीधे assembly लिखने में बहुत बड़ा value नहीं दिखता, लेकिन assembly पढ़ना सच में मददगार रहा
मैं Compiler Explorer(https://godbolt.org/) अक्सर इस्तेमाल करता हूँ, generated assembly देखता हूँ, और समझता हूँ कि compiler performance optimization के दौरान कौन-से optimizations करता है
“यह multimedia processing को तेज़ करने के लिए है। assembly code लिखने से 10x से ज्यादा speedup मिलना बहुत आम है, और जब आप बिना रुकावट real-time में video play करना चाहते हैं तो यह खास तौर पर महत्वपूर्ण होता है”
K&R reference देखकर अच्छा लगा। C और programming overall सीखने के लिए मैंने वही किताब खरीदी थी
शुरुआत में मैंने C++ को पहली language के रूप में try किया था, लेकिन अंदर क्या हो रहा है यह लगातार जानने की इच्छा के कारण वह बहुत abstract लगा और सीखना मुश्किल लगा
यह resource बिल्कुल सही है। 386 era का x86 assembly मुझे पता था, लेकिन ज्यादा advanced processors पर यह बहुत complex हो गया था
मैं modern CPU की SIMD के बारे में और सीखना चाहता था, और यह बेहतरीन resource लगता है
यह वाक्य confusing है: “ध्यान दें कि
qsuffix pointer size को दर्शाता है *(*यानी C में 64-bit system पर *sizeof(*src) == 8 का मतलब है, और x86asm इतना smart है कि 32-bit system पर 32-bit इस्तेमाल करे) लेकिन actual load 128-bit है”i.eशायदi.e.,होना चाहिए, और*(*का क्या मतलब है? क्या यह बस opening parenthesis नहीं होना चाहिए?मुझे यह भी नहीं पता कि किस context में
*sizeof(*src)valid माना जा सकता है। मेरी जानकारी मेंsizeofpointer produce नहीं करतालगता है उस sentence में randomly asterisks छिड़क दिए गए हैं, या italic के लिए asterisks और C syntax को मिलाने की कोशिश में गड़बड़ हो गई है
splitting method की परवाह नहीं, बस कहना चाहता हूँ कि यह guide सच में अच्छी है
पहले जब मेरी बहुत low-level चीज़ों में रुचि थी, तब काश ऐसा resource होता
Assembly, C से 10 गुना तेज़ है? किसी दौर में यह ज़रूर सच रहा होगा, लेकिन क्या आज भी ऐसा है? क्या compiler सच में इतना ठहर गया है कि हाथ से लिखी assembly के करीब भी नहीं पहुँच पाता?
FFmpeg developers intrinsics के खिलाफ कुछ हद तक कुख्यात रूप से विरोधी माने जाते हैं, और अगर मेरी याद सही है तो performance equivalent assembly जितनी अच्छी होने पर भी वे इसे codebase में स्वीकार नहीं करते। फिर भी लेख के अपने अनुमान के हिसाब से भी intrinsics और assembly के बीच का फर्क लगभग 10–15% के स्तर का है
सावधानी से optimized assembly की तुलना naive C से करें, तो जहां vectorization संभव है लेकिन compiler उसका उपयोग नहीं कर पाता, वहाँ 10x फर्क दिख सकता है। ऐसा होना आम है, क्योंकि automatic vectorization मामूली cases से आगे बढ़ते ही अब भी कुल मिलाकर खास अच्छा नहीं है
हालांकि expert द्वारा लिखा code naive code पर भारी पड़े, यह कोई चौंकाने वाली बात नहीं है
और सरल शब्दों में कहें तो, C compiler सिर्फ साधारण C implementation देखकर यह अनुमान नहीं लगा सकता कि लेखक किसी खास math को अधिक efficient SIMD intrinsics के रूप में व्यक्त करना चाहता है। वजह यह है कि उसे लेखक की mathematical intent तक पहुँच नहीं होती
target-specific considerations भी होते हैं। compiler अनिवार्य रूप से general-purpose compiler होता है। resources, जैसे register allocation जैसी समस्याएँ NP-complete हैं और knapsack problem के बराबर हैं। बहुत कम लोग चाहेंगे कि compiler absolutely optimal assembly generation खोजने में घंटों लगा दे। उस optimality को statically जाना भी जा सकता है या नहीं, यह अलग सवाल है
compiler सामान्य code में smart होते हैं, लेकिन codec सामान्य code नहीं है। मैं FFmpeg programmer नहीं हूँ, लेकिन audio के साथ काम करने का background है
जिन problems पर SIMD लागू हो सकता है, उनमें naive scalar implementation की तुलना में 2x से 16x तक speedup की reasonable उम्मीद की जा सकती है
मैं C से 10x बेहतर assembly नहीं लिख सकता, लेकिन यह मानकर नहीं चलूँगा कि कोई भी ऐसा नहीं कर सकता