Zig의 C 라이브러리 재작성 과정에서 문자열 처리 방식이 바뀌면서, 한 체스 분석 서비스의 SQLite 데이터 저장이 크게 느려졌다. 서비스 운영자는 1.2MB 파일을 토큰화할 때 속도가 약 12,000배 느려졌다고 보고했다.
운영자는 Rust와 SQLite를 사용하는 체스 분석 서비스를 운영한다. 체스 게임 수백 개를 처리한 실행기들이 대량의 JSON 데이터를 보내면, 주 서비스가 이를 분석해 SQLite에 저장한다. 평소에는 밀리초, 심지어 밀리초 미만이면 끝날 것으로 예상한 저장 작업이 약 4만 건을 처리할 때 몇 분이나 걸렸다. 작업은 CPU 코어 하나를 모두 사용하는 상태였다.
원인은 Linux용 SQLite를 빌드할 때 사용한 zig cc에서 비롯됐다. 프로젝트 자체는 Rust로 작성됐지만, 여러 플랫폼용 빌드에 cargo-zigbuild를 사용하면서 Linux 대상 SQLite를 Zig 컴파일러로 컴파일했다. 이때 사용된 Zig의 C 표준 라이브러리 재작성본에서 문자열 함수의 동작이 문제를 일으켰다.
운영자가 지목한 strspn 재작성은 처리에 앞서 문자열 전체 길이를 측정한다. strcspn, strpbrk, strstr의 재작성에서도 비슷한 방식이 나타난다. 긴 문자열을 처리하는 토크나이저가 공백을 건너뛴 뒤 다음 위치에서 같은 작업을 반복하면, 매 단계마다 문자열 전체 길이를 다시 재게 된다. 글쓴이는 이를 작은 선택이 전체 처리 시간을 크게 키우는 ‘화가 쉬멜’ 패턴이라고 설명했다.
SQLite에 전달된 JSON을 토큰화할 때 이 문제가 드러났고, Zig를 사용하지 않은 Mac 빌드에서는 나타나지 않았다. 운영자의 측정에 따르면 1.2MB 파일의 토큰화 속도는 이 패턴에서 약 12,000배 느려졌다. 파일이 커질수록 속도 저하도 급격히 커진다고 덧붙였다.
운영자는 처음에는 자신의 코드에서 JSON의 공백을 미리 제거해 토큰화 비용을 줄였다. 다른 곳에서도 문제가 생길 수 있다고 판단한 뒤에는 Linux 빌드를 Linux 컨테이너로 옮겼다. 이 경험은 조엘 스폴스키의 ‘Back to Basics’에서 다룬 화가 쉬멜 패턴이 최신 도구에서도 다시 나타날 수 있음을 보여준다고 설명한다. 과거 Firefox의 문자열 연결에서도 비슷한 문제가 있었다는 사례와, 체스 분석 서비스 관련 글도 덧붙였다.
문제가 된 Zig의 strspn 재작성과 musl 구현을 순수 Zig 구현으로 바꾼 변경을 참고할 수 있다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요