TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 20 READS

러스트 SIMD 2026: 자동 벡터화부터 올인원 크레이트까지

현대 CPU는 산술 연산 장치를 넉넉히 갖추고 있지만, 명령어를 해독하는 블록은 하나뿐이라 그 연산 능력의 상당 부분이 놀고 있다. 이 병목을 우회하는 방법이 SIMD, 즉 '하나의 명령으로 여러 데이터를 처리'하는 방식이다. 두 숫자를 더하는 대신 숫자 묶음(벡터) 두 개를 한 번에 더해도 걸리는 시간은 비슷하다. 최신 x86 칩은 이 묶음을 최대 512비트까지 키울 수 있어 이론상 f64 연산은 8배, u8 연산은 64배까지 빨라질 수 있다. 물론 실제로는 그보다 느릴 수도, 빠를 수도 있다.

SIMD가 까다로운 이유는 아키텍처가 설계된 뒤에 확장 형태로 덧붙여졌다는 데 있다. ARM은 NEON이라 부르며 64비트 CPU 전부에 필수로 넣었고, 웹어셈블리는 128비트 패킹 SIMD 확장을 표준으로 둔다. 문제는 x86이다. 64비트 x86은 기본 SSE2 위에 SSE4.2, 256비트를 다루는 AVX와 AVX2, 512비트와 추가 연산을 얹은 AVX-512까지 시대별로 확장을 쌓아 올렸다. 그래서 x86_64 CPU라고 해서 특정 확장이 있으리란 보장이 없고, 컴파일러는 기본적으로 SSE2를 넘어서는 명령을 쓰지 못한다.

멀티버저닝이라는 숙제

자사 서버나 퍼블릭 클라우드에서만 돌리는 바이너리라면 'AVX2 정도는 다 있다'고 단정하고 없으면 죽어버리게 만들 수 있다. AVX2는 도입된 지 10년이 넘었으니 무리한 가정도 아니다. 하지만 남에게 배포할 바이너리라면 그럴 수 없다. 그래서 등장하는 것이 함수 멀티버저닝이다. 같은 함수를 여러 SIMD 확장용으로 각각 컴파일해 두고, 프로그램이 실제로 돌 때 CPU가 지원하는 기능을 확인해 알맞은 버전을 고르는 방식이다. 이 골칫거리는 사실상 x86에만 존재한다. ARM은 NEON을 필수화한 뒤 쓸 만한 SIMD 확장을 거의 추가하지 않았고, 웹어셈블리는 SIMD 유무에 따라 바이너리 두 개를 만들어 자바스크립트로 브라우저 지원 여부를 확인하게 한다.

컴파일러에 맡기는 자동 벡터화

가장 손쉬운 길은 평범한 러스트를 쓰고 컴파일러의 최적화 휴리스틱에 맡기는 것이다. 의존성도 필요 없고 컴파일러가 지원하는 모든 명령어 집합을 자동으로 활용한다. 다만 코드를 컴파일러가 안정적으로 벡터화할 수 있는 형태로 짜야 하는데, 보통 슬라이스를 그대로 순회하는 대신 as_chunks()로 잘라 도는 식이다. 신뢰성은 이 방식의 약점이다. 함수가 크고 복잡해질수록 벡터화 실패 확률이 높아지고, 컴파일러 버전이나 주변 코드 변화만으로도 성능이 크게 출렁인다. 부동소수점은 특히 조심해야 한다. 예전에는 결과 정밀도가 바뀐다는 이유로 자동 벡터화가 아예 막혀 있었지만, 러스트 1.98에서 algebraic_add() 같은 대수 연산이 안정화되면서 -ffast-math의 덜 위험한 버전처럼 컴파일러가 관측 결과를 바꾸도록 허용할 수 있게 됐다. 다만 대부분의 경우 코드를 이 연산으로 다시 써야 벡터화 대상이 된다.

자동 벡터화에는 여전히 멀티버저닝이 필요한데, multiversion 크레이트가 유용하다. 함수에 애너테이션 한 줄만 붙이면 되지만, 문서화되지 않은 함정이 있다. 애너테이션이 붙은 함수를 호출할 때 십수 개 미만의 명령 정도로 아주 작은 오버헤드가 생기는데, 정작 그 함수가 작을수록 이 비용이 두드러진다. 경험칙으로 루프가 있는 함수에는 multiversion을, 값 몇 개만 처리하는 함수에는 호출 사슬 위쪽에 multiversion이 있다는 전제로 inline(always)를 붙이는 편이 낫다. 또한 multiversion은 정확한 CPU 확장을 지정할 수 있는 유일한 크레이트지만, AVX-512는 '존재 여부'만 확인하고 '실제로 빠른지'는 따지지 않아 오히려 성능을 해칠 수 있다.

올인원 크레이트의 지형도

러스트 표준 라이브러리의 std::simd는 완결된 해법이 아니라 표준에 반드시 있어야 할 빌딩 블록에 가깝다. LLVM 위에 직접 올라타 LLVM이 지원하는 어떤 플랫폼이든 겨냥할 수 있다는 게 존재 이유다. 반대로 LLVM에 딱 맞는 연산이 없으면 대안 없이 SIMD가 아예 쓰이지 않는데, 이런 일이 생각보다 잦다. sin()이나 reduce_sum() 같은 함수는 SIMD 외피만 두른 스칼라 구현이라 부동소수점에서 비자명한 함수는 쓰지 않는 게 좋다. 삼각함수에 가장 근접한 것은 SLEEF를 일부 이식한 sleef 크레이트인데, 이마저 조금 버그가 있다. 게다가 std::simd는 나이틀리 전용이고 이따금 호환성을 깨는 API 변경이 있어, 컴파일러를 올리면 코드가 갑자기 빌드되지 않을 수 있다.

서드파티 확장이 개별로는 동작해도 서로 조합되지 않는다는 한계 때문에 모든 부품이 맞물려 돌아가는 올인원 해법이 필요하다. 이 글의 저자가 관리자로 참여하는 Fearless SIMD가 그런 방향으로, AVX-512를 성능에 해가 되지 않는 최신 CPU에서만 기본적으로 사용하도록 설계됐고 최근 보안 정책까지 갖춘 v1.0을 냈다. 가장 큰 공백은 삼각함수로, SLEEF에 준하는 이식이 아직 없다. wide는 플랫폼 커버리지가 넓고 구현 연산이 많으며 정밀도를 명시하지 않은 삼각함수까지 있는 v1.0이지만, 멀티버저닝과 근본적으로 맞지 않아 x86에서 알려진 하드웨어용으로 빌드하지 않으면 성능이 크게 깎인다. pulp는 선형대수 라이브러리 faer를 위해 만들어져 연산이 대부분 수학 중심이고 네이티브 폭 벡터에 맞춰져 있으며, 멀티버저닝 설정은 저자가 본 것 중 가장 장황하다. pulp의 친척인 macerator는 burn의 CPU 백엔드용으로, 원소 타입 제네릭을 지원하고 f16 데이터에 일부 이식 연산을 제공한다.

실무자 입장에서 정리하면, SIMD 도입은 언제나 이식성·안정성·성능·보일러플레이트 사이의 선택이다. 의존성 없이 빠르게 시작하려면 자동 벡터화에 algebraic 연산과 multiversion을 얹되 함수 크기에 따른 오버헤드를 유념해야 하고, 안정적인 조합을 원한다면 Fearless SIMD 같은 올인원 크레이트가 유리하다. 다만 삼각함수를 비롯한 비자명한 부동소수점 함수는 어느 라이브러리에서도 아직 완전히 신뢰하기 어렵다는 점, 그리고 std::simd 계열은 나이틀리 의존과 API 변경 위험을 감수해야 한다는 점은 도입 전 반드시 저울질할 대목이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://shnatsel.github.io/state-of-simd-rust-2026/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...