Cơ chế kiểm tra hàm không cấp phát của OxCaml mà nhiều ngôn ngữ nên học hỏi
(theconsensus.dev)- OxCaml, siêu tập hợp của OCaml do Jane Street phát triển, cho phép khai báo với trình biên dịch bằng
[@zero_alloc]rằng toàn bộ cây lời gọi của một hàm không được cấp phát trên heap - Nếu có cấp phát xảy ra trên đường gọi đã khai báo, lỗi sẽ lộ ra ngay bằng lỗi biên dịch, giúp ngăn hồi quy nhanh hơn so với cách tìm sau bằng profiler
- Trong C, C++, Java, Go, C#, Rust, Zig, OCaml, v.v., cách tiếp cận chủ yếu thường là dùng profiler để tìm và giảm cấp phát trên hot path
- Mã trên hot path chỉ cần một thay đổi nhỏ cũng có thể tạo ra cấp phát trở lại; nếu quên bối cảnh tối ưu hóa trước đó, nhóm sẽ phải lặp lại cùng một cuộc điều tra
- Quy ước truyền allocator trong Zig hoặc một số phần của Rust hiện đại cũng hữu ích, nhưng kiểm tra của trình biên dịch là cơ chế an toàn trực tiếp hơn so với quy ước
[@zero_alloc] thay đổi cách quản lý cấp phát như thế nào
- OxCaml là siêu tập hợp của OCaml do Jane Street phát triển, hỗ trợ khẳng định rằng một hàm không cấp phát trên heap
- Khi gắn
[@zero_alloc]vào một hàm, trình biên dịch sẽ kiểm tra việc cấp phát trên heap không chỉ trong hàm đó mà cả cây lời gọi bên dưới nó - Nếu có cấp phát xảy ra trong cây lời gọi, quá trình build sẽ thất bại và trình biên dịch sẽ báo nơi phát sinh cấp phát
- Có thể tạo kiểm tra tương tự bằng phân tích tĩnh, nhưng xét theo các tóm tắt được sinh ra, hiếm có ngôn ngữ chính thống nào đưa chức năng như vậy trực tiếp vào trình biên dịch
Khác biệt với cách tiếp cận dựa vào profiler
- Ở các ngôn ngữ khác, thông thường người ta dùng profiler để tìm cấp phát, rồi loại bỏ hoặc giảm cấp phát, đặc biệt là trong các vòng lặp chạy hàng triệu lần
- C, C++, Java, Go, C#, Rust, Zig, OCaml được liệt kê là các ví dụ của cách tiếp cận dựa vào profiler
- Chỉ cần đổi một dòng trên hot path cũng có thể đưa cấp phát trở lại, và để tìm nguyên nhân lại phải quay về dùng profiler
- Trong Zig hoặc một số phần của Rust hiện đại, việc không truyền allocator vào hàm có thể giảm hồi quy, nhưng điều này phụ thuộc vào quy ước
- Điểm khác biệt của
[@zero_alloc]là nó không dựa vào phân tích hậu kỳ hay quy tắc của đội ngũ, mà để trình biên dịch chặn hồi quy cấp phát ngay tại thời điểm build
1 bình luận
Các ý kiến trên Lobste.rs
Tôi hiểu rằng lý do Zig truyền allocator làm tham số hàm là để những hàm không nhận allocator làm tham số sẽ không thể cấp phát heap; không biết như vậy có đúng không
Nếu câu “quy ước có thể bị phớt lờ” thực sự đúng thì có vẻ khác với ý định ban đầu
Ngay trong hàm cũng có thể tạo một allocator mới như cách làm bên ngoài
gift link: https://theconsensus.dev/p/2026/…
nogctừ khá lâu trước đây, và tôi cho rằng nó có ngữ nghĩa tương tự ở chỗ GC đảm nhiệm việc cấp pháthttps://dlang.org/phobos/dmd_nogc.html
Có một thời từng bổ sung thử nghiệm cả ngoại lệ nogc, nhưng tôi chưa kiểm tra trạng thái hay phần triển khai hiện tại
https://dlang.org/changelog/2.079.0.html#dip1008
Trong Rust, chỉ dùng core cũng là một cách để tránh cấp phát
Cách tiếp cận đó rốt cuộc gần với “hãy kiểm soát dependency” hoặc “hãy kiểm tra thủ công toàn bộ call graph” hơn
Trọng tâm của bài viết là cách công cụ như compiler cưỡng chế tĩnh một thuộc tính nào đó, và cung cấp quy trình làm việc để tìm ra một cách hiệu quả những điểm mà thuộc tính đó bị phá vỡ
D có
@nogc, và phần tiếp theo là vấn đề chỉ dùng các abstraction có thể kiểm soát trực tiếp với mẫu cấp phát rõ ràngNói thêm vì người ta đã mất khả năng đặt tiêu đề bài viết một cách mô tả: điểm cốt lõi là tính năng gắn
[@zero_alloc]vào hàm và khiến compiler từ chối chương trình nếu cây lời gọi của hàm đó đụng tới heapTôi tò mò liệu cách này có thể áp dụng cho nhiều điều kiện khác như “không ném exception hay panic”, “không có khóa”, “luôn kết thúc” hay không