더 빠른 asin() 함수가 바로 눈앞에 있었다
Faster asin() was hiding in plain sight
TL;DR Highlight
개발자가 raytracer 최적화를 위해 Taylor 급수와 Padé 근사로 asin() 함수를 직접 구현하려다가, 1960년대 수학 교재와 NVIDIA CG 라이브러리에 이미 최적화된 공식이 존재했음을 발견했다.
Who Should Read
그래픽스, 게임 엔진, 레이트레이싱 등 수치 연산 성능이 중요한 C++ 개발자. 또는 수학 함수의 근사(approximation) 최적화 기법을 실무에 적용해보고 싶은 개발자.
Core Mechanics
- PSRayTracing(C++ 레이트레이서 프로젝트)를 프로파일링했더니 std::asin() 호출이 병목으로 나타났고, 이를 근사 함수로 대체해 성능을 높이려는 시도가 시작됐다.
- 먼저 Taylor 급수(어떤 함수를 다항식의 합으로 표현하는 수학적 기법) 4차 근사를 직접 구현했고, x가 -0.8~0.8 범위 안에서는 잘 동작했지만 그 범위 밖에서는 오차가 커서 결국 범위 밖에서는 std::asin()으로 fallback하는 방식을 사용했다. 이 방법으로 약 5% 속도 향상을 얻었다.
- 다음으로 Padé Approximant(Taylor 급수를 분수 형태의 다항식으로 변환해 더 넓은 범위에서 정확도를 높이는 기법)를 Python으로 계산해 적용했지만, 오히려 표준 라이브러리보다 느려서 실패했다.
- 막힌 상황에서 LLM에게 질문했더니, Stack Overflow에 올라온 NVIDIA CG 라이브러리 기반의 fast_asin 코드를 알려줬다. 알고 보니 이 코드는 1955년 Hastings의 수치해석 교재와 Abramowitz & Stegun 수학 공식 모음집(4.4.45번 공식)에 이미 수록된 내용이었다.
- 해당 NVIDIA CG 기반 근사 공식은 square root를 활용한 구조로 되어 있으며, 최종적으로 이 코드를 C++에 적용했더니 약 90% 속도 향상(1.9배)이라는 큰 개선을 얻었다. 이전의 Taylor 급수 5% 향상과 비교하면 압도적인 차이다.
- LLM이 알려준 코드의 출처를 추적했더니 7년 된 Stack Overflow 답변이었고, 그 답변은 다시 1960년대 수학 교재로 이어졌다. 저자는 '처음부터 직접 찾아봤으면 훨씬 빨리 해결됐을 것'이라고 자평했다.
- 이 경험의 핵심 교훈은 '수치 함수 최적화는 이미 수십 년간 연구가 쌓인 분야이므로, Taylor 급수 같은 200년 된 기법보다 minimax 근사나 Remez 알고리즘 같은 현대적 기법을 먼저 찾아보는 것이 낫다'는 것이다.
Evidence
- 댓글에서 수치 근사 분야 배경이 있는 사람들이 Taylor 급수는 근사 함수 구현에 좋지 않은 선택이라고 지적했다. Padé Approximant도 Taylor 급수를 기반으로 하면 효과가 제한되고, 차라리 원래 함수에 직접 Chebyshev 근사나 Remez 알고리즘을 적용하는 것이 minimax(최대 오차를 최소화) 관점에서 훨씬 낫다는 의견이 있었다.
- 저자가 '60년대 수학 교재'라고 표현한 Abramowitz & Stegun 공식 모음집이 실제로는 수치해석 분야에서 Knuth의 'The Art of Computer Programming'에 준하는 권위 있는 레퍼런스라는 점을 댓글러들이 지적했다. 이 책이 obscure한 텍스트가 아니라는 점을 모르고 넘어간 것이 저자의 실수라는 뉘앙스의 댓글이 있었다.
- 빠른 asin()의 진짜 출처를 추적한 댓글이 있었는데, 1955년 Hastings의 'Approximations for Digital Computers'(Princeton) 159~163페이지가 원조이며, 이후 Abramowitz & Stegun이 허가를 받아 수록했고, NVIDIA CG 라이브러리가 이를 코드로 옮긴 것이라는 계보가 밝혀졌다. 원래 목적도 1950년대 컴퓨터의 성능에 맞는 효율적인 연산이었다.
- 알고리즘 근사보다 SIMD(한 번의 명령어로 여러 데이터를 동시 처리하는 CPU 기능)나 GPU 병렬화를 먼저 고려하는 것이 우선순위 면에서 더 낫다는 의견도 있었다. 1.05~1.90배 속도 향상도 좋지만, 16배 향상을 줄 수 있는 SIMD를 먼저 검토해야 한다는 것이다. Intel AVX-512에 `_mm512_asin_ps` intrinsic이 있다는 정보도 제공됐다.
- LLM이 답을 찾아준 것에 대해 '결국 7년 된 Stack Overflow 답변을 가져온 것'이라는 냉소적인 댓글이 있었고, 실제로 해당 Stack Overflow 링크(https://stackoverflow.com/a/26030435)가 이 글이 올라오기 전 'fast asin'을 검색하면 첫 번째 결과로 나왔다는 점을 지적하며, 처음부터 검색만 했어도 됐다는 의견이 있었다.
How to Apply
- C++/C 그래픽스 코드에서 std::asin() 호출이 프로파일링 병목으로 잡히는 경우, NVIDIA CG 라이브러리 기반의 fast_asin 공식(Stack Overflow: https://stackoverflow.com/a/26030435 참고)을 적용하면 별도의 복잡한 수학 없이 약 1.9배 속도 향상을 즉시 얻을 수 있다.
- 수치 함수(sin, cos, atan2 등)를 직접 근사 구현하려는 경우, Taylor 급수부터 시작하지 말고 Robin Green의 'Faster Math Functions' 자료(GDC 발표 포함)나 Abramowitz & Stegun 공식 모음집을 먼저 확인하면 수십 년간 검증된 minimax 근사 공식을 바로 활용할 수 있다.
- 새로운 수학 근사 함수 최적화를 시작하기 전에, 먼저 목표 범위(예: [-1, 1])에서 오차 파형(error waveform)을 그려보고 minimax 특유의 등진동(equioscillation) 형태가 나타나는지 확인하는 것이 좋다. 이 형태가 없으면 아직 최적화 여지가 있다는 신호이며, Remez 알고리즘(최적 다항식 근사를 생성하는 알고리즘) 도구를 사용하면 더 나은 계수를 자동으로 구할 수 있다.
Code Example
// NVIDIA CG 라이브러리 기반 fast_asin (Stack Overflow: https://stackoverflow.com/a/26030435)
// 원출처: Hastings 1955 / Abramowitz & Stegun 4.4.45
float fast_asin(float x) {
float negate = float(x < 0);
x = abs(x);
float ret = -0.0187293f;
ret *= x;
ret += 0.0742610f;
ret *= x;
ret -= 0.2121144f;
ret *= x;
ret += 1.5707288f;
ret = 3.14159265358979f * 0.5f - sqrt(1.0f - x) * ret;
return ret - 2 * negate * ret;
}
// Taylor 급수 기반 4차 근사 (저자 직접 구현, ~5% 향상, -0.8 ~ 0.8 범위만 유효)
double _asin_approx_private(const double x) {
if ((x < -0.8) || (x > 0.8)) {
return std::asin(x); // fallback
}
constexpr double a = 0.5;
constexpr double b = a * 0.75;
constexpr double c = b * (5.0 / 6.0);
constexpr double d = c * (7.0 / 8.0);
const double aa = (x * x * x) / 3.0;
const double bb = (x * x * x * x * x) / 5.0;
const double cc = (x * x * x * x * x * x * x) / 7.0;
const double dd = (x * x * x * x * x * x * x * x * x) / 9.0;
return x + (a * aa) + (b * bb) + (c * cc) + (d * dd);
}Terminology
관련 논문
Swift로 LLM 학습시키기 Part 1: 행렬 곱셈을 Gflop/s에서 Tflop/s로 끌어올리기
Apple Silicon에서 Swift로 직접 행렬 곱셈 커널을 구현하며 CPU, SIMD, AMX, GPU(Metal)를 단계별로 최적화해 Gflop/s에서 Tflop/s 수준까지 성능을 높이는 과정을 상세히 설명한 글이다. 프레임워크 없이 LLM 학습의 핵심 연산을 밑바닥부터 구현하고 싶은 개발자에게 Apple Silicon의 성능 한계를 체감할 수 있는 드문 자료다.
fsync 없이 로컬 스토리지 엔진을 crash-consistent하게 만든 방법
FractalBits가 fsync 없이 SSD 전용 KV 스토리지 엔진을 구현해 동일 조건 대비 약 65% 높은 쓰기 성능을 달성한 설계 방법을 공유했다. fsync의 메타데이터 오버헤드를 피하기 위해 사전 할당, O_DIRECT, SSD 원자 쓰기 단위 정렬 저널을 조합한 구조가 핵심이다.
Google Chrome, 사용자 동의 없이 4GB AI 모델(Gemini Nano)을 몰래 설치
Google Chrome이 사용자 동의 없이 Gemini Nano 4GB 모델 파일을 자동 다운로드하고, 삭제해도 재다운로드되는 문제가 발견됐다. GDPR 위반 가능성과 수십억 대 기기에 적용될 때의 환경 비용 문제가 제기되고 있다.
OpenAI가 대규모 저지연 Voice AI를 제공하는 방법
OpenAI가 9억 명 이상의 사용자에게 실시간 음성 AI를 제공하기 위해 WebRTC 스택을 어떻게 재설계했는지 설명하는 글로, relay + transceiver 분리 아키텍처의 설계 결정과 trade-off를 상세히 다룬다.
Truncated Decoding Tree의 결정론적 탐색을 통한 효율적인 Test-Time Inference
Self-consistency의 중복 샘플링 낭비를 없애는 결정론적 트리 탐색 디코딩 기법 DLE로 수학/코드 추론 성능과 속도를 동시에 개선
GoModel – Go로 작성된 오픈소스 AI Gateway
OpenAI, Anthropic, Gemini 등 여러 AI 프로바이더를 하나의 OpenAI 호환 API로 묶어주는 Go 기반 오픈소스 AI 게이트웨이로, LiteLLM의 컴파일 언어 대안이다.