- Khám phá cách Unix pipe được hiện thực trên Linux và các phương pháp tối ưu hóa chương trình kiểm thử ghi và đọc dữ liệu qua pipe
- Chương trình ban đầu có thông lượng khoảng 3,5GiB/s, và qua nhiều tối ưu hóa khác nhau đã được cải thiện lên gấp 20 lần
- Các tối ưu hóa này được thực hiện bằng cách profiling chương trình bằng công cụ
perf của Linux
- Bài viết được truyền cảm hứng từ một chương trình FizzBuzz đã được tối ưu hóa có thể đẩy đầu ra vào pipe với tốc độ khoảng ~35GiB/s
- Đào sâu vào cách pipe hoạt động bên trong, lý do việc ghi và đọc từ chúng lại chậm, và cách các system call
vmsplice và splice có thể cải thiện hiệu năng
- Thảo luận về cách phân trang của Linux và việc sử dụng huge pages có thể tạo ra các phiên bản chương trình nhanh hơn
- Tối ưu hóa cuối cùng bao gồm việc thay polling bằng một busy loop
- Các bài kiểm thử được thực hiện trên CPU Intel Skylake i7-8550U và Linux 5.17
- Bài viết giải thích chi tiết cách bộ nhớ được cấu thành từ các trang có kích thước cố định và cách CPU dùng page table để dịch địa chỉ ảo sang địa chỉ vật lý
- Bài viết kết luận với quan sát rằng chuyển sang huge pages trong chương trình có thể cải thiện hiệu năng khoảng 50%
- Bài viết cũng bàn về việc sử dụng huge pages trên CPU và cách giảm lỗi Translation Lookaside Buffer (TLB), điều này có thể cải thiện hiệu năng
- Mã kernel giả định rằng
struct page trỏ tới một trang có kích thước chuẩn của kiến trúc hiện tại. Với huge pages, struct page ở vị trí "head" chứa thông tin về trang vật lý thực tế, còn các trang "tail" liên tiếp chỉ chứa con trỏ trỏ về trang head
- Các kernel gần đây (sau 5.17) bao gồm một kiểu mới là
struct folio để nhận diện rõ ràng trang head. Điều này giúp giảm nhu cầu phải kiểm tra lúc chạy xem struct page là trang head hay tail, từ đó cải thiện hiệu năng
- Bài viết thảo luận về khái niệm busy loop để tránh chi phí đồng bộ hóa. Cách này yêu cầu
vmsplice trả về khi không thể ghi vào pipe, rồi quay busy loop cho tới khi sẵn sàng. Nó có thể mang lại mức tăng hiệu năng 25%, nhưng phải đánh đổi bằng việc chiếm trọn một lõi CPU cho tới khi vmsplice sẵn sàng
- Tác giả tóm tắt các chủ đề chính được đề cập trong bài: thao tác zero-copy, ring buffer, phân trang & bộ nhớ ảo, overhead đồng bộ hóa
- Tác giả cũng thừa nhận còn nhiều tùy chọn và chi tiết khác không được bàn tới trong bài, vì chúng không liên quan hoặc không đủ thú vị với tác giả
- Bài viết được độc giả đón nhận tích cực vì họ thấy nó hữu ích và thú vị
1 bình luận
Ý kiến trên Hacker News
vmsplice, hoạt động như một cơ chế bộ nhớ chia sẻ thu nhỏ giữa hai tiến trìnhvmspliceđòi hỏi phải xử lý bộ đệm cẩn thận khi đọc và ghi; khá phức tạp nhưng có thể hiệu quảsplice()vàvmsplice(), được cho là khó dùng và ít được tận dụng trong phần lớn chương trìnhstdoutcủa chương trình này sangstdincủa chương trình khác, biến thao tác này thànhzerocopyhoặc, trong tình huống kém tối ưu hơn, làonecopynhanhperf, nhấn mạnh tầm quan trọng của nó đối với thông lượngcat,sed,awk,cut,grep,uniq,jqv.v.