Cơ Chế Khóa Lạc Quan Và Khóa Bi Quan
Gần đây, Tiểu Tĩnh đã đề xuất một pull request (PR) cho hệ thống Skynet sau một thời gian bàn luận kỹ lưỡng. Ban đầu, nhóm phát triển phát hiện ra vấn đề này khi đang điều tra một lỗi khác, đồng thời nhận thấy rằng việc cải tiến cơ chế lập lịch tin nhắn của Skynet có thể nâng cao hiệu suất sử dụng CPU trong một số trường hợp nhất định.
Trước đây, Skynet sử dụng cấu trúc không khóa (lock-free) dựa trên cơ chế Compare-and-Swap (CAS) để quản lý hàng đợi tin nhắn. Tuy nhiên, về bản chất, dù áp dụng spin-lock hay cấu trúc lock-free, cấu trúc dữ liệu hàng đợi song song vẫn yêu cầu các thao tác vào/ra hàng đợi phải được thực hiện tuần tự để đảm bảo thứ tự. Điều này khiến hiệu suất hàng đợi không được cải thiện đáng kể khi có nhiều core xử lý.
Cấu trúc lock-free thể hiện quan điểm lạc quan về khả năng xung đột: phần mềm giả định rằng xung đột hiếm khi xảy ra, và khi xung đột thực sự xuất hiện, hệ thống sẽ lặp lại thao tác từ đầu. Ngược lại, spin-lock thể hiện quan điểm bi quan: nó coi xung đột là điều tất yếu, vì vậy sẽ khóa tài nguyên ngay khi thao tác trên dữ liệu chung, đảm bảo chỉ một thread được xử lý tại một thời điểm. Dù chọn phương pháp nào, các thao tác vẫn phải thực hiện lần lượt — không thể song song hóa hoàn toàn.
Vậy tại sao cấu trúc lock-free, dù logic phức tạp hơn, vẫn được ưa chuộng? Câu trả lời nằm ở hiệu suất trong tình huống lý tưởng. Khi không có xung đột, lock-free cho phép các thread xử lý gần như song song. Chỉ tại thời điểm có khả năng xung đột, hệ thống mới sử dụng lệnh nguyên tử (atomic instruction) để khóa một ô nhớ duy nhất, sau đó giải phóng ngay lập tức. Quá trình khóa/mở khóa này là nguyên tử, giúp tránh deadlock và giảm thiểu thời gian chờ do khóa không cần thiết. Ngược lại, spin-lock có thể gây lãng phí tài nguyên nếu số thread vượt quá số core, vì việc treo thread trong lúc chờ khóa sẽ làm các thread khác bị delay.
Trong trường hợp cụ thể của Skynet, lợi ích từ lock-free lại rất hạn chế. Lý do là bởi Skynet sử dụng hàng đợi hai cấp (two-stage queue): Khi một hàng đợi cấp thấp có tin nhắn, hệ thống sẽ xử lý trực tiếp mà không đưa vào hàng đợi chính (được bảo vệ bằng khóa). Điều này giúp giảm đáng kể tần suất cạnh tranh tài nguyên trên hàng đợi toàn cục.
Việc loại bỏ cấu trúc lock-free cũng cho phép đơn giản hóa thiết kế dữ liệu. Thay vì dùng danh sách lien kết xâm nhập (intrusive linked list) phức tạp để quản lý độ dài hàng đợi, nhóm phát triển chuyển sang dùng danh sách liên kết đơn giản, đồng thời loại bỏ phần mảng không cần thiết. Sửa đổi này mang lại hiệu ứng rõ rệt: thời gian khởi động hàng loạt dịch vụ (thường xảy ra khi có đợt kết nối ồ ạt tạo ra nhiều agent) đã nhanh hơn đáng kể.
Bổ sung ngày 12/12:
Bài viết này không nhằm so sánh hiệu năng của khóa lạc quan và khóa bi quan. Giải thích cuối cùng về vấn đề của Skynet không liên quan đến việc tăng hiệu suất khóa, mà xuất phát từ thiết kế gốc:
Khi các worker thread cạnh tranh truy cập hàng đợi toàn cục, nhóm phát triển giả định rằng việc giành quyền xử lý là “lạc quan” — vì thời gian xử lý tin nhắn thường dài hơn rất nhiều so với thời gian thao tác hàng đợi. Trong tình huống cạnh tranh, nếu một worker thất bại trong việc giành khóa, hệ thống sẽ giả định hàng đợi rỗng thay vì thử lại. Chiến lược này khả thi nhờ tính chất đồng nhất của các worker (tất cả đều có vai trò như nhau). Khi xảy ra xung đột, điều đó cho thấy các worker không có việc nặng để làm — do đó, việc để worker thất bại tạm thời từ bỏ xử lý sẽ tiết kiệm tài nguyên hệ thống. Nói cách khác, nếu tải việc thấp, việc xử lý theo mô hình đơn luồng hiệu quả hơn.
Việc thay đổi này không nhằm tăng tốc độ, mà để đơn giản hóa mã nguồn (tránh logic thử lại phức tạp). Tuy nhiên, nhóm phát hiện ra rằng trong một số trường hợp đặc biệt, giả định này là sai lầm. Cuối cùng, quyết định chuyển sang dùng spin-lock là do mã nguồn đơn giản hơn nhiều. Trong thực tế, dù chọn cơ chế nào, tác động đến hiệu suất hệ thống cũng vô cùng nhỏ. Vì vậy, lựa chọn một giải pháp dễ bảo trì hơn là hợp lý.
Kết luận:
Việc cân bằng giữa lock-free và khóa bi quan phụ thuộc vào đặc thù hệ thống. Trong Skynet, việc đơn giản hóa thiết kế đôi khi mang lại lợi ích lớn hơn là theo đuổi hiệu suất lý thuyết.