- recall เป็นบริการที่ให้บอตสำหรับการประชุมแก่บริษัทหลายร้อยแห่ง และดำเนินโครงสร้างพื้นฐานขนาดใหญ่บน AWS
- เพื่อให้บริการมีประสิทธิภาพด้านต้นทุน จึงพยายามใช้ประโยชน์จากสมรรถนะของฮาร์ดแวร์ให้ได้มากที่สุด
- ตลอดหลายปีที่ผ่านมา ความพร้อมใช้งานของ GPU จากผู้ให้บริการคลาวด์ไม่เสถียร จึงประมวลผลวิดีโอบน CPU แทน GPU
- เมื่อลองทำโปรไฟล์บอตที่ใช้ headless Chromium พบว่าเวลาส่วนใหญ่ของ CPU ไม่ได้ถูกใช้ไปกับการประมวลผลวิดีโอ (encoding/decoding) แต่ถูกใช้ไปกับฟังก์ชันคัดลอกหน่วยความจำ
__memmove_avx_unaligned_erms และ __memcpy_avx_unaligned_erms
memmove และ memcpy คือฟังก์ชันคัดลอกบล็อกหน่วยความจำใน C standard library (glibc)
memmove รองรับกรณีพิเศษบางอย่างที่เกี่ยวข้องกับการคัดลอกหน่วยความจำในช่วงที่ทับซ้อนกัน แต่ทั้งสองฟังก์ชันก็จัดอยู่ในกลุ่มฟังก์ชัน "คัดลอกหน่วยความจำ" ได้เหมือนกัน
- suffix
avx_unaligned_erms หมายถึงการปรับแต่งให้เหมาะกับระบบที่รองรับ Advanced Vector Extensions (AVX) และยังเหมาะกับการเข้าถึงหน่วยความจำที่ไม่จัดแนวด้วย
erms ย่อมาจาก Enhanced REP MOVSB/STOSB ซึ่งเป็นการปรับแต่งสำหรับการย้ายหน่วยความจำอย่างรวดเร็วบนโปรเซสเซอร์ Intel รุ่นใหม่ พูดง่าย ๆ คือหมายถึง "implementation ที่เร็วขึ้นสำหรับโปรเซสเซอร์บางประเภท"
- จากผลการทำโปรไฟล์ พบว่าตัวที่เรียกใช้ฟังก์ชันเหล่านี้มากที่สุดคือ Python WebSocket client ที่รับข้อมูล
- รองลงมาคือ implementation ของ WebSocket ใน Chromium ที่ใช้ส่งข้อมูล
ปัญหาของ WebSocket
- ใช้ WebSocket server ภายในเครื่องเพื่อส่งข้อมูลวิดีโอดิบจากสภาพแวดล้อม JS ของ Chromium ไปยัง encoder
- สตรีมวิดีโอดิบ 1080p 30fps ต้องใช้แบนด์วิดท์สูงกว่า 93MB ต่อวินาที
- การใช้ WebSocket ทำให้เกิดต้นทุนการประมวลผลสูง โดยสาเหตุหลักคือ fragmentation และ masking
- fragmentation: implementation ของ WebSocket ใน Chromium จะแยกข้อความที่มีขนาดเกิน 131KB ออกเป็นหลายเฟรม ทำให้เฟรมวิดีโอดิบขนาดมากกว่า 3MB ถูกแบ่งส่งเป็นเฟรมแยกมากกว่า 24 เฟรม
- masking: ด้วยเหตุผลด้านความปลอดภัย WebSocket จะทำ masking กับทุกเฟรมที่ส่งจาก client ไป server ซึ่งกลายเป็น overhead ที่มีนัยสำคัญเมื่อข้อมูลมีขนาดเกิน 100MB ต่อวินาที
การมองหาทางเลือก
- เนื่องจากใช้เพียง browser API แล้วทำสิ่งที่มีประสิทธิภาพสูงกว่า WebSocket ได้ยากมาก จึงตัดสินใจ fork Chromium เพื่อใส่ฟังก์ชันแบบกำหนดเอง
- พิจารณาทางเลือก 3 แบบ: raw TCP/IP, Unix Domain Socket, Shared Memory
- TCP/IP: แม้จะหลีกเลี่ยงปัญหา fragmentation/masking ของ WebSocket ได้ แต่ขนาดแพ็กเก็ตสูงสุดยังเล็ก จึงยังมีปัญหา fragmentation อยู่ และยังมี overhead จากการคัดลอกข้อมูลเข้าสู่ kernel space
- Unix Domain Socket: ข้าม network stack ได้ทั้งหมด แต่ยังต้องคัดลอกข้อมูลระหว่าง user space กับ kernel space
- Shared Memory: หน่วยความจำที่หลายโปรเซสเข้าถึงพร้อมกันได้ โดย Chromium เขียนลง shared memory ได้โดยตรง และ encoder ก็อ่านได้ทันทีโดยไม่ต้องคัดลอกระหว่างทาง
การทำ transmission บน shared memory
- ใช้โครงสร้างแบบ ring buffer เพื่อให้อ่านและเขียนข้อมูลต่อเนื่องบน shared memory ได้
- ข้อกำหนด: lock-free, multi-producer/single-consumer, ขนาดเฟรมแปรผัน, zero-copy read, เป็นมิตรกับ sandbox, และ low-latency signaling
- มีการประเมิน implementation ของ ring buffer ที่มีอยู่แล้ว แต่ไม่พบตัวที่ตอบโจทย์ทั้งหมด จึงตัดสินใจทำเอง
- เพื่อรองรับ zero-copy read จึงแยก pointer ออกเป็น 3 ตัวคือ write, peek, read
- เพื่อความปลอดภัยด้านเธรด ใช้ atomic operation และใช้ named semaphore เพื่อแจ้งว่ามีข้อมูลใหม่หรือมีพื้นที่ว่างพร้อมใช้งาน
- ด้วย implementation ของ ring buffer บน shared memory และการปรับแต่งอื่น ๆ ทำให้ลดการใช้ CPU ของบอตได้สูงสุดถึง 50% และสุดท้ายก็ลดค่าใช้จ่าย AWS ได้มากกว่า 1 ล้านดอลลาร์ต่อปี
3 ความคิดเห็น
ความคิดเห็นบน Hacker News
เป็นเรื่องราวแบบฉบับของสตาร์ทอัพที่เลือกทางลัดแบบ "ดีพอใช้" แล้วค่อยไปปรับแต่งทีหลัง
มีความเห็นว่าปริมาณแบนด์วิดท์ของข้อมูลวิดีโอดิบนั้นสูงจนน่าตกใจ
มีความเห็นว่านี่ไม่ใช่ปัญหาของ AWS แต่เป็นปัญหาของการสิ้นเปลือง CPU cycle
ชี้ให้เห็นว่า MTU และ MSS ของเครือข่าย TCP/IP มีขนาดเล็กเมื่อเทียบกับขนาดเฟรมวิดีโอ
มีความเห็นว่าสามารถใช้ Mojo ของ Chromium ได้โดยไม่ต้องกังวลกับโค้ดเฉพาะแต่ละแพลตฟอร์ม
มีความเห็นว่าปัญหาไม่ใช่เรื่องเครือข่าย แต่เป็นการขาดความเข้าใจเกี่ยวกับวิดีโอ codec
ชื่นชมความโปร่งใส และอยากเห็นความโปร่งใสเรื่องราคาสินค้าด้วย
อธิบายว่าการ masking ของโปรโตคอล WebSocket เป็นความพยายามในการแก้ปัญหาคนกลาง
ชี้ว่าวิธีส่งข้อมูลวิดีโอโดยไม่บีบอัดนั้นดูแปลก
ระบุว่าแนวทางเริ่มต้นที่ส่งวิดีโอดิบผ่าน WebSocket นั้นน่าประหลาดใจ
สรุปคือตั้งแต่แรกก็พัฒนากันมาผิดแล้วนี่นา..
"แนวทางเริ่มต้นที่ส่งวิดีโอดิบผ่าน WebSocket นั้นน่าทึ่งจริง ๆ" เห็นด้วยกับคำพูดนี้