본문에서는 json
gem의 성능을 개선하기 위해 적용된 구체적인 최적화 기법들을 상세히 설명합니다.
-
불필요한 중복 검사 회피 (Avoid Redundant Checks):
JSON.dump
벤치마크 프로파일링 결과,isLegalUTF8
및rb_enc_str_asciionly_p
함수에서 상당한 시간이 소요됨을 발견했습니다. Ruby 문자열의coderange
속성을 활용하여 UTF-8 유효성 검사를 한 번만 수행하도록 코드를 개선했습니다. 이는 문자열을 두 번 스캔하는 대신 최대 한 번만 스캔하도록 하여 3%의 성능 향상을 가져왔습니다. 이는 메모리 로딩 비용이 높기 때문에 예상만큼 큰 성능 향상은 아니었지만, 여전히 유의미한 개선이었습니다. -
더 저렴하고 가능성이 높은 조건 먼저 확인 (Check the Cheaper, More Likely Condition First):
fbuffer_inc_capa
함수에서 버퍼 할당 여부를 확인하는 데 5.7%의 시간이 소요됨을 확인했습니다. 이 함수는 대부분의 경우 버퍼가 이미 할당된 상태에서 호출되므로,RB_UNLIKELY
매크로를 사용하여 CPU가 가장 일반적인 경우를 먼저 예측하고 실행하도록 코드를 재작성했습니다. 또한 함수를inline
으로 선언하여 함수 호출 비용을 줄였습니다. 이러한 변경으로 15%의 성능 향상을 달성했으며, 이는 C 코드뿐만 아니라 Ruby 코드에도 적용될 수 있는 일반적인 최적화 기법임을 강조합니다. -
초기 설정 비용 절감 (Reducing Setup Cost): Yusuke Endoh(Mame)의 기여로 JSON 생성의 ‘설정’ 비용이 크게 줄었습니다.
JSON.generate
의pretty
옵션(array_nl
,object_nl
,indent
,space
)과 관련된 문자열 세그먼트를 미리 계산하는 방식이 실제로는 더 느리다는 것을 발견하고, 이 최적화를 되돌렸습니다. 이는 마이크로 벤치마크에서 51%라는 매우 큰 성능 향상을 가져왔습니다. -
포인터 추적 회피 (Avoid Chasing Pointers):
rb_enc_get
호출을 제거하여 성능을 개선했습니다.rb_enc_get
은 다양한 타입 체크와 고수준 API 호출로 인해 느렸습니다. 대신, 문자열 객체에 직접 저장된encoding_index
(enc_idx
)를 사용하여 인코딩 호환성을 직접 확인하도록 변경했습니다. 이는 실제rb_encoding *
포인터를 로드할 필요 없이 인덱스 비교만으로 가능하게 하여 8%의 성능 향상을 가져왔습니다. -
룩업 테이블 활용 (Lookup Tables): 문자열을 JSON으로 덤프할 때 각 문자를 이스케이프해야 하는지 확인하는 데 드는 비용을 줄이기 위해 ‘룩업 테이블’을 사용했습니다. 미리 계산된 정적 배열을 사용하여 여러 조건 검사 대신 단일 배열 참조로 이스케이프 여부를 판단하도록 했습니다. 대부분의 문자열에 이스케이프가 필요한 문자가 없다는 가정하에, 먼저 빠르게 검사하여 전체 문자열을 한 번에 복사하는 방식을 적용하여
twitter.json
벤치마크에서 30%라는 상당한 성능 향상을 이끌어냈습니다.