프로그래밍 및 소프트웨어 개발

Numba를 사용해 금융 서비스의 계산 집약적 Python 모델을 가속하는 방법

Milliman의 Chad Schuster는 Numba를 사용하면 Python 프로그램의 일부를 LLVM을 통해 최적화된 코드로 변환하고 병렬 처리와 GPU를 활용할 수 있는 방법을 설명합니다. 실험에서는 상당한 성능 향상이 나타났지만, 객체 지향 프로그래밍, 형 추론, 컴파일 시간의 제약도 드러났으며, 가속에 적합한 부분을 선택하는 일이 핵심 요소로 제시됩니다.

2026-08-27
4 분 읽기
13 조회수
فريق تحرير certi.news
Numba를 사용해 금융 서비스의 계산 집약적 Python 모델을 가속하는 방법

Milliman의 금융 리스크 관리 부문 Principal인 Chad Schuster는 C++로 작성된 애플리케이션에 전적으로 의존하는 대신 Python과 Numba를 사용해 고성능 계산 모델을 구축한 실무 경험을 소개했습니다. 이 실험은 금융 서비스에서 흔히 발생하는 문제에서 출발합니다. 많은 시나리오에 걸쳐 현금흐름을 시뮬레이션하는 모델은 상당한 계산 능력을 필요로 하는 반면, 팀은 Python의 개발 속도와 유지관리 편의성을 유지하기를 원합니다.

이 문제는 생명보험 모델과 annuities 같은 연금 상품에서 특히 중요합니다. 이러한 계산은 미래의 부채를 평가하고 여러 시나리오를 실행하는 데 사용됩니다. 발표에 따르면 보험회사들은 역사적으로 5,000~10,000개의 노드로 구성된 온프레미스 그리드를 사용해 왔지만, 클라우드로 이전하면서 실행 시간과 각 작업의 비용이 인프라 결정에서 더욱 분명해졌습니다.

Numba는 Python에 무엇을 더하는가?

전통적인 Python, 즉 CPython은 실행 중 코드를 해석하는 반면, C++ 같은 컴파일 언어는 실행 전에 코드를 기계어 명령으로 변환합니다. Numba는 Just-in-Time 컴파일을 통해 이 격차를 줄이려 합니다. 즉, LLVM을 사용해 실행 중 적합한 함수를 컴파일한 다음 원래 함수를 컴파일된 구현으로 대체합니다.

개발자는 일반적으로 JIT 또는 njit 같은 Python 데코레이터를 통해 이 과정을 활성화합니다. Numba는 함수 단위로 작동하여 코드를 검사하고 자체적인 중간 표현으로 변환한 뒤 변수, 인수, 반환값의 형을 추론하고, 해당 표현을 LLVM IR로 낮추면서 필요한 최적화를 수행합니다. 함수가 서로 다른 형으로 호출되면 Numba는 polymorphic dispatch라고 부르는 방식으로 각 형 조합에 특화된 컴파일 구현을 만들 수 있습니다.

성능 향상은 고정된 수치가 아니다

Schuster가 소개한 개념 증명에서는 Numba를 사용한 프로그램이 해석된 Python보다 약 75배 빨라졌습니다. 다른 모델에서는 결과가 달랐습니다. 계산 집약적인 부분을 중앙처리장치의 Numba로 옮겼을 때 약 2배의 속도 향상이 나타났고, 이후 GPU로 전환하자 해당 특정 실행에서 추가로 750배의 속도 향상이 이루어졌습니다. 발표에서는 이 정도의 개선으로 해당 실행에서 GPU 하나가 약 750개의 사용 중인 코어에 해당하게 되었으며, 추정 비용은 약 10분의 1로 줄었다고 설명했습니다.

그러나 이러한 결과가 모든 애플리케이션에 대한 일반적인 약속을 의미하지는 않습니다. 향상 폭은 컴파일 가능한 코드의 양, 입출력 작업에 비해 계산 작업이 차지하는 규모, 기존 Python 구현의 효율성, 그리고 LLVM이 생성된 코드를 최적화할 수 있는 능력에 따라 달라집니다. 또한 GPU 처리는 모든 알고리즘에 적합하지 않으므로 모델을 재설계하기 전에 실제 병목을 측정해야 합니다.

엔지니어링 팀에 실질적으로 달라지는 점은 무엇인가?

이 실험에서는 데이터 준비와 입력·출력 계층은 Python에 유지하고, 가능한 경우 계산 집약적인 부분만 Numba로 옮길 것을 권장합니다. 이 접근 방식은 재작성 범위를 줄이고 Python 환경의 상당 부분을 유지하면서, 실행 시간 대부분을 차지하는 함수에 최적화 작업을 집중할 수 있게 합니다. 수치 함수가 제한적이고 명확하다면 Python 환경을 유지하려는 사람에게 Numba를 시도하는 것은 직접적인 선택지가 될 수 있다고 Schuster는 말합니다.

반면 광범위하고 복잡한 로직을 Numba에 도입하면 설계와 유지관리 비용이 증가합니다. 이 실험에서는 Numba의 지원이 Python의 모든 기능을 포괄하지 않기 때문에 NumPy 배열, tuples, 단순한 데이터 구조를 사용했습니다. 언급된 제약에는 유연한 형을 가진 딕셔너리, 예외, 컨텍스트 관리자, 클로저, comprehensions로 생성된 리스트, 그리고 print, sorted, getattr 같은 일부 일반적인 함수에 대한 제한이 포함됩니다.

도입 전에 고려해야 할 제약

객체 지향 프로그래밍은 이 실험에서 여전히 중요한 약점입니다. Numba는 객체와 유사한 동작을 제공하기 위해 jitclassesstructrefs 같은 실험적 기능을 제공하지만, 이러한 기능은 버전별로 변경될 수 있고 GPU를 지원하지 않습니다. 이 때문에 팀이 채택한 접근 방식에는 적합하지 않았습니다. 따라서 애플리케이션은 유지관리성과 고객에게 모델을 전달하는 측면에서 객체 지향 설계가 바람직했음에도, 함수형 프로그래밍과 단순한 데이터 구조에 가까운 방식을 사용했습니다.

또한 형 추론 오류와 lowering 오류는 추적하기 어려울 수 있으며, 특히 호출 계층이 많은 프로그램에서 그렇습니다. 오류 메시지가 실제 문제가 발생한 위치와 동떨어진 함수를 가리킬 수도 있습니다. 제안된 실용적인 방법 중 하나는 필요할 때 njit 대신 JIT를 사용하는 것입니다. 이렇게 하면 Numba를 일시적으로 끄고 해석된 Python으로 돌아가 디버깅을 쉽게 한 다음, 운영 실행에서 다시 가속을 활성화할 수 있습니다.

각 함수의 최초 호출 시 초기 컴파일 시간이 발생하며, inlining 기법을 광범위하게 사용하면 이 시간이 크게 늘어날 수 있습니다. 이러한 지연을 피하기 위해 Ahead-of-Time 사전 컴파일을 사용할 수 있지만, 이는 Just-in-Time 컴파일에 비해 실제 장치에 맞춰 코드가 적응하는 능력을 떨어뜨릴 수 있습니다.

편집부의 견해: 이 실험의 핵심 가치는 750배라는 수치 자체가 아니라 성능 측정과 모델 분할을 연결하는 방법론에 있습니다. Numba는 병목이 계산 작업이며 명확한 형과 구조를 가진 함수로 격리할 수 있을 때 적합합니다. 반면 전체 Python 시스템을 컴파일 가능한 코드로 변환하면 실행 속도 저하 문제가 개발과 디버깅의 복잡성 문제로 바뀔 수 있습니다. 따라서 금융 및 엔지니어링 팀은 이를 C++의 전면적인 대체재나 시스템의 더 광범위한 재설계에 대한 대안으로 간주하기 전에 실행 속도, 유지관리성, 컴파일 시간, GPU 호환성 사이의 균형을 검토해야 합니다.

뉴스 출처
InfoQ - Architecture Articles
원문 보기 ↗
ف
작성자

فريق تحرير certi.news

같은 카테고리

추천 기사

모든 뉴스 보기