얼쑤는 연주자가 악보보다 조금 빠르거나 느리게 불어도 같은 음끼리 비교할 수 있도록 DTW로 두 시간축을 맞춥니다. 녹음 길이 제한을 검토하면서, 계산이 끝나는 데 걸리는 시간과 함께 한 요청이 차지하는 메모리도 확인했습니다. 운영과 같은 조건에서 120초 합성 녹음을 처리하는 데 17.25초가 걸렸고 프로세스 최대 메모리는 3,065MiB였습니다.

같은 서버에서 녹음 길이 비교

운영과 같은 분석 이미지와 6GiB 메모리 제한으로 일회용 컨테이너를 실행했습니다. 네트워크를 끄고 합성 음계를 이어 붙여 만든 녹음을 길이별로 한 번씩 처리했습니다. 각 실행에서 짧은 입력으로 pYIN을 먼저 호출해 준비 시간을 따로 기록했습니다. 아래 시간은 준비 이후의 음높이 추정과 채점을 합한 값입니다.

합성 녹음 길이pYIN 음높이 추정채점합계프로세스 최대 메모리
60초6.20초2.15초8.35초976MiB
120초9.50초7.75초17.25초3,065MiB

측정 당시 호스트는 4코어였고, 측정 기록에 남은 부하 평균은 5.96 → 5.49였습니다. 부하 평균에는 CPU를 기다리는 작업 외에 I/O를 기다리는 작업도 들어갈 수 있습니다. 이 수치는 실행 환경을 설명하는 값이며, 17.25초를 다른 실행에서도 넘지 않는다는 근거는 아닙니다.

메모리는 Linux의 ru_maxrss를 MiB로 환산한 값입니다. 분석 프로세스의 최고 상주 메모리라서 라이브러리와 준비 단계의 메모리도 포함합니다. 서비스 요청의 대기열 체류, 다운로드, 디코딩, DB 저장은 이 실험에서 재지 않았습니다. pYIN 준비 시간과 전체 요청의 마감은 시간 예산 글에서 살폈습니다. pYIN 문서 · 연주 분석 60초의 시작점

DTW가 비교하는 칸 수

저희 구현은 정답의 프레임과 녹음의 프레임을 한 축씩 놓고, 각 조합의 차이와 누적 비용을 행렬에 기록합니다. 정답과 녹음의 길이가 함께 두 배가 되면 행과 열이 모두 두 배가 되어 칸 수는 네 배가 됩니다. 정답 길이가 고정된 채 녹음만 두 배가 되면 칸 수는 약 두 배가 됩니다.

행렬 자체의 비용을 보려고 별도로 개발 노트북에서 음높이가 일정한 배열을 채점기에 넣고, 채점 함수의 메모리 할당 최고치를 tracemalloc로 쟀습니다. 정답과 녹음을 각각 2초, 4초, 8초로 맞춰 길이별로 세 번 실행했습니다. 최고치를 전체 칸 수로 나누면 2초에서 칸당 20.64바이트, 4초에서 20.36바이트, 8초에서 20.20~20.22바이트였습니다. 비용과 누적 비용에 8바이트씩, 이동 기록에 4바이트를 쓰는 구조와 비슷했습니다. 이 값은 앞 표의 프로세스 최대 메모리와 측정 범위가 다릅니다. Python tracemalloc 문서

녹음과 기준 악보의 길이

분석 API는 정답 프레임 수와 디코딩된 녹음 프레임 수를 곱하고, 여유를 둔 칸당 24바이트를 적용합니다. 추정값이 4GiB를 넘으면 음높이 추정과 DTW를 시작하기 전에 요청을 중단합니다. 예를 들어 10ms 간격에서 정답과 녹음이 모두 600초면 각각 약 60,000프레임입니다. 칸당 20바이트의 실측값으로 계산하면 행렬이 약 67GiB에 이릅니다. 약 67GiB는 작은 입력에서 구한 칸당 비용을 긴 입력에 적용한 추정입니다.

이 검사를 할당 앞에 둔 이유는 메모리가 고갈되면 Linux가 프로세스 자체를 종료할 수 있기 때문입니다. 프로세스가 종료되면 Python 예외 처리기가 응답을 보낼 수 없고, 같은 프로세스에서 처리 중이던 요청도 함께 끊깁니다. Linux 커널의 OOM 처리

HTTP 테스트에서는 합성 녹음을 넣고 DTW 예산을 1바이트로 낮춰 HTTP 500과 ANALYSIS_ERROR 응답을 확인했습니다. 엔드포인트는 여러 분석 예외를 같은 응답으로 돌려줍니다. 어디에서 멈췄는지는 아래 경계 검사로 확인했습니다.

경계 검증에서는 디코더를 길이만 반환하는 대역으로 바꾸고, API 내부의 채점 함수를 직접 호출했습니다. 음높이 추정 대역에 도달하면 테스트용 예외를 던지게 해 RecordingTooLong과 구분했습니다. 정답과 녹음이 모두 133초일 때는 추정 단계에 도달했습니다. 둘 다 134초 또는 600초일 때는 RecordingTooLong이 발생해 그 전에 멈췄습니다. 반대로 정답이 15초인 경우에는 녹음이 600초여도 검사를 통과했습니다. 이 검증은 요청을 어디에서 멈추는지 확인하는 실험이어서 큰 녹음이나 행렬을 실제로 만들 필요가 없었습니다.

분석 엔드포인트는 동기 함수인 def analyze이고, FastAPI는 이 함수를 스레드 풀에서 실행합니다. 요청이 겹치면 같은 프로세스에 여러 요청의 배열이 동시에 놓일 수 있습니다. 각 요청의 DTW 추정값이 4GiB 아래여도, 그 합은 컨테이너의 6GiB 한도를 넘을 수 있습니다. 검사는 디코딩이 끝난 뒤 요청별로 적용됩니다. 두 요청이 동시에 실행되는 경우의 메모리와 다운로드·디코딩을 포함한 전체 처리 시간은 별도 검증 범위로 남았습니다. FastAPI의 동기 엔드포인트 실행