Cộng đồng lập trình C++ đang chứng kiến một cuộc tranh luận chưa từng có về một trong những tính năng đầy tham vọng nhất của ngôn ngữ này. Sau hơn năm năm kể từ khi C++20 giới thiệu modules, ngày càng có nhiều tiếng nói từ các nhà phát triển và chuyên gia đặt câu hỏi liệu tính năng này có nên tiếp tục tồn tại trong tiêu chuẩn hay không.
Tranh cãi xuất phát từ một thực tế khắc nghiệt: bất chấp nhiều năm nỗ lực phát triển, C++ modules đã thất bại trong việc thực hiện lời hứa chính của mình về việc cải thiện đáng kể thời gian biên dịch. Điều từng được ca ngợi là giải pháp cho các vấn đề tốc độ build khét tiếng của C++ thay vào đó đã trở thành nguồn gốc của sự thất vọng cho các nhà phát triển đang cố gắng áp dụng các thực hành C++ hiện đại.
Lời hứa về hiệu suất không bao giờ thành hiện thực
Khi C++ modules lần đầu được đề xuất, điểm bán hàng chính rất rõ ràng - cải thiện tốc độ biên dịch một cách đáng kể. Hệ thống include header truyền thống tạo ra một thuật toán O(N²) trong đó cùng một đoạn code được phân tích cú pháp lặp đi lặp lại trên nhiều file nguồn. Modules được cho là sẽ khắc phục điều này bằng cách lưu trữ code đã được tiền xử lý ở định dạng nhị phân có thể được tải nhanh chóng từ ổ đĩa.
Tuy nhiên, việc kiểm tra trong thực tế cho thấy một câu chuyện khác. Các triển khai hiện tại chỉ cho thấy những cải thiện khiêm tốn từ 10-20% trong những trường hợp tốt nhất, còn rất xa so với những lợi ích mang tính chuyển đổi ban đầu được hứa hẹn. Một số thành viên cộng đồng đã phát hiện ra rằng các kỹ thuật hiện có, như các thư viện tiêu chuẩn được thiết kế cẩn thận, đã có thể đạt được tốc độ biên dịch nhanh hơn 4 lần mà không cần đến sự phức tạp mà modules mang lại.
Tình hình trở nên đáng lo ngại hơn khi xem xét rằng precompiled headers, một công nghệ cũ hơn nhiều, thường cung cấp những cải thiện hiệu suất tương tự hoặc tốt hơn với độ phức tạp triển khai thấp hơn đáng kể. Điều này đặt ra những câu hỏi cơ bản về việc liệu nỗ lực kỹ thuật khổng lồ đầu tư vào modules có đáng giá hay không.
Hiệu suất Module hiện tại so với kỳ vọng
- Lời hứa ban đầu: Tăng tốc biên dịch 5x-10x
- Thực tế hiện tại: Cải thiện 10-20% trong các trường hợp tốt nhất
- Precompiled Headers: Thường có hiệu suất tương đương hoặc tốt hơn
- Giải pháp thay thế: ZapCC đạt được tốc độ tăng tốc 5x+ bằng cách sử dụng công nghệ hiện có
Sự hỗn loạn triển khai trên các compiler
Một trong những khía cạnh gây tổn hại nhất của việc triển khai modules là sự thiếu phối hợp giữa các nhà cung cấp compiler và các nhà phát triển build system. Mỗi compiler lớn đã triển khai modules theo cách khác nhau, tạo ra một hệ sinh thái phân mảnh trong đó code hoạt động với một toolchain có thể thất bại với toolchain khác.
Các thách thức tích hợp nghiêm trọng đến mức các build system giờ đây phải tạo ra các compiler flag bổ sung trong quá trình biên dịch, lưu trữ chúng trong các file tạm thời, và chuyển chúng đến các lệnh biên dịch tiếp theo. Mức độ phức tạp này trái ngược hoàn toàn với sự đơn giản thanh lịch mà modules được cho là sẽ mang lại cho việc phát triển C++.
Chúng tôi không muốn biến compiler thành một build system đã trở thành phản hồi phổ biến từ các nhà phát triển compiler khi đối mặt với các đề xuất cải thiện tích hợp module, tạo ra một bế tắc hiệu quả trong đó không bên nào muốn chịu trách nhiệm làm cho hệ thống hoạt động một cách liền mạch.
Việc thiếu một product owner thống nhất có thẩm quyền đối với tất cả các bộ phận chuyển động đã tạo ra thứ mà nhiều người mô tả là một cơn ác mông phức tạp kiểu kafkaesque. Không có ai được trao quyền phối hợp giữa các nhóm compiler, những người duy trì build system, và các nhà triển khai thư viện tiêu chuẩn, modules vẫn mắc kẹt trong trạng thái chức năng một phần.
Các Thách Thức Triển Khai Chính
- Phân Mảnh Trình Biên Dịch: Mỗi nhà cung cấp triển khai module theo cách khác nhau
- Tích Hợp Hệ Thống Build: Yêu cầu quản lý file tạm thời phức tạp
- Vấn Đề Tính Di Động: Các file nhị phân module không thể di chuyển giữa các trình biên dịch (trừ MSVC )
- Hỗ Trợ Toolchain: Hỗ trợ module của Apple vẫn được liệt kê là "một phần"
- Code Cũ: Không thể kết hợp
include <vector>
vàimport <vector>
trong cùng một dự án
Học hỏi từ các cách tiếp cận thay thế
Cuộc thảo luận của cộng đồng đã làm nổi bật một số cách tiếp cận thay thế có thể thành công hơn. Một số nhà phát triển chỉ ra ngôn ngữ lập trình D, đã triển khai một hệ thống module sạch sẽ hơn hai thập kỷ trước và tiếp tục hoạt động đáng tin cậy. Cách tiếp cận của D bao gồm các tính năng như closed namespaces và semantic independence giúp các modules thực sự được cô lập và có thể dự đoán được.
Các đề xuất khác tập trung vào những giải pháp đơn giản hơn có thể cung cấp hầu hết các lợi ích với độ phức tạp ít hơn nhiều. Một câu lệnh import đơn giản hoạt động như include nhưng không có context leakage có thể được triển khai dễ dàng hơn nhiều trong khi vẫn cho phép các tối ưu hóa compiler đáng kể.
Các công cụ như ZapCC, đã đạt được tốc độ biên dịch nhanh hơn 5 lần thông qua các quy trình compiler liên tục, chứng minh rằng những cải thiện thời gian build đáng kể đã có thể thực hiện được bằng công nghệ hiện có. Những giải pháp này phần lớn bị bỏ qua để ủng hộ cách tiếp cận modules phức tạp hơn.
Con đường phía trước
Khi cuộc tranh luận ngày càng gay gắt, một số kết quả tiềm năng đang được thảo luận. Một số người lập luận để loại bỏ hoàn toàn modules khỏi tiêu chuẩn và bắt đầu lại với một cách tiếp cận đơn giản hơn. Những người khác đề xuất tập trung vào tập con import std, có thể cung cấp những lợi ích có ý nghĩa cho việc sử dụng thư viện tiêu chuẩn mà không cần đến toàn bộ sự phức tạp của các modules tổng quát.
Thách thức cơ bản vẫn là modules đòi hỏi sự phối hợp rộng rãi giữa nhiều tổ chức độc lập, mỗi tổ chức có ưu tiên và ràng buộc riêng. Không có cấu trúc quản trị rõ ràng và cam kết chung để làm cho modules hoạt động đúng cách, tính năng này có thể vẫn mãi mãi được triển khai một nửa.
Cộng đồng C++ giờ đây đối mặt với một lựa chọn khó khăn: tiếp tục đầu tư tài nguyên vào một tính năng liên tục thất bại trong việc đáp ứng kỳ vọng, hoặc thừa nhận những thách thức triển khai và theo đuổi các cách tiếp cận thay thế có thể mang lại những cải thiện tốc độ biên dịch mà các nhà phát triển cần một cách tuyệt vọng.
Tham khảo: Nibble Stew