본문으로 건너뛰기

[Ray Serve] stop_replicas()의 pop-all/re-add 사이클 제거

PR 링크: ray-project/ray#60832 상태: Merged | 변경: +35 / -6

들어가며

Ray Serve의 stop_replicas() 함수는 특정 replica를 중지할 때, 전체 7개 상태의 모든 replica를 pop한 다음, 중지 대상이 아닌 것들을 다시 add하는 방식으로 동작했습니다. 각 re-add마다 update_actor_details()가 호출되어 pydantic 모델을 재생성합니다. 4096개 replica 중 2개만 중지해도 4094개의 불필요한 pop/re-add가 발생하는 심각한 비효율이 있었습니다.

핵심 코드 분석

Before: 전체 pop 후 re-add

def stop_replicas(self, replicas_to_stop) -> None:
    for replica in self._replicas.pop():
        if replica.replica_id in replicas_to_stop:
            self._stop_replica(replica)
        else:
            self._replicas.add(replica.actor_details.state, replica)

self._replicas.pop()이 모든 상태의 모든 replica를 리스트로 반환합니다. 대상이 아닌 replica는 add()로 다시 넣으면서 update_state()가 호출되어 pydantic ReplicaDetails를 재구성합니다.

After: ID set 기반 단일 패스 remove

def remove(self, replica_ids: Set[ReplicaID]) -> List[DeploymentReplica]:
    replica_ids = set(replica_ids)
    removed = []
    remaining_to_find = len(replica_ids)
    for state in ALL_REPLICA_STATES:
        if remaining_to_find == 0:
            break
        found_any = False
        remaining = []
        for replica in self._replicas[state]:
            if remaining_to_find > 0 and replica.replica_id in replica_ids:
                removed.append(replica)
                remaining_to_find -= 1
                found_any = True
            else:
                remaining.append(replica)
        if found_any:
            self._replicas[state] = remaining
    return removed

def stop_replicas(self, replicas_to_stop: Set[ReplicaID]) -> None:
    for replica in self._replicas.remove(replicas_to_stop):
        self._stop_replica(replica)

핵심 최적화:

  • O(1) set lookup으로 대상 replica 식별
  • 매칭되지 않는 replica는 제자리에 유지 (re-add 없음)
  • remaining_to_find로 모든 대상을 찾으면 early-exit
  • 매칭이 발생한 상태에서만 리스트 재구성

왜 이게 좋은가

벤치마크 결과가 극적입니다:

시나리오 (4096 replicas) Old New 개선
2개 중지 (다운스케일) 3402µs 587µs 5.8x
전체 중지 (teardown) 1636µs 1378µs 1.2x
10% 중지 (409개) 3301µs 1299µs 2.5x
  • update_state 호출: Old는 4094번, New는 0번 (2개 중지 시)
  • 메모리: 257KB -> 33KB (224KB 절약, 2개 중지 시)

일반적인 다운스케일 시나리오(소수만 중지)에서 최대 6배의 속도 향상과 메모리 절약을 달성합니다. 불필요한 객체 재생성을 제거한 알고리즘 최적화의 훌륭한 사례입니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글