TL;DR
ruby-enum의 상속 열거형 병합 기능은 정확했지만, 하위 클래스의 조회를 상위 클래스보다 약 5배 느리게 만들었음.Copilot CLI에 한 문장으로 요청해 벤치마크 스크립트를 작성하면서 성능 회귀를 발견하고, 재실행 가능한rake benchmark:inheritance작업으로 저장함.- 병합된 해시를 메모이제이션하고
define으로 새 항목을 추가할 때만 캐시를 무효화해 상속 깊이와 무관하게 조회 성능을 맞춤. Ruby::Enum의 상수 접근은 일반 루비 상수와 같은 속도이며, 해시 기반 조회는 직접적인Hash#[]보다 3~5배,Case매처는 기본case문보다 50~100배 느림.- 테스트가 정확성만 확인하면 성능 회귀를 놓치기 쉽지만, 에이전트로 벤치마크를 작성하는 비용이 거의 사라짐.
상속 지원이 불러온 성능 회귀
- 이전 글에서는 관리 중인 젬
ruby-enum의 버그 네 가지를 다뤘으며, 모두 클래스 수준 인스턴스 변수가 하위 클래스로 상속되지 않는 데서 비롯됨. - 세 번째 수정인 #59는
keys,key?,value?,key,value,to_h,parse,each가 상위 클래스를 거슬러 올라가 부모 열거형을 병합하도록 변경함. 하위 클래스가 모든 조상 클래스의 정의를 확인할 수 있도록 한 올바른 수정이며 테스트를 모두 통과해 배포됐지만, 하위 클래스에서 해당 메서드가 모두 대략 5배 느려짐. _enum_hash는 호출할 때마다 상위 클래스의 해시를 가져와 자체 해시와 병합하며, 결과를 캐시하지 않음. 한 단계 하위 클래스에서.value를 호출하면 새 해시를 만들고, 상위 클래스도 같은 작업을 반복함.- 테스트는 정확성을 확인할 뿐 속도를 확인하지 않으며, 이 변경은 정확성에 문제가 없어 테스트에서 성능 저하가 드러나지 않음.
벤치마크로 성능 문제 발견
- README의 새 기능 옆에 ‘벤치마크’ 섹션을 어떻게 작성할지 궁금해
Copilot CLI에 상속 깊이에 따른 조회 성능을 비교하는 벤치마크 스크립트를 요청함. 프롬프트 하나와 약 1분 만에 작동하는 스크립트를 얻음. - 벤치마크는 기본 클래스
Colors, 한 단계 하위 클래스SubColors, 두 단계 하위 클래스SubSubColors를 만들고, 각 클래스에서.value(:RED)를 호출해 비교함. - 첫 실행에서 한 단계 하위 클래스의
.value호출은 기본 클래스보다 대략 5배 느렸고, 두 단계 하위 클래스는 더 느렸음. - #59를 병합하기 전에 이 벤치마크를 작성하지 않은 이유는 일회성 성능 확인에 쓰는 임시 스크립트 작성이 그만한 가치가 있는 것보다 번거롭게 느껴졌기 때문임. 이제는 에이전트에게 한 문장으로 요청할 수 있으며, 스크립트는 누구나 다시 실행할 수 있는
rake benchmark:inheritance작업으로 저장됨.
캐시 적용과 조회 성능 개선
- 수정은 병합된 해시를 메모이제이션하고,
define이 새 항목을 추가할 때만 캐시를 무효화하는 방식임. - 수정 후 #60에서 같은 벤치마크를 다시 실행한 결과는 기본 클래스 0.0627, 한 단계 하위 클래스 0.0608, 두 단계 하위 클래스 0.0606임.
- 이제 상속 깊이와 관계없이 하위 클래스의 조회 성능은 기본 클래스와 구별할 수 없는 수준임.
README에 공개한 성능 비용
- 젬의 오버헤드에 관한 README 설명을 작성하기 전에 근거 없이 ‘미미하다’고 쓰지 않기 위해,
Copilot CLI로 #61 벤치마크를 추가 작성함. 이 벤치마크는 기본Ruby::Enum연산과 일반 루비 연산을 비교함. - 상수 접근은 비용이 없으며,
Colors::RED는 일반 루비 상수 접근과 정확히 같은 속도임. .value와.key?같은 해시 기반 조회는Hash#[]를 직접 호출하는 것보다 3~5배 느림. 이는Ruby::Enum의 추가 메서드 디스패치와 객체 래핑에 따른 비용임. 알아둘 만한 수치지만 젬 사용을 피할 정도는 아님.- 전수 매칭을 수행하는
Ruby::Enum::Case는 네이티브case문보다 50~100배 느림. 이 수치와 다른 성능 결과는 README의 전용 성능 섹션에 공개돼 있어 질문하거나 추측할 필요가 없음.
벤치마크 작성 비용의 변화
- 테스트가 정확성만 확인하면 성능 회귀는 도입하기도 쉽고 놓치기도 쉬움.
- 달라진 점은 성능 회귀를 찾는 데 드는 비용임. 예전에는 ‘그냥 getter 메서드’ 변경을 확인하기 위한 벤치마크가 금요일 오후에 생략할 법한 작업이었지만, 이제 벤치마크 작성 비용은 거의 없음.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요