처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.08.11 44

러스트 1.98의 대수 연산자: 부동소수점 계산을 다시 빠르게

Hacker News 원문 보기

부동소수점 연산은 흔히 정수 연산보다 느리다. 알고리즘 탓이 아니라, 컴파일러가 최적화에 지나치게 조심스럽기 때문이다. 러스트는 1.98 버전(공개 예정일 2026년 8월 20일)에서 이 한계를 프로그래머가 직접 풀 수 있는 새로운 '대수(algebraic)' 산술 연산자를 도입한다. Itamar Turner-Trauring이 pythonspeed.com에 정리한 벤치마크를 따라가면, 왜 이 작은 API가 수치 계산 실무자에게 의미가 있는지가 드러난다.

왜 정수는 빠르고 실수는 느린가

출발점은 정수 합산이다. 필자는 x86-64-v3 타깃(대략 최근 10년 내 x86-64 CPU)으로 컴파일해, i7-12700K에서 int64 배열 100만 개를 더하는 함수가 값 하나당 약 0.5개의 CPU 명령만 쓰는 것을 확인했다. 이것이 가능한 이유는 SIMD, 즉 하나의 명령으로 여러 값을 한꺼번에 처리하는 명령어 덕분이다. 이 CPU의 256비트 SIMD는 64비트 정수 4개를 한 번에 다루므로, 100만 번이 아니라 25만 번의 반복으로 합을 끝낼 수 있다.

그런데 같은 방식으로 실수(f64)를 더하면 눈에 띄게 느려지고, 컴파일러는 SIMD 실수 연산을 쓰지 않는다. 이유는 컴파일러가 지키는 약속에 있다. 릴리스 모드의 최적화는 코드를 여러 방식으로 변형하되, 그 결과가 원래 코드와 정확히 같은 동작을 내야 한다. 정수 덧셈은 결합법칙이 성립해 a + (b + c)와 (a + b) + c가 항상 같으므로, 컴파일러는 덧셈 순서를 마음대로 바꿔 SIMD로 묶을 여지가 넓다.

순서가 결과를 바꾸는 실수의 특성

실수는 다르다. 표현 범위가 워낙 넓어서, 충분히 큰 수에 충분히 작은 수를 더하면 큰 수가 그대로 남는다. 예컨대 1e16 + 1.0의 결과는 여전히 1e16이다. 그래서 여러 값을 연달아 더할 때 a + (b + c)가 (a + b) + c와 항상 같지는 않다. 1e16 뒤에 1.0이 잔뜩 이어지는 배열과 그 배열을 뒤집은 배열을 각각 합하면 결과가 달라진다. 컴파일러는 프로그래머가 특정 순서를 요구한 데는 이유가 있다고 보고, 결과를 바꿀 수 있는 재정렬이나 그 밖의 최적화를 적용하지 않는다. 기본값으로서 이 보수성은 옳다. 문제는 프로그래머가 '여기서는 순서를 바꿔도 상관없다'고 알고 있는데도 그 사실을 컴파일러에 전할 방법이 없었다는 점이다.

러스트 1.98의 대수 연산자가 바로 그 통로다. 문서 표현으로는, 실수 연산에 대해 실수(real number)의 통상적인 대수적 성질을 모두 활용한 최적화를 허용하며 여기에는 연산 순서 변경도 포함된다. 즉 같은 코드 안에서도 어떤 부분은 엄격하게, 어떤 부분은 느슨하게 지정할 수 있게 됐다.

페어와이즈 합산이 보여주는 두 연산자의 공존

실용적 예가 대량 실수 합산이다. 앞서 본 1e16 + 1.0 문제 때문에, 많은 실수를 더할 때는 반올림 오차를 줄이는 알고리즘을 쓴다. NumPy의 sum()이 주로 쓰는 페어와이즈(pairwise) 합산은 속도와 정확도의 균형이 좋은 방식으로, 배열을 둘로 나눠 각각 재귀적으로 합한 뒤 두 결과를 더한다. 배열 크기가 일정 임계값(NumPy는 128) 아래로 내려가면 그때는 순서대로 단순 합산하며, 이 구간은 순서를 바꿔도 무방하다.

필자는 이를 러스트로 구현하면서 두 연산자를 나눠 썼다. 마지막에 두 부분합을 합치는 곳은 결과가 바뀌면 안 되므로 일반 덧셈을, 임계값 아래의 단순 합산 구간은 순서가 상관없으므로 대수 덧셈을 썼다. 그 결과 이 구현은 같은 알고리즘을 쓰는 NumPy보다 오히려 빠르면서, 오차 규모는 NumPy와 비슷하게 낮았다. 두 방식의 오차 차이는 의미 있는 것이 아니라 우연이며, 핵심은 둘 다 한 개씩 순서대로 더하는 소박한 방식보다 오차가 훨씬 작다는 점이다. 덤으로, SIMD 코드를 유도하는 러스트 구조가 NumPy 구현보다 훨씬 간결했다.

대수 연산자는 합산에만 쓰이지 않는다. 두 배열의 제곱 차이 합을 계산하는 예에서, 일반 연산자 대신 대수 연산자를 적용하자 필자의 컴퓨터에서 코드가 두 배 빠르게 돌았다. 다만 f64의 powi()가 대수 연산을 쓰는지 분명치 않아, 제곱 부분도 명시적으로 대수 연산으로 작성했다는 점은 이 API가 아직 세밀한 확인을 요구한다는 신호로 읽을 만하다.

실무자가 기억할 지점

이 기능의 값어치는 '무조건 빠른 실수 연산'이 아니라 '엄격함과 느슨함을 코드 안에서 골라 쓰는 통제권'에 있다. 페어와이즈 합산 예가 그 이유를 압축해서 보여준다. 정확도가 걸린 곳은 일반 덧셈으로 순서를 지키고, 속도만 필요한 곳은 대수 덧셈으로 컴파일러에 SIMD 재량을 주는 식이다. 반대로 말하면, 어디서 순서가 결과를 좌우하는지 스스로 판단하지 못하는 코드에 대수 연산자를 일괄 적용하면 재현성 없는 수치 오차를 떠안을 수 있다. 러스트로 수치 코드를 다룬다면 1.98을 쓸 수 있게 되는 시점에 자신의 핫스팟에서 두 연산자를 나눠 시험해 볼 가치가 있고, 이 벤치마크가 x86-64-v3라는 특정 타깃과 특정 CPU에서 측정됐다는 조건도 함께 기억해 둘 필요가 있다.

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

파이썬으로 자동화를 시작해보세요

파이썬 기초부터 자동화까지 실전 강의.

파이썬 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.