Magic-trace: Diagnosing tricky performance issues easily with Intel Processor Trace

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

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

Bối cảnh

  • Hệ giao dịch của Jane Street gần như toàn thời gian ngồi chờ gói tin mạng và phải phản hồi nhanh hơn nhiều so với chu kỳ lấy mẫu khoảng 250 µs của sampling profiler, nên profiler lấy mẫu gần như vô dụng cho latency ở thang đó.
  • Tracing bằng instrumentation (kiểu tracy, khoảng 2 ns/lần gọi) phải gắn tay, gắn khắp nơi thì làm chậm hàm 1 ns lên gấp ba, và vấn đề thường nằm ở chỗ chưa gắn.
  • Intel Processor Trace (IPT) ghi toàn bộ luồng điều khiển với timing khoảng 30 ns, overhead 2–20% (các chương trình của họ dưới 10%, thường dưới 5%), nhưng hầu như không ai dùng vì khó bắt dữ liệu và chỉ có bản dump text khổng lồ; dữ liệu khoảng 1 GB/s, decode chậm hơn thời gian thực khoảng 60 lần.

Thay đổi chính

  • Thu hẹp phạm vi để có MVP nhanh (dự án thực tập hè 2021): chỉ dùng chế độ circular buffer và chụp snapshot khoảng 10 ms trước một lần gọi hàm được chọn; chỉ hiện gọi/trả hàm trên timeline.
  • Bản đầu bọc perf: hàm Magic_trace.take_snapshot gửi SIGUSR2 cho perf, rồi parse perf script và dựng lại call stack (xử lý tail call, exception của OCaml). Xong trong chưa tới một tuần.
  • Bản hai dùng thẳng perf_event_open + libipt để decode tới mức lệnh (theo bài, không thấy ai từng ghép hai thứ này).
  • Attach vào process đang chạy bằng hardware breakpoint qua perf_event_open: không cần process hợp tác, không tốn gì khi không attach, dùng được với bất kỳ hàm nào, đọc được thanh ghi để lấy tham số.
  • Gặp một bug kernel (null pointer với một tổ hợp tính năng IPT hiếm) và nghi một erratum phần cứng (SKL171) làm mất packet trên một thế hệ CPU.
  • Fork Perfetto UI để zoom tới mức nanosecond; mở mã nguồn.

Vì sao

  • Sau khoảng một tháng dùng nội bộ: tìm ra một regression 100 ns do một hàm thôi được inline, và lý do một phiên bản compiler mới làm hệ giao dịch chậm đi (cũng do quyết định inline).
  • Dùng được cho cả "long async cycle" của Async: chụp snapshot khi một cycle quá dài, nhìn ngược 10 ms cuối thấy code nào làm quá nhiều việc.

Bài học cho SWE

  • Với latency dưới micro giây, cần trace đầy đủ một cửa sổ ngắn quanh sự kiện hiếm chứ không cần thống kê lấy mẫu.
  • Hẹp phạm vi để có công cụ dùng được sớm, rồi mới mở rộng.
  • Dùng phần cứng/kernel ít người dùng thì đừng mặc định bug là của mình. Theo tác giả, giờ nên bắt đầu bằng perf + perf-dlfilter thay vì tự làm lại với libipt.

Liên quan