본문으로 건너뛰기

[Ray] ActorHandle의 __hash__ 캐싱 및 __eq__ 정확성 수정

PR 링크: ray-project/ray#61638 상태: Merged | 변경: +63 / -15

들어가며

Ray의 ActorHandle.__hash__는 호출될 때마다 Python -> Cython -> C++ 전체 경로를 순회하고 있었다. Actor ID는 불변이므로 해시값을 캐싱할 수 있는데 그렇게 하지 않고 있었다. 또한 __eq__hash(self) == hash(other)로 구현되어 있어, 해시 충돌 시 서로 다른 actor를 동일하다고 판단하는 정확성 버그가 있었다.

핵심 코드 분석

Before: 매번 해시 재계산 + 부정확한 동등성 비교

def __hash__(self):
    return hash(self._actor_id)

def __eq__(self, __value):
    return hash(self) == hash(__value)

After: 해시 캐싱 + Actor ID 직접 비교

def __hash__(self):
    # __dict__에서 직접 조회하여 __getattr__을 우회
    # (cross-language actor에서 __getattr__이 ActorMethod를 반환하는 문제 방지)
    try:
        return self.__dict__["_ray_cached_hash"]
    except KeyError:
        h = hash(self._ray_actor_id)
        self._ray_cached_hash = h
        return h

def __eq__(self, other):
    if not isinstance(other, ActorHandle):
        return NotImplemented
    return self._ray_actor_id == other._ray_actor_id

마이크로벤치마크 결과 (50 actors)

연산 Before After 개선
eq same (h == h) 335 ns 217 ns -35%
eq different (a==b) 331 ns 209 ns -37%
eq non-ActorHandle 206 ns 78 ns -62%
dict insert d[h]=i 151 ns 106 ns -30%
dict lookup d[h] 128 ns 86 ns -33%

왜 이게 좋은가

  1. 해시 캐싱: 불변 ID의 해시를 한 번만 계산하고 캐싱하여, dict/set 연산에서 반복 해시 계산을 제거한다.
  2. 정확성 수정: hash(a) == hash(b)는 해시 충돌 시 false positive를 만든다. Actor ID 직접 비교로 정확한 동등성을 보장한다.
  3. NotImplemented 반환: 비 ActorHandle 타입과의 비교에서 NotImplemented를 반환하여 Python의 비교 프로토콜을 올바르게 따른다.
  4. Ray Data 핵심 경로: Ray Data의 actor pool operator는 ActorHandle을 dict 키로 빈번하게 사용하므로, 이 최적화의 실제 영향이 크다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글