GN⁺: FFmpeg 어셈블리 언어 학교
(github.com/FFmpeg)소개
- FFmpeg의 어셈블리 언어 수업에 오신 것을 환영함. 이 수업은 어셈블리 언어가 FFmpeg에서 어떻게 작성되는지에 대한 기초를 제공함.
필요한 지식
- C 언어, 특히 포인터에 대한 지식이 필요함.
- 고등학교 수준의 수학 지식(스칼라와 벡터, 덧셈, 곱셈 등)이 필요함.
어셈블리 언어란?
- 어셈블리 언어는 CPU가 처리하는 명령어에 직접 대응하는 코드를 작성하는 프로그래밍 언어임.
- FFmpeg의 대부분의 어셈블리 코드는 SIMD(Single Instruction Multiple Data)로, 이는 벡터 프로그래밍이라고도 불림.
- SIMD는 이미지, 비디오, 오디오와 같은 순차적으로 메모리에 저장된 많은 데이터를 처리하는 데 적합함.
왜 어셈블리 언어로 작성하는가?
- 멀티미디어 처리 속도를 빠르게 하기 위함. 어셈블리 코드로 작성하면 10배 이상의 속도 향상을 얻을 수 있음.
- FFmpeg에서는 인트린직을 사용하지 않고 직접 어셈블리 코드를 작성함. 인트린직은 보통 수작업 어셈블리보다 10-15% 느림.
어셈블리 언어의 종류
- 이 수업은 x86 64비트 어셈블리 언어에 중점을 둠. 이는 amd64로도 알려져 있으며 Intel CPU에서도 작동함.
- x86 어셈블리 구문에는 AT&T와 Intel 두 가지가 있으며, Intel 구문을 사용할 것임.
지원 자료
- FFmpeg 어셈블리 프로그래밍은 고성능 이미지 처리에 중점을 두고 있으며, 독특한 접근 방식을 가짐.
- "The Art of 64-bit assembly" 책의 다이어그램이 도움이 될 수 있음.
레지스터
- 레지스터는 CPU에서 데이터를 처리하는 영역임. CPU는 메모리를 직접 조작하지 않고, 데이터를 레지스터에 로드하여 처리한 후 메모리에 다시 씀.
범용 레지스터
- 범용 레지스터(GPR)는 데이터나 메모리 주소를 포함할 수 있음. FFmpeg의 어셈블리 코드에서는 GPR가 주로 발판 역할을 함.
벡터 레지스터
- 벡터(SIMD) 레지스터는 여러 데이터 요소를 포함함. 다양한 유형의 벡터 레지스터가 존재함.
- 대부분의 비디오 압축 및 압축 해제 계산은 정수 기반임.
x86inc.asm 포함
- x86inc.asm은 FFmpeg, x264, dav1d에서 어셈블리 프로그래머의 작업을 쉽게 하기 위해 사용되는 경량 추상화 계층임.
간단한 스칼라 어셈블리 코드
- 예제 코드를 통해 스칼라 어셈블리 코드가 어떻게 작동하는지 설명함.
기본 벡터 함수 이해하기
- 첫 번째 SIMD 함수 예제를 통해 각 줄의 의미를 설명함.
-
movu
,paddb
와 같은 명령어를 사용하여 벡터 연산을 수행함. - 함수는 인수의 데이터를 수정하며 값을 반환하지 않음.
Hacker News 의견
-
동일 주제에 대한 또 다른 자료로는 FFmpeg와 dav1d의 사례가 있음
- FFmpeg는 자주 사용되므로 명확한 사용 사례로 볼 수 있음
- dav1d는 주요 브라우저와 안드로이드 운영체제에서 사용되며, 성공의 큰 요소는 손으로 작성된 SIMD 덕분임
- dav1d의 코드 중 일부는 하루에 수조 번 실행되므로 최대한 빠르게 실행되어야 함
- 손으로 작성된 SIMD와 컴파일러가 생성한 SIMD 간의 성능 차이는 최대 50%까지 발생할 수 있음
- 이러한 기술을 유지하기 위해 FFmpeg 어셈블리 언어 학교 같은 자원이 중요함
-
어셈블리 작성보다는 내장 함수를 사용하는 것이 더 가치 있다고 생각하지만, 읽는 것은 매우 유용했음
- Compiler Explorer를 사용하여 컴파일러가 성능 최적화를 위해 수행하는 최적화를 이해함
-
이 가이드가 매우 훌륭하다고 생각함
- 저수준에 관심이 있었을 때 이 가이드를 가졌으면 좋았을 것임
-
어셈블리를 배우거나 구현하는 것에 대한 "즐거움"이 있는지 궁금함
- LISP나 RISC-V처럼 즐거움이 있는지, 아니면 COBOL처럼 특정 시스템과 작업하기 위해 배우는 것인지 궁금함
- 일상 업무에서 어셈블리를 사용할 이유가 없지만 재미로 시간을 투자할 가치가 있는지 궁금함
-
"q" 접미사는 포인터 크기를 나타내며, 64비트 시스템에서는 8임
- 문장이 혼란스럽게 느껴짐
- "i.e"는 "i.e.,"로 되어야 하며, "("는 열림 괄호로 되어야 함
- "sizeof"는 포인터를 반환하지 않음
-
K&R 참조에 대한 칭찬
- C와 프로그래밍을 배우기 위해 처음 구매한 책이었음
- 처음에는 C++를 배웠지만 너무 추상적이라 이해하기 어려웠음
-
어셈블리 사용의 단점은 코드가 아키텍처에 종속적이라는 것임
- x86, arm, x86_64에 대해 각각 다른 코드를 작성해야 함
- SIMD에 대한 이식 가능한 코드를 작성하는 좋은 방법이 없음
- Rust는 이식 가능한 SIMD API를 안정화하고 있으며, Zig는 SIMD 지원을 제공하지만 FFmpeg는 여전히 속도에 불만을 가질 수 있음
-
인라인 어셈블리에 대한 반대가 혼란스러움
- 인라인 어셈블리가 어셈블리 함수 호출보다 효율적일 것 같음
-
이 자료는 완벽함
- 386 시절의 x86 어셈블리를 알고 있었지만, 더 고급 프로세서는 너무 복잡했음
- 최근 CPU의 SIMD에 대해 더 배우고 싶음
-
어셈블리가 C보다 10배 빠르다는 것이 여전히 사실인지 궁금함
- 컴파일러가 손으로 작성된 어셈블리에 근접할 수 없을 정도로 정체되었는지 궁금함