- recall là dịch vụ cung cấp bot họp cho hàng trăm công ty và đang vận hành hạ tầng quy mô lớn trên AWS
- Công ty muốn tận dụng tối đa hiệu năng phần cứng để cung cấp dịch vụ hiệu quả về chi phí
- Trong vài năm qua, do khả năng sẵn có của GPU từ các nhà cung cấp cloud không ổn định, nên xử lý video được thực hiện trên CPU thay vì GPU
- Khi profiling các bot dùng headless Chromium, họ phát hiện phần lớn thời gian CPU không được dùng cho xử lý video (mã hóa/giải mã), mà lại tiêu tốn trong các hàm sao chép bộ nhớ
__memmove_avx_unaligned_erms và __memcpy_avx_unaligned_erms
memmove và memcpy là các hàm sao chép khối bộ nhớ trong thư viện chuẩn C (glibc)
memmove xử lý một số trường hợp ngoại lệ liên quan đến sao chép bộ nhớ ở các vùng chồng lấn, nhưng cả hai đều có thể được xếp vào nhóm hàm "sao chép bộ nhớ"
- Hậu tố
avx_unaligned_erms cho biết chúng được tối ưu cho hệ thống hỗ trợ Advanced Vector Extensions (AVX), đồng thời tối ưu cho cả truy cập bộ nhớ không căn chỉnh
erms là viết tắt của Enhanced REP MOVSB/STOSB, một tối ưu cho việc di chuyển bộ nhớ nhanh trên các bộ xử lý Intel hiện đại. Có thể hiểu là "triển khai nhanh hơn cho một số bộ xử lý cụ thể"
- Kết quả profiling cho thấy thành phần gọi các hàm này nhiều nhất là Python WebSocket client nhận dữ liệu
- Đứng sau đó là implementation WebSocket của Chromium dùng để gửi dữ liệu, cũng gọi chúng rất nhiều
Vấn đề của WebSocket
- Họ sử dụng một máy chủ WebSocket cục bộ để truyền dữ liệu video thô từ môi trường JS của Chromium sang encoder
- Một luồng video thô 1080p 30fps đòi hỏi băng thông cao hơn 93MB mỗi giây
- Việc dùng WebSocket gây ra chi phí tính toán lớn, mà nguyên nhân chính là fragmentation và masking
- Fragmentation: implementation WebSocket của Chromium sẽ chia nhỏ các message lớn hơn 131KB thành nhiều frame. Một frame video thô lớn hơn 3MB sẽ bị tách thành hơn 24 frame riêng biệt để truyền đi
- Masking: vì lý do bảo mật, WebSocket sẽ mask mọi frame truyền từ client lên server. Với dữ liệu khối lượng lớn vượt 100MB mỗi giây, đây trở thành một overhead đáng kể
Tìm giải pháp thay thế
- Với các API trình duyệt hiện có, rất khó để triển khai thứ gì đó có hiệu năng tốt hơn nhiều so với WebSocket, nên họ quyết định fork Chromium để tự thêm chức năng tùy biến
- Họ cân nhắc 3 phương án thay thế: raw TCP/IP, Unix Domain Socket, Shared Memory
- TCP/IP: có thể tránh được vấn đề fragmentation/masking của WebSocket, nhưng kích thước packet tối đa vẫn nhỏ nên vẫn còn vấn đề phân mảnh. Ngoài ra còn có overhead sao chép vào kernel space
- Unix Domain Socket: có thể bỏ qua hoàn toàn network stack, nhưng vẫn cần sao chép dữ liệu giữa user space và kernel space
- Shared Memory: là bộ nhớ mà nhiều process có thể cùng truy cập. Chromium có thể ghi trực tiếp vào vùng nhớ chia sẻ mà không cần sao chép trung gian, và encoder có thể đọc ngay từ đó
Triển khai truyền dữ liệu dựa trên shared memory
- Họ triển khai theo dạng ring buffer để đọc và ghi dữ liệu liên tục trong shared memory
- Yêu cầu gồm: lock-free, đa producer/đơn consumer, kích thước frame biến đổi, đọc zero-copy, thân thiện với sandbox, signaling độ trễ thấp
- Họ đã đánh giá các implementation ring buffer có sẵn nhưng không có cái nào đáp ứng toàn bộ yêu cầu, nên quyết định tự xây dựng
- Để hỗ trợ đọc zero-copy, họ tách con trỏ thành ba loại: write, peek, read
- Để bảo đảm thread safety, họ dùng atomic operation và dùng named semaphore để báo có dữ liệu mới / có không gian trống
- Nhờ implementation ring buffer dựa trên shared memory cùng các tối ưu khác, họ có thể giảm mức sử dụng CPU của bot xuống tối đa 50%. Kết quả là tiết kiệm được hơn 1 triệu USD chi phí AWS mỗi năm.
3 bình luận
Ý kiến Hacker News
Đây là một câu chuyện điển hình về việc một startup chọn lối tắt “đủ ổn” rồi tối ưu hóa sau.
Có ý kiến cho rằng mức băng thông cao của dữ liệu video thô là điều đáng kinh ngạc.
Có ý kiến cho rằng đây không phải vấn đề của AWS mà là vấn đề lãng phí chu kỳ CPU.
Có người chỉ ra rằng MTU và MSS của mạng TCP/IP nhỏ so với kích thước khung hình video.
Có ý kiến cho rằng có thể dùng Mojo của Chromium để không phải lo về mã theo từng nền tảng.
Có người cho rằng vấn đề không nằm ở mạng mà là ở sự thiếu hiểu biết về codec video.
Có người khen ngợi sự minh bạch và nói rằng cũng muốn thấy sự minh bạch về giá sản phẩm.
Có người giải thích rằng cơ chế masking của giao thức WebSocket là một nỗ lực nhằm giải quyết vấn đề kẻ trung gian.
Có người chỉ ra rằng việc truyền dữ liệu video mà không nén là điều kỳ lạ.
Có người nói cách tiếp cận ban đầu là truyền video thô qua WebSocket thật đáng ngạc nhiên.
Ngay từ đầu đã làm sai cách rồi..
"Cách tiếp cận ban đầu là truyền video thô qua WebSocket thật sự khiến người ta ngạc nhiên." Đồng cảm với câu này.