본문으로 건너뛰기

[feast] Feast Feature Server의 직렬화 성능 4배 향상: MessageToDict 최적화

PR 링크: feast-dev/feast#6015 상태: Merged | 변경: +764 / -11

들어가며

Feast의 Feature Server는 온라인 피처 서빙을 위해 GetOnlineFeaturesResponse를 클라이언트에 반환합니다. 기존에는 google.protobuf.json_format.MessageToDict를 사용하여 Protobuf 메시지를 JSON으로 변환해왔습니다. 하지만 MessageToDict는 범용적인 라이브러리로서, 실행 시점에 필드를 탐색하는 리플렉션(reflection) 기반의 방식을 사용하기 때문에 대규모 피처 응답 시 상당한 오버헤드가 발생합니다. 본 PR은 이 병목을 해결하기 위해 커스텀 직렬화 로직을 도입하여 성능을 4배 향상시켰습니다.

코드 분석

1. sdk/python/feast/feature_server.py: 범용 라이브러리 제거

기존에는 MessageToDict를 직접 호출하여 직렬화했으나, 이제는 최적화된 유틸리티 함수를 사용하도록 변경되었습니다.

# Before
response_dict = await run_in_threadpool(
    MessageToDict,
    response.proto,
    preserving_proto_field_name=True,
    float_precision=18,
)

# After
response_dict = await run_in_threadpool(
    convert_response_to_dict, response.proto
)
return JSONResponse(content=response_dict)

2. sdk/python/feast/feature_server_utils.py: 커스텀 직렬화 로직 구현

새로 추가된 convert_response_to_dictGetOnlineFeaturesResponse의 구조를 미리 알고 있으므로, 리플렉션 없이 직접 필드에 접근합니다.

def convert_response_to_dict(response: GetOnlineFeaturesResponse) -> Dict[str, Any]:
    result: Dict[str, Any] = {
        "results": [
            {
                "values": [_value_to_native(v) for v in feature_vector.values],
                "statuses": [_STATUS_NAMES.get(s, "INVALID") for s in feature_vector.statuses],
                # ... (timestamp 처리)
            }
            for feature_vector in response.results
        ]
    }
    # ...
    return result

특히 bytes 타입 처리 시 base64 인코딩을 명시적으로 수행하여 JSONResponse가 직렬화 오류를 일으키지 않도록 처리한 점이 돋보입니다.

왜 이게 좋은가

  1. 리플렉션 제거: MessageToDict는 모든 필드를 동적으로 검사하지만, 커스텀 빌더는 고정된 스키마를 따라 직접 값을 추출합니다. 이로 인해 CPU 사이클을 크게 절약합니다.
  2. 성능 수치: 일반적인 피처 응답(50-200개 피처)에서 요청당 5-15ms의 지연 시간을 단축하며, 직렬화 속도가 약 4배 향상되었습니다.
  3. 안정성: bytes 타입에 대한 base64 인코딩 처리를 통해 json.dumps 호출 시 발생할 수 있는 TypeError를 방지했습니다.

교훈

범용 라이브러리는 편리하지만, 특정 데이터 구조가 고정되어 있고 성능이 중요한 핫패스(hot path)에서는 직접적인 데이터 매핑이 훨씬 효율적입니다. 특히 Protobuf와 같이 스키마가 명확한 경우에는 타입별로 최적화된 직렬화 로직을 작성하는 것이 큰 성능 이득을 가져옵니다.

리뷰어 피드백 반영

리뷰 과정에서 bytes_val 직렬화 시 발생하는 TypeError 문제를 지적받았고, 이를 base64 인코딩으로 해결한 점은 매우 실무적인 피드백이었습니다. 또한 ORJSONResponse가 FastAPI에서 deprecated 되었다는 점을 고려하여 표준 JSONResponse를 사용하도록 조정된 점도 유지보수 측면에서 훌륭한 결정입니다.

참고 자료

⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.

댓글

관련 포스트

PR Analysis 의 다른글