고성능 코드를 작성하는 개발자에게 SIMD(Single Instruction, Multiple Data)는 오랫동안 계륵 같은 존재였다. CPU가 한 번의 명령으로 여러 데이터를 동시에 처리하게 해주는 이 기술은 이미지 처리, 오디오 디코딩, 수치 연산 같은 영역에서 몇 배의 속도 향상을 가져다주지만, 그 대가로 플랫폼별 인트린식(intrinsic)에 직접 손을 대야 하고 Rust에서는 이 과정이 거의 예외 없이 unsafe 코드로 얼룩지곤 했다. Linebender 진영이 8년 전 프로토타입에서 출발해 이번에 정식으로 내놓은 Fearless SIMD 1.0은 바로 이 지점, 즉 '안전성과 성능을 동시에'라는 오래된 난제를 정면으로 겨냥한 라이브러리다.
이식성과 성능을 둘 다 잡겠다는 설계
이식 가능한(portable) SIMD 추상화에 흔히 따라붙는 비판은 성능이 충분하지 않다는 것이다. 플랫폼마다 동작이 다른 연산, 예컨대 스위즐(swizzle)이나 부동소수점 최댓값 처리 같은 엣지 케이스가 대표적이다. Fearless SIMD는 이런 연산에 대해 모든 플랫폼에서 동일한 결과를 보장하는 정밀(precise) 변형과, 엣지 케이스가 발생하지 않는다고 확신할 때 쓸 수 있는 빠른(fast) 변형을 함께 제공한다. 후자는 플랫폼에 따라 결과가 달라질 수 있는 대신 불필요한 보정 비용을 없앤다. 개발자가 상황에 맞게 안전 마진과 속도를 직접 선택하도록 한 것이다.
또 하나 눈여겨볼 점은 하드웨어의 네이티브 벡터 크기를 기준으로 알고리즘을 표현하기 쉽게 만들었다는 것이다. 이렇게 하면 코드가 어떤 CPU에서 돌든 그 하드웨어의 벡터 폭을 최대한 활용한다. 물론 고정된 벡터 크기가 필요한 알고리즘을 위해 고정 크기도 지원한다. 라이브러리 개발진은 이식성 연산의 구현을 최신 수준으로 끌어올리기 위해 Rust와 LLVM 상류(upstream)에 직접 개선 사항을 기여했다고 밝혔다. 그리고 이식성 추상화가 커버하지 못하는 명령이 필요하면 오버헤드 없이 플랫폼 인트린식으로 내려갈 수 있어, 성능 상한이 사실상 존재하지 않는다는 점을 강조한다.
'unsafe 수천 개' 문제를 구조로 해결
Fearless SIMD가 가장 차별화를 내세우는 지점은 unsafe 코드의 부재다. 다른 SIMD 추상화 라이브러리의 소스를 뒤져보면 unsafe 블록이 수천 개씩 나오는 경우가 흔한데, 이 크레이트는 임시방편식 unsafe를 요구하지 않도록 설계됐다. 핵심 장치 중 하나는 컴파일러의 target feature v1.1 기능에 기대어 대부분의 SIMD 인트린식을 unsafe 없이 호출하는 kernel! 매크로다. 다만 이것만으로는 원시 포인터를 다루는 load/store 연산까지 덮지 못한다.
그 빈틈을 메우는 것이 bytemuck, zerocopy 같은 크레이트에서 영감을 받은 안전한 transmute 모듈이다. _mm_loadu_epi32 같은 로드 인트린식이 실은 내부적으로 평범한 로드·스토어로 변환된다는 점에 착안해, 재사용 가능한 단일 래퍼로 그 기능을 그대로 복제했다. 결과적으로 감사(audit)해야 할 대상은 이 두 개의 작고 독립적인 빌딩 블록뿐이다. 이 두 조각의 메모리 안전성만 보장되면 Rust 타입 시스템의 힘으로 나머지 코드베이스 전체의 메모리 안전성이 따라온다는 논리다. 안전성 검증의 표면적을 극단적으로 좁힌 이 접근은 실무에서 코드 리뷰와 신뢰 확보 비용을 크게 줄여준다.
#[simd] 매크로와 남은 과제
기존 방식들은 함수마다 #[inline(always)] 어노테이션을 붙이고 그 함의를 이해하거나, 아니면 모든 함수 호출에 약간의 오버헤드를 감수해야 했다. 후자는 대개 무난하지만 아주 작은 함수에서는 성능이 떨어지고, 이를 피하려면 다시 수작업으로 인라인 지시를 넣어야 한다. 두 방식 모두 내부 동작을 개발자가 계속 신경 써야 하는 '새는 추상화'인 셈이다. 이번에 함께 공개된 fearless_simd_macros 0.1의 #[simd] 매크로는 SIMD 함수에 붙이기만 하면 알아서 동작하도록 해 이 부담을 걷어낸다. 절차적 매크로를 선호하지 않는다면 다소 불편하더라도 기존 방식을 그대로 쓸 수 있다.
다만 개발진 스스로 인정하듯 아직 상용구(boilerplate)가 남아 있고, 에르고노믹스는 앞으로 더 진화할 여지가 있는 영역이다. Struct Target Features RFC를 통한 컴파일러 지원으로 #[simd] 어노테이션 자체를 없애는 방향 등이 거론된다. 대신 이런 변화가 코어 fearless_simd 크레이트의 안정성 보장을 훼손하지는 않으며, 오늘 작성한 코드는 매크로 사용 여부와 무관하게 계속 동작한다고 약속한다. v1.0과 이후 버전에는 3년간 보안 업데이트가 제공된다.
표준 라이브러리와의 관계, 그리고 채택 현황
한국의 실무자가 도입을 검토할 때 자연스럽게 떠오르는 질문은 언젠가 표준화될 std::simd와의 관계다. 개발진의 답은 std::simd가 안정화되더라도 Fearless SIMD가 무용지물이 되지는 않는다는 것이다. Rust 표준 라이브러리는 반드시 표준에 들어가야 하는 부분만 구현하고, 멀티버저닝이나 하드웨어 폭 벡터 같은 나머지는 생태계 크레이트의 몫으로 남긴다는 설명이다. 실제로 Fearless SIMD는 안정 버전 Rust에서 동작하는 std::simd 상당 기능을 이미 포함하고 있으며, std::simd가 안정화되면 그 위로 이식해 자체 구현 코드를 상당 부분 걷어내고 난해한 플랫폼 지원까지 얻을 계획이다. f16 타입 같은 근시일 기능은 물론 SVE, RISC-V 벡터 확장 같은 장기 하드웨어 기능까지 API 파괴 없이 수용할 경로가 있다고 본다.
라이브러리의 가치는 결국 쓰임새로 증명된다. Fearless SIMD는 이미 30개 크레이트가 직접 의존하고 있고 1,000개가 넘는 크레이트가 간접적으로 기대고 있어, Rust 생태계의 적지 않은 부분을 떠받치고 있다. 정리하자면 이번 릴리스는 '안전한 코드는 느리다'는 통념을 구조적 설계로 반박하려는 시도이며, 성능이 절실하면서도 unsafe 감사 부담을 줄이고 싶은 팀에게 현실적인 선택지가 됐다. 반대로 에르고노믹스가 아직 다듬어지는 중이고 상용구가 남아 있다는 점은 도입 전 감안해야 할 대목이다.