14P by neo 1달전 | favorite | 댓글 3개
  • recall은 수백개의 회사에 회의용 봇을 제공하는 서비스로 AWS에서 대규모 인프라를 운영하고 있음
  • 비용 효율적인 서비스를 위해 하드웨어 성능을 최대한 활용하려 함
  • 지난 몇 년 동안 클라우드 제공업체의 GPU 가용성이 불안정했기 때문에 비디오 처리를 GPU가 아닌 CPU에서 수행
  • 헤드리스 Chromium을 사용하는 봇을 프로파일링한 결과, 대부분의 CPU 시간이 비디오 처리(인코딩/디코딩)이 아닌 메모리 복사 함수 __memmove_avx_unaligned_erms__memcpy_avx_unaligned_erms에서 소비되고 있었음
    • memmovememcpy는 C 표준 라이브러리(glibc)에 있는 메모리 블록 복사 함수
    • memmove는 겹치는 범위의 메모리 복사와 관련된 몇 가지 예외 사례를 처리하지만, 두 함수 모두 "메모리 복사" 함수로 분류할 수 있음
    • avx_unaligned_erms 접미사는 Advanced Vector Extensions(AVX)를 지원하는 시스템에 최적화되어 있으며, 정렬되지 않은 메모리 접근에도 최적화되어 있음을 의미
    • erms는 Enhanced REP MOVSB/STOSB의 약자로, 최신 Intel 프로세서에서 빠른 메모리 이동을 위한 최적화. "특정 프로세서를 위한 더 빠른 구현"을 의미한다고 볼 수 있음
  • 프로파일링 결과, 이 함수들을 가장 많이 호출한 것은 데이터를 수신하는 Python WebSocket 클라이언트였음
    • 그 다음으로는 데이터를 전송하는 Chromium의 WebSocket 구현체가 많이 호출했음

WebSocket의 문제점

  • 원시 비디오 데이터를 Chromium의 JS 환경에서 인코더로 전송하기 위해 로컬 WebSocket 서버를 사용했음
  • 원시 1080p 30fps 비디오 스트림은 초당 93MB 이상의 높은 대역폭을 요구함
  • WebSocket 사용이 많은 연산 비용을 초래했는데, 주된 원인은 단편화(fragmentation)와 마스킹(masking)이었음
    • 단편화: Chromium의 WebSocket 구현체는 131KB 이상의 메시지를 여러 프레임으로 단편화함. 3MB 이상의 원시 비디오 프레임은 24개 이상의 별도 프레임으로 나뉘어 전송됨
    • 마스킹: 보안상의 이유로 WebSocket은 클라이언트에서 서버로 전송되는 모든 프레임을 마스킹함. 초당 100MB 이상의 대용량 데이터에서는 의미있는 오버헤드가 됨

대안 모색

  • 브라우저 API로는 WebSocket보다 훨씬 더 성능이 좋은 것을 구현하기 어려워 Chromium을 포크하여 사용자 정의 기능을 구현하기로 함
  • 3가지 대안을 고려했음: raw TCP/IP, Unix Domain Socket, Shared Memory
    • TCP/IP: WebSocket의 단편화/마스킹 문제는 피할 수 있지만 최대 패킷 크기가 작아 여전히 단편화 문제가 있음. 커널 공간으로의 복사 오버헤드도 있음
    • Unix Domain Socket: 네트워크 스택을 완전히 건너뛸 수 있지만 사용자 공간과 커널 공간 사이에 데이터 복사가 필요함
    • Shared Memory: 여러 프로세스가 동시에 접근할 수 있는 메모리. 중간에 복사 없이 Chromium이 공유 메모리에 직접 쓰고 인코더가 바로 읽을 수 있음

공유 메모리 기반 전송 구현

  • 공유 메모리에 데이터를 연속적으로 읽고 쓰기 위해 링 버퍼 형태로 구현
  • 요구사항: lock-free, 다중 생산자/단일 소비자, 가변 프레임 크기, 무복사 읽기, 샌드박스 친화성, 저지연 시그널링
  • 기성 링버퍼 구현체를 평가했으나 요구사항을 모두 만족하는 것이 없어 직접 구현하기로 함
  • 무복사 읽기를 지원하기 위해 포인터를 write, peek, read 세 개로 나누었음
  • 스레드 안전성을 위해 원자적 연산을 사용하고, 새 데이터 발생/공간 확보를 알리기 위해 named semaphore를 사용함
  • 공유 메모리 기반 링버퍼 구현과 기타 최적화를 통해 봇의 CPU 사용량을 최대 50%까지 감소시킬 수 있었음. 결국 AWS 비용을 연간 백만 달러 이상 절감함.
Hacker News 의견
  • 한 스타트업이 "충분히 괜찮은" 지름길을 선택하고 나중에 최적화하는 전형적인 이야기임.

    • 한 회사에서 CPU 사용량이 높은 VM 클러스터가 있었고, 이를 최적화하기 위해 프로파일러를 사용했음.
    • 오래된 데이터를 삭제하고 쿼리에 필터를 추가하여 CPU 사용량을 줄였음.
  • 원시 비디오 데이터의 높은 대역폭이 놀랍다는 의견이 있음.

    • WebSockets의 설계 결정을 비판하며 CPU 사용량 문제를 예상하지 못했음.
  • AWS 문제가 아니라 CPU 사이클을 낭비한 문제라는 의견이 있음.

    • WebSockets는 데이터 전송이나 API 게이트웨이 비용과 관련이 있음.
  • TCP/IP 네트워크의 MTU와 MSS가 비디오 프레임 크기에 비해 작다는 점을 지적함.

    • 기술적 지식 부족을 지적하며 개발자를 고용할 필요가 있다고 주장함.
  • Chromium의 Mojo를 사용하여 플랫폼별 코드를 걱정할 필요가 없다는 의견이 있음.

    • 커스텀 링 버퍼 구현도 괜찮다고 생각함.
  • 네트워크 문제가 아니라 비디오 코덱에 대한 이해 부족이 문제라는 의견이 있음.

    • RDP와 같은 비디오 스트리밍 프로토콜을 사용하지 않는 이유를 이해할 수 없다고 함.
  • 투명성을 칭찬하며 제품 가격에 대한 투명성도 원한다고 함.

  • WebSocket 프로토콜의 마스킹이 중간자의 문제를 해결하려는 시도라고 설명함.

    • 관련 RFC를 읽어볼 가치가 있다고 함.
  • 비디오 데이터를 압축하지 않고 전송하는 방식이 이상하다고 지적함.

    • 압축된 스트림을 전송하지 않는 이유를 이해할 수 없다고 함.
  • 원시 비디오를 WebSocket으로 전송하는 초기 접근 방식이 놀랍다고 함.

    • 비효율성이 제품 개발에 방해가 되지 않았다고 함.
    • 데이터의 양을 고려하지 않는 접근 방식을 이해할 수 없다고 함.
  • 제품 개발 시 성능에 대한 고려는 전혀 없었다고 본다.
  • 이 문제는 결국 대량 데이터를 어떻게 IPC 할것 인지의 문제로 귀결됨.
  • 차이점은 일반적인 IPC가 아닌, 크롬 브라우져와 IPC 한다는 것이고
  • 크롬 브라우져 내부 방식은 그리 쉽지는 않겠지만, 오픈되어 있으니 수정은 가능
  • 그럼 결국 IPC 선택의 문제임

첨부터 개발을 잘못 했구만..

"원시 비디오를 WebSocket으로 전송하는 초기 접근 방식이 놀랍다고 함." 이 말에 공감.