Rust의 새 API로 부동소수점 연산 가속하기
요약
Rust의 새로운 대수적 산술 API를 통한 부동소수점 연산 가속과 컴파일러 최적화 사이의 불확실성을 분석합니다. 기존 fast-math 옵션과 달리 연산 단위로 제어가 가능하며, 최적화 과정에서 발생할 수 있는 비결정적 동작과 안전성 문제를 다룹니다.
핵심 포인트
- Rust의 대수적 산술 API는 연산 단위로 최적화를 제어할 수 있음
- 컴파일러 최적화 단계에서 부동소수점 연산의 비결정적 결과 가능성 존재
- 기존 fast-math보다 안전하며 정밀한 제어가 가능하다는 장점
- 최적화 보장에 의존하는 코드 작성 시 주의가 필요함
대수적 연산의 실행 의미론이 불분명함. 문서에 따르면 컴파일러 최적화의 예측 불가능성 때문에 같은 입력도 한 번의 프로그램 실행 중 서로 다른 결과를 낼 수 있으며, 안전성을 위해 반환값의 어떤 속성에도 의존하면 안 된다고 함
그래도 반환값이 한 번 정해지면 고정된다는 보장은 있어야 하지 않는지 궁금함. 최적화 단계가 코드를 복제할 수 있고 대수적 연산이 비결정적이라면 freeze poison과 비슷하게 동작하는지도 모호함 a의 값이 같더라도 상수에서 왔는지, FMA로 바뀔 수 있는 곱셈에서 왔는지에 따라 a.algebraic_add(b)의 결과가 달라질 수 있음. 이는 부동소수점 값에도 출처(provenance) 가 있는 것처럼 보이며, 값은 같지만 출처가 다를 때 동등한지와 최적화 단계가 이를 고려하는지가 궁금함
let sum = a + b;
let algebraic = sum.algebraic_sub(b);
let exact = sum - b;
if algebraic == exact { // (1)
assert_eq!(algebraic, a); // (2)
}
(1)은 IEEE 754 의미론과 NaN이 없다는 전제에서 true로, (2)는 기호적으로 계산한다는 전제에서 true로 각각 최적화할 수 있지만 둘을 함께 적용하면 잘못된 컴파일이 가능해 보임. LLVM에서 실제 오컴파일을 재현하지는 못했지만 각 최적화 단계가 이를 의도적으로 고려하는지 알기 어렵고, Rust의 대수적 산술 API 논의에서도 답을 찾지 못함
이 코드는 잠재적인 오컴파일이라기보다, 스스로 포기한 최적화 관련 보장에 의존하므로 애초에 작성하면 안 되는 코드로 해석됨. 매우 의외의 결과가 나올 수 있지만 그런 동작을 요청한 셈임
다만 이 기능을 비활성화해야 하거나 문서를 개선해야 할 가능성도 있으므로 개발자에게 직접 문의하는 편이 좋겠음
GCC/Clang의 -fastmath 옵션과 같은 기능인지 궁금함
어느 정도 비슷하지만 더 나은 방식임. 기본 fast-math는 정의되지 않은 동작을 일으킬 수 있는 최적화까지 활성화하지만, 이 기능에는 그런 동작이 없음. 물론 fast-math에서도 문제가 없는 최적화만 골라 활성화할 수는 있음
또한 fast-math가 기껏해야 함수 단위인 데 비해 이 기능은 연산 단위로 적용됨
문제 영역과 Rust의 대수적 연산을 이미 알고 있었지만, 이 글은 간결하면서 핵심을 잘 전달함. 하드웨어 성능 카운터로 수집한 것으로 보이는 SIMD 연산 횟수 통계도 유용한 요소임
실제로 하드웨어 성능 카운터에서 수집한 값임. 책을 위해 간단한 벤치마킹용 Jupyter 매직을 작성했고 글은 Quarto로 렌더링함
다음처럼 실행하면 결과가 Markdown 표로 생성됨
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기