NumKong 딥다이브: 2,000개의 SIMD 커널을 바닥부터 직접 짜는 것이 미친 짓이자 천재적인 이유
얼마 전 Hacker News에서 흥미로운 프로젝트 릴리스 글을 하나 읽었다. Ash Vardanian이 공개한 NumKong(이전 프로젝트명 SimSIMD의 후속)이다. 스레드에서 누군가 “인간이 읽을 수 있는 수준의 README가 없어서 도대체 뭘 하는 프로젝트인지 모르겠다”고 불평하는 것을 보았다. 솔직히 나도 처음엔 그저 흔한 선형대수 최적화 라이브러리 중 하나일 것이라 생각했다.
하지만 코드를 까보고 아키텍처 문서를 분석해 보니, 이건 단순한 라이브러리가 아니다. 무려 2,000개의 SIMD 커널을 7개 프로그래밍 언어로 포팅하고, Float118부터 Int4까지 온갖 기괴한 데이터 타입을 지원하는 괴물 같은 프로젝트다. 현대 CPU 아키텍처와 로우레벨 SIMD 최적화에 대한 마스터클래스에 가깝다.
15년 넘게 백엔드와 인프라 코어 단을 다뤄온 엔지니어로서, 이 프로젝트가 왜 기술적으로 경이로운지, 그리고 우리가 여기서 무엇을 배울 수 있는지 몇 가지 딥다이브 포인트를 정리해 보았다.
하드웨어의 수렴, 그리고 소프트웨어의 발산
최근 몇 년간 하드웨어 벤더들은 약속이나 한 듯이 타일 기반의 MAC(Multiply-Accumulate) 연산기를 CPU에 때려 넣고 있다. Intel의 AMX, Arm의 SME, 그리고 RISC-V의 Vector Extension(RVV)이 그 예다.
작성자는 이 중에서도 Apple M4 등에 탑재된 Arm SME(Scalable Matrix Extensions)를 극찬한다. 나 역시 최근 M4 프로파일링을 진행하면서 깊이 공감했던 부분이다. Intel AMX가 기존 AVX-512와는 다소 동떨어진 ‘격리된 가속기’ 느낌이라면, Arm SME는 SVE(Scalable Vector Extensions)와 매우 유연하게 결합된다. AMX가 Inner-product 방식으로 전체 K 차원을 한 번에 소비한다면, SME는 Outer-product 방식으로 스트리밍 하듯 연산한다. 이 차이 때문에 SME가 훨씬 더 높은 Composability를 제공한다.
반면 RISC-V에 대한 평가는 꽤나 뼈아프지만 정확하다. “정치적인 이유로 밀어주고 있지만, 2026년인 지금도 클라우드에서 쓸만한 RVV 1.0 규격의 하드웨어를 찾을 수 없다.” ISA 자체는 Segment load(vlseg)나 Widening reduction(vfwredusum) 같은 훌륭한 설계를 갖추고 있음에도 불구하고, 파편화된 생태계가 발목을 잡고 있는 현실을 정확히 짚었다.
기술적 하이라이트: 소름 돋는 비트 트위들링과 대수학적 트릭
이 블로그 포스트에서 가장 내 심박수를 올렸던 부분은 구형 Intel 칩(Ice Lake 등)에서 Int8/UInt8 대칭 내적(Symmetric Dot-Product)을 최적화한 방법이다.
- 문제 상황: Intel의
DPBUSD인스트럭션은 내적을 기가 막히게 수행하지만, 반드시 피연산자 하나는 Unsigned, 다른 하나는 Signed여야 한다는 기괴한 제약이 있다. 만약 두 입력값이 모두 Signed라면 어떻게 할 것인가? - 멍청한 해결책: 두 값을 모두 16비트로 캐스팅(
cvtepi8_epi16)한 뒤VPDPWSSD를 태운다. 하지만 이 캐스팅 연산은 Port 5를 점유하므로 곧바로 파이프라인 병목이 발생한다. - 우아한 해결책: 대수학적 트릭을 쓴다. 부호가 있는 바이트를
0x80과 XOR 연산하면 부호 비트가 뒤집히며[-128, 127]범위가[0, 255]의 Unsigned로 매핑된다. 즉, 값에 128을 더하는 것과 같다. 이렇게 변환한 뒤DPBUSD를 태우고, 마지막에SAD(Sum of Absolute Differences) 인스트럭션을 사용해 보정값(128 * Σb)을 빼준다.SAD는 Port 5에서 실행되고DPBUSD는 Port 0에서 실행되므로 완벽하게 병렬 처리가 된다.
이런 코드를 보면 정말 변태 같다는 생각(물론 칭찬이다)이 든다. 2018년쯤 비슷한 VNNI 병목 문제를 풀기 위해 인스트럭션 사이클을 세어가며 밤을 새웠던 기억이 스쳐 지나갔다. Sierra Forest부터는 VPDPBSSD가 추가되어 이런 똥꼬쇼를 안 해도 되지만, 세상에는 여전히 수백만 대의 구형 제온 서버가 돌아가고 있다.
Sub-byte 타입과 LUT의 마법
요즘 AI 판에서는 Float6(E3M2, E2M3)나 Int4, UInt4 같은 Sub-byte 타입이 유행이다. 문제는 CPU에 이런 4비트, 6비트 타입을 네이티브로 연산할 하드웨어가 없다는 것이다.
NumKong은 여기서 LUT(Look-Up Table)를 적극적으로 활용한다. 예를 들어 E2M3의 경우, 가능한 모든 5비트 Magnitude를 레지스터 하나에 꽉 채운 LUT로 만들어 버린다. x86에서는 VPERMUTEXVAR_EPI16을, Arm에서는 VQTBL2Q를 사용하여 비트 시프팅과 브랜칭 오버헤드를 완전히 날려버린다. 128개의 Magnitude를 가지는 E4M3 같은 경우엔 Normal 값은 산술 변환을 하고 Subnormal 값만 작은 LUT를 태우는 하이브리드 방식을 쓴다. 극단적인 최적화의 교본이라 할 만하다.
SIMD 환경에서의 마스킹된 수렴 (Vincenty 알고리즘)
지리공간(Geospatial) 연산에서 Haversine 공식은 빠르지만 지구의 타원체 형태를 무시하기 때문에 오차가 발생한다. 이를 보정하기 위해 Vincenty 알고리즘을 쓰는데, 이건 반복(Iterative) 알고리즘이다. SIMD 벡터 레지스터에 8개의 좌표쌍을 밀어 넣고 연산을 돌리면, 어떤 쌍은 3번 만에 수렴하고 어떤 쌍은 8번을 돌아야 한다.
이걸 SIMD로 어떻게 구현할까? NumKong은 __mmask8 converged_mask를 도입했다. 이미 수렴한 레인의 마스크를 세팅하고, 모든 비트가 1이 되면 루프를 탈출한다. 중요한 건 단순히 연산을 건너뛰는 게 아니라, Division by zero로 인해 NaN이 전파되는 것을 막기 위해 _mm512_maskz_div_pd 같은 마스킹된 나눗셈 인스트럭션을 사용한다는 점이다. 이 패턴은 Newton-Raphson 방식이나 Ray-sphere 교차 판정 등 실무에서 다양한 반복 알고리즘을 벡터화할 때 당장 훔쳐다 쓰고 싶을 정도로 깔끔하다.
총평: 그래서 이거 프로덕션에서 쓸 만한가?
솔직히 처음에 이 프로젝트의 규모를 들었을 때, 천재적인 개발자의 오버엔지니어링된 장난감이 아닐까 의심했다. 하지만 이 라이브러리는 이미 USearch의 코어 엔진으로 통합되어 ClickHouse, DuckDB 등에서 벡터 검색을 가속하는 데 쓰이고 있다. 전 세계 1억 대 이상의 디바이스에서 돌아가고 있다는 뜻이다.
Python 생태계에 찌들어 Numpy와 PyTorch의 무거운 오버헤드에 지친 C++나 Rust 개발자라면, NumKong의 제로 코스트 텐서 뷰와 컴파일 타임 슬라이싱 기능은 가뭄의 단비처럼 느껴질 것이다.
단순히 가져다 쓰는 것을 넘어, 이 라이브러리의 소스 코드는 현대 CPU가 어떻게 데이터를 씹어 먹는지 이해하기 위한 최고의 교재다. 주말에 시간을 내서 nk_dot_i8 커널 코드라도 한번 읽어보길 강하게 권한다. 우리가 당연하게 여기던 곱셈과 덧셈 밑바닥에서 어떤 치열한 최적화가 일어나고 있는지 깨닫게 될 것이다.
References:
- Original Article: https://ashvardanian.com/posts/numkong/
- Hacker News Thread: https://news.ycombinator.com/item?id=47459447