Ruby 3.4 REXML XML 파싱 성능 개선 및 StringScanner 활용

[JA] Improvement of REXML and speed up using StringScanner / NAITOH Jun @naitoh

3줄 요약

  • Ruby 3.4의 REXML 라이브러리에서 StringScanner를 활용하여 XML 파싱 성능을 최대 60%까지 개선했습니다.
  • 이는 보안 취약점 패치로 인한 성능 저하를 극복하고, StringScanner의 효율적인 문자열 처리 기능을 적극 활용한 결과입니다.
  • 불필요한 객체 생성을 피하고 문자열 매칭 최적화를 통해 다양한 XML 파서의 속도를 향상시켰습니다.

본 발표는 Ruby 3.4 버전에서 XML 파싱 라이브러리인 REXML의 성능을 획기적으로 개선한 과정과 핵심 기술인 StringScanner의 활용 방안을 제시합니다. 발표자는 RBPDF 젬의 SVG 이미지 PDF 지원 동기에서 시작하여, 보안 취약점 패치로 인한 REXML 성능 저하를 극복하고 고속의 XML 파싱을 재구현하기 위한 1년간의 노력을 상세히 설명합니다.

REXML은 Ruby의 표준 XML 처리 라이브러리로, DOM, SAX2, Pull, Stream 등 네 가지 파서를 제공합니다. Ruby 3.4에 번들된 REXML 3.4.0은 기존 3.2.6 대비 최대 50%, 최신 3.4.1 버전은 최대 60%까지 파싱 속도가 향상되었습니다. 이러한 성능 개선의 핵심은 StringScanner의 도입과 최적화된 사용입니다. StringScanner는 문자열을 순차적으로 스캔하며, memcmp 기반의 빠른 문자열 매칭(scan_until(String))을 지원하여 일반적인 정규 표현식보다 월등히 빠른 속도를 제공합니다.

발표자는 불필요한 MatchData 객체 생성과 무거운 정규 표현식 처리가 성능 저하의 주요 원인임을 지적하며, StringScannercheck? 또는 match? 메서드로 객체 생성을 회피하고, peek_byte와 같은 메서드로 문자열 객체 자체를 생성하지 않음으로써 이를 해결했습니다. 또한, Regexp.escape와 같은 느린 연산에는 상수화 및 메모이제이션을 적용하여 성능을 더욱 끌어올렸습니다. 이러한 최적화 기법들은 특히 긴 문자열 처리 시 성능 저하를 최소화하는 데 기여했습니다.

성능 개선 외에도, 유효하지 않은 XML 형식에 대한 엄격한 검사 기능이 강화되었고, REXML의 다양한 파서 간 파싱 결과의 일관성이 확보되었습니다. 이는 개발자가 XML 유효성 검사를 별도로 수행할 필요를 없애고, 모든 파서에서 동일한 보안 조치가 적용되도록 하여 라이브러리의 신뢰성을 높였습니다.

결론적으로, 본 발표는 Ruby 3.4의 REXML 라이브러리에서 StringScanner를 효과적으로 활용함으로써 XML 파싱 성능을 크게 향상시킬 수 있음을 입증했습니다. 불필요한 문자열 객체 생성을 피하고, StringScanner의 고성능 문자열 매칭 기능을 적극적으로 사용하는 것이 핵심 전략이었습니다. 이러한 개선은 REXML의 사용성을 높이고, Ruby 기반 애플리케이션에서 XML 처리 성능을 요구하는 다양한 시나리오에 긍정적인 영향을 미칠 것으로 기대됩니다. 향후 StringScanner의 새로운 기능을 활용한 추가적인 최적화와 다른 젬으로의 기술 전파 가능성이 시사되었습니다.