Inside HRT's Python Fork: Leveraging PEP 690 for Faster Imports

Bài tóm tắt Hudson River Trading 2025 4. Điều chỉnh Độ khó: Trung bình

Hudson River Trading · 2025 · 4. Điều chỉnh · Độ khó: Trung bình — bài gốc

Bối cảnh

  • Phần lớn code của HRT nằm trong một monorepo; module Python nội bộ import được ở mọi nơi. Tiện, nhưng import tăng không kiểm soát. Import Python chạy lúc runtime và chậm khi module lớn, nằm trên filesystem phân tán, có side effect chậm, kéo nhiều import bắc cầu, hay là extension C/C++ nhiều thư viện.
  • Hậu quả: script khởi động trễ hàng chục giây, notebook mất vài phút (có ngữ cảnh 10–15 phút) để nạp, job phân tán ngắn tốn phần lớn thời gian cho import. Trong khi đó đa số module chỉ dùng vài tên của module nó import.

Thay đổi chính

  • Lazy import lấy từ Cinder (fork CPython của Meta): lệnh import gán tên vào một đối tượng LazyImport; chỉ khi tên được tra trong dict (ví dụ globals()) mới thật sự import. Import trong try/except và with luôn chạy ngay vì thường dùng để bắt lỗi import. Không lazy được import trong hàm và from x import *.
  • Danh sách loại trừ: module có side effect lúc import (đặt sys.excepthook, cấu hình logging, đăng ký copyreg) phải import ngay. HRT đưa toàn bộ gói bên thứ ba vào danh sách này.
  • Lộ trình: Q1/2023 một nhóm tình nguyện làm prototype trong một tuần hackathon nội bộ (fork CPython 3.10, cherry-pick vài commit Cinder). Được duyệt cho đội Python@HRT làm toàn thời gian. Hai quý sau migrate dần, bắt đầu từ các team háo hức nhất. Q2/2024 khoảng 50% monorepo tương thích lazy. Họ gộp phần còn lại vào đợt nâng lên Python 3.12 (lazy là mặc định trong runtime mới) để không phải migrate cả công ty hai lần. Q3/2024 port sang 3.12; Q2/2025 lazy mặc định toàn công ty.
  • Ba loại lỗi khi migrate: side effect bị trễ hoặc không chạy (decorator đăng ký, __init_subclass__, override toàn cục; khó debug nhất vì thường là đổi hành vi âm thầm); phụ thuộc thứ tự import ngầm (excepthook, cấu hình logging); import bắc cầu ngầm (dùng bar.baz mà không import, dễ sửa vì lỗi ImportError rõ).

Vì sao

  • CLI có "thời gian tới lúc làm được việc" ngắn hẳn; notebook bớt ma sát khi khởi động lại; job phân tán ngắn không còn nạp dependency không dùng (có ca nạp lâu hơn cả job).
  • HRT chấp nhận chi phí bảo trì fork interpreter. Họ đồng ý với việc Steering Council từ chối PEP 690 (lazy ngầm không hợp upstream vì đúng những bug kể trên) và định đề xuất một PEP mới với từ khoá lazy tường minh.

Bài học cho SWE

  • Monorepo "import được mọi thứ" có chi phí ẩn ở thời gian khởi động; đo nó.
  • Thay đổi ngữ nghĩa toàn runtime: bắt đầu với người tự nguyện, có danh sách loại trừ, gộp phần còn lại vào một đợt nâng cấp vốn bắt buộc.
  • Side effect lúc import là phụ thuộc ngầm; lazy làm lộ ra chúng.

Liên quan