느린 Python 서버, Rust로 바꾸면 얼마나 빨라질까?

100ms 걸리는 Python API 응답 중 절반이 DB를 기다리는 시간이다. 나머지 코드를 Rust로 다시 써서 10배 빠르게 만들면 응답은 얼마나 줄어들까?

느린 Python 서버, Rust로 바꾸면 얼마나 빨라질까?
세 줄 요약
  • Rust로 옮겨 빨라질 수 있는 것은 바꾼 부분이다. DB나 외부 API의 대기가 그대로면 전체 개선에는 상한이 생긴다.
  • 100ms 중 50ms가 바뀌지 않는 대기라면, 나머지를 10배 빠르게 해도 응답은 55ms다. 이는 설명용 계산이며 벤치마크가 아니다.
  • 먼저 실제 병목을 측정하고, 작업량 감소·네이티브 확장·전체 재작성 중 그 병목을 줄이는 방법을 선택해야 한다.

바꾸지 않은 대기 시간이 전체 속도의 상한을 정한다

느린 Python 서버를 Rust로 다시 써서 얻을 효과는 실제로 줄일 수 있는 시간이 얼마인지에 달려 있다. 응답의 대부분을 외부 시스템에서 기다린다면 앱 코드의 실행 속도만 높여서는 그 시간을 없앨 수 없다.

소프트웨어 엔지니어 Sean Goedecke는 10월 23일 글에서 AI로 코드를 더 빠른 언어로 옮기는 방식보다 병목과 설계를 먼저 보자고 주장했다. 그가 든 예는 요청 시간의 절반을 데이터베이스 조회에 쓰는 웹사이트다. 이 비율은 모든 서비스에서 측정된 통계가 아니라 가정이다.

이를 100ms 요청으로 바꾸면 계산이 선명해진다. DB 대기 50ms가 그대로이고 나머지 50ms만 빨라진다고 가정하자.

앱 코드의 속도 변화전체 응답 시간전체 속도 향상
2배 빨라진다.50 + 25 = 75ms다.약 1.33배다.
10배 빨라진다.50 + 5 = 55ms다.약 1.82배다.
앱 처리 시간이 0에 가까워진다.50ms에 가까워진다.상한은 2배다.

이 표는 Rust의 실측 성능이 아니라, 바뀌지 않는 시간이 남을 때의 산술 예시다. 쿼리 수를 줄이거나 캐시·동시성을 바꿔 DB 대기 자체를 줄였다면 전제도 달라진다. 그때의 개선을 언어 교체 효과만으로 설명할 수는 없다.

호출한 함수가 느린지, 그 함수가 기다리는지 구분한다

첫 조사 대상은 실제로 느린 요청이다. 요청 전체 시간과 함께 앱 함수의 처리 시간, DB·외부 API 호출 구간을 나눠 보면 재작성할 부분이 드러난다. ‘웹 서비스니까 I/O 병목일 것’이라는 추측도 측정값을 대신하지 못한다.

Python의 결정적 프로파일러는 함수 호출 횟수와 시간을 기록한다. 함수 자체에서 보낸 시간과 하위 호출까지 포함한 누적 시간을 구분해야 한다. 누적 시간이 큰 함수가 실제로는 더 아래의 느린 작업을 호출하고 있을 수 있기 때문이다.

Python 3.15 문서는 profiling.tracing을 제공하며, cProfile 이름도 호환용으로 사용할 수 있다고 설명한다. 다만 호출을 기록하는 계측 자체에도 부가 비용이 있다. 프로파일러는 병목을 찾는 데 쓰고, 변경 전후의 응답 시간은 같은 조건의 일반 실행에서도 비교해야 한다.

빠른 Rust 함수도 Python 객체를 만들며 시간을 쓸 수 있다

CPU 연산이 몰린 작은 부분이라면 앱 전체를 옮기지 않고 네이티브 확장으로 바꾸는 선택지가 있다. 그러나 함수 안의 계산 시간만 비교하면 Python과 Rust 사이에서 데이터를 변환하는 비용을 놓친다.

PyO3 성능 문서는 Rust 문자열을 Python 문자열로 만들 때 새 객체를 할당하는 비용을 구체적으로 설명한다. 반복 사용되는 문자열을 매번 만드는 경우에는 문자열 재사용 기법도 제공한다. Rust 쪽 계산이 빨라져도 경계를 오갈 때 새 객체를 많이 만들면 전체 효과가 줄어들 수 있다는 뜻이다.

이미 네이티브 라이브러리나 GPU가 대부분의 계산을 맡고 있다면 Python 호출부를 바꾸는 것만으로 핵심 연산까지 빨라지는 것도 아니다. 전체 서비스가 아니라 느린 반복문 하나가 문제인지, 데이터 변환이 문제인지, 기존 라이브러리 호출이 시간을 쓰는지부터 나눠야 한다.

언어를 정하기 전에 줄일 작업을 정한다

재작성 범위는 병목의 위치에 맞춰 정하는 편이 합리적이다. 다음은 계측 결과를 변경 후보에 연결하는 판단 기준이다.

측정한 병목먼저 비교할 변경
같은 데이터를 위한 DB·API 호출이 많다.불필요한 호출 제거와 캐시 가능성을 검토한다.
순수 Python 연산에 시간이 집중된다.계산할 작업을 줄이고, 해당 함수의 네이티브 구현도 비교한다.
Python과 Rust 사이의 변환에 시간이 든다.객체 할당과 변환 횟수를 줄일 수 있는지 본다.
이미 외부 라이브러리가 무거운 연산을 수행한다.호출부 교체가 실제 연산 경로를 바꾸는지 확인한다.

AI에 일을 맡길 때도 ‘전부 Rust로 바꾸기’보다 ‘느린 요청의 병목을 측정하고 그 구간을 줄일 변경을 제안하기’가 더 구체적인 출발점이다. 재작성은 측정한 문제를 해결할 수 있을 때 선택하는 수단이다. 변경 뒤에는 같은 입력과 부하에서 응답 시간과 결과의 정확성을 함께 비교해야 한다.

자주 묻는 질문

Python에서 Rust로 옮기면 최대 2배만 빨라지나?

아니다. 2배는 응답 시간의 절반이 바뀌지 않는다는 예시의 상한이다. CPU 연산 비중이나 쿼리 구조가 다르면 개선 폭도 달라진다.

그렇다면 전체 재작성은 필요 없나?

그렇게 결론 낼 수 없다. 성능 외에 유지보수나 이식성도 재작성의 이유가 될 수 있다. 속도가 목적이라면 바꾸려는 부분이 현재 병목을 얼마나 차지하는지부터 확인해야 한다.