无标题
Vấn đề về ngoại lệ trong C++
Vấn đề về ngoại lệ trong C++
Vấn đề về ngoại lệ trong C++
Vấn đề về cơ chế xử lý ngoại lệ trong C++
Trong buổi hội thảo C++ hôm nay, tôi có dịp gặp gỡ một người bạn trẻ tên Bảo (Bao) rất đam mê lập trình. Mới sáng nay, bạn ấy đã gửi cho tôi một email chia sẻ về blog cá nhân của mình. Qua đường dẫn trong email, tôi đã tìm thấy một bài viết rất thú vị của Bảo về chủ đề xử lý ngoại lệ (exception handling) trong C++ với tiêu đề “Hiểu sâu về exception”.
Thực ra, vào ngày thứ hai của hội nghị, tôi đã tham dự buổi thuyết trình về chủ đề này. Tuy nhiên, nội dung trình bày không đáp ứng được kỳ vọng của tôi. Phần lớn diễn giả tập trung trình bày về Structured Exception Handling (SEH) của Windows - vốn không liên quan mật thiết đến chuẩn C++. Tôi vốn mong chờ được nghe phân tích về cơ chế triển khai exception trong C++, đặc biệt là từ góc độ nghiên cứu mà tôi đã từng tìm hiểu trước đây. Vì vậy, tôi đã chuyển sang phòng bên cạnh để lắng nghe bài giảng xuất sắc về lập trình meta từ chuyên gia của Honor. Nghe nói sau đó buổi thảo luận ở phòng cũ đã trở nên khá sôi nổi, tiếc là tôi không được chứng kiến trực tiếp.
Về vấn đề Bảo nêu ra liên quan đến trình biên dịch VC6, tôi có thể xác nhận rằng: Trong trường hợp “happy path” (luồng xử lý bình thường), cơ chế exception handling của VC6 gần như không gọi đến các API nhân hệ điều hành. Hiệu năng xử lý exception của VC6 cũng không hề thua kém các compiler như Intel C++ Compiler (icc) hay GCC. Tuy nhiên, việc so sánh cụ thể chất lượng giữa các nền tảng vẫn cần thêm phân tích chi tiết.
Cá nhân tôi không quá quan tâm đến hiệu năng xử lý trong trường hợp “unhappy path” (xảy ra ngoại lệ). Điều tôi chú ý hơn là khi tắt cơ chế exception trong VC6, hiệu năng chương trình có thể được cải thiện nhẹ. Lý do là vì VC6 sử dụng thanh ghi FS để lưu trữ thông tin môi trường, đồng thời các compiler khác thường sinh ra lượng lớn mã lệnh để xử lý luồng ngoại lệ. Dù việc dùng mã trả về (return code) sẽ tạo ra binary nhỏ gọn hơn, nhưng trong thực tế ít ai còn quan tâm đến kích thước mã nguồn nữa. Cá nhân tôi từng gặp phải tình huống mà sự phình to của binary ảnh hưởng đến hiệu năng thực thi - dù không đáng kể. Một số compiler như ICC có thể tối ưu tốt hơn bằng cách sắp xếp vật lý mã lệnh để giảm thiểu ảnh hưởng đến luồng xử lý chính.
Về vấn đề chia cho không (division by zero), trong thực tế: Các chương trình không chuyên về toán học hiếm khi dùng phép chia, và khi dùng thì logic chương trình thường đã đảm bảo mẫu số khác không. Còn các ứng dụng toán học thường dùng số thực dấu phẩy động (floating-point), nơi phép chia cho không sẽ trả về giá trị vô cực (infinity) chứ không ném ngoại lệ. Do đó, tôi cho rằng việc lấy ví dụ exception chia cho không để minh họa ưu điểm của exception handling là chưa thuyết phục.
Trong dự án hiện tại, tôi không sử dụng cơ chế exception vì yêu cầu đặc thù. Thay vào đó, tôi dùng lệnh goto để xử lý các trường hợp lỗi - phương pháp bị nhiều lập trình viên coi là “tệ hại”, nhưng theo tôi, nếu dùng goto để định nghĩa các điểm thoát rõ ràng trong hàm thì đó lại là giải pháp rất hiệu quả và trực quan.
Việc không dùng exception mang lại nhiều lợi ích: (1) Giảm độ phức tạp luồng điều khiển, (2) Đơn giản hóa thiết kế giao diện API, (3) Cải thiện nhẹ hiệu năng, (4) Đặc biệt quan trọng với các hệ thống dùng coroutine - vốn rất khó tương thích với exception handling trong C++. Phải thừa nhận rằng kinh nghiệm thực tế của tôi với exception khá hạn chế, chủ yếu dựa trên nghiên cứu lý thuyết, nên không dám bình luận sâu.
Tuy vậy, tôi hoàn toàn đồng tình với phần lớn quan điểm mà Bảo đã trình bày trong blog!