Building a lower-latency GC

Bài tóm tắt Jane Street 2015 3. Quy mô lớn Độ khó: Khó

Jane Street · 2015 · 3. Quy mô lớn · Độ khó: Khó — bài gốc

Bối cảnh

  • GC gây pause không đoán trước, xấu cho hệ giao dịch. Cách thường làm là pool hết object, không cấp phát gì trên đường nóng; cách này chạy được nhưng code khó viết và khó suy luận. Jane Street muốn một GC cho phép vẫn cấp phát mà latency vẫn thấp.
  • GC của OCaml vốn khá tốt: major heap thu incremental theo lát, minor heap nhỏ (đo bằng MB, so với 5–10 GiB young generation hay khuyên cho Java). Vấn đề còn lại (theo bài): không có profiling từng pha; false promotion (object sắp chết bị đẩy lên major heap vì minor GC đến sai lúc); GC được "lên dây" theo lượng cấp phát chứ không theo thời gian, nên làm việc đúng lúc hệ bận; quét mảng lớn và GC root không incremental; khối lượng major slice tính ngay theo lượng promote của lần trước.

Thay đổi chính

Damien Doligez (tác giả GC của OCaml) làm việc tại Jane Street vài tháng, làm các nhánh thử nghiệm:

  • Probe ghi chi tiết từng pha và pha con của GC.
  • Aging ở minor heap: object sống qua vài lần minor GC rồi mới được promote.
  • Incremental hơn: quét mảng và root có thể ngắt giữa chừng.
  • Tách major slice khỏi minor GC: chạy riêng từng loại, vào lúc nào cũng được. Một job ở tầng ứng dụng định kỳ đoán xem hệ có rảnh không rồi tự lên lịch GC (minor chỉ khi minor heap đã lớn hơn ngưỡng để tránh false promotion).
  • Kế toán công việc làm mượt: việc cần làm trải ra n slice tới trong một circular buffer; slice do ứng dụng ép chạy được trừ vào bucket đầu.
  • Free list chia theo kích thước (đang làm).

Vì sao

  • Kết quả 2015: dùng các thay đổi này cùng job ép major slice vào lúc rảnh, tail latency giảm 3 lần trên một ứng dụng production thật, gần như chưa tune tham số.
  • Điểm đáng thất vọng: với batch job, aging gần như không cải thiện tổng thời gian tính (tiết kiệm promote nhưng minor GC đắt hơn), dù HotSpot, CLR, GHC đều có aging.

Bài học cho SWE

  • Với hệ cần latency ổn định, câu hỏi là khi nào GC chạy chứ không chỉ chạy bao lâu: dời việc khỏi lúc bận sang lúc rảnh.
  • Pool object là giải pháp được nhưng tốn công bảo trì; sửa runtime có thể trả lại sự dễ viết code.
  • Đo từng pha trước khi tối ưu.

Liên quan