Vì sao SQLite được viết bằng C
(sqlite.org)- SQLite được phát triển bằng ngôn ngữ C ngay từ những ngày đầu (năm 2000) vì hiệu năng, tính tương thích, ít phụ thuộc và độ ổn định
- C có thể dùng trên gần như mọi hệ điều hành và ngôn ngữ, đặc biệt hỗ trợ chạy nhanh với vai trò thư viện cấp thấp
- Lý do chọn C thay vì ngôn ngữ hướng đối tượng là khả năng mở rộng, khả năng được gọi từ nhiều ngôn ngữ khác nhau, cùng với việc C++ và Java còn non nớt vào thời điểm phát triển
- SQLite có cấu trúc một tệp duy nhất với hầu như không có phụ thuộc, chỉ dùng số ít hàm tối thiểu của thư viện chuẩn C
- Dù có tranh luận về việc viết lại bằng "ngôn ngữ an toàn" như Rust hay Go, C vẫn chiếm ưu thế về kiểm soát chất lượng, hiệu năng và khả năng gọi như thư viện
1. Vì sao C là lựa chọn tối ưu
- SQLite đã được duy trì bằng ngôn ngữ C từ lần phát triển đầu tiên vào ngày 29 tháng 5 năm 2000 cho đến nay
- Hiện tại chưa có kế hoạch viết lại bằng ngôn ngữ khác
- C có khả năng kiểm soát gần với phần cứng nhưng vẫn có tính di động rất cao, nên thường được gọi là “ngôn ngữ assembly có tính di động”
- Các ngôn ngữ khác có thể tuyên bố là “nhanh ngang C”, nhưng không có ngôn ngữ nào tuyên bố nhanh hơn C
1.1. Hiệu năng
- Thư viện cấp thấp như SQLite bị gọi rất thường xuyên nên cần phải hoạt động cực nhanh
- Ngôn ngữ C phù hợp để viết mã nhanh, có tính di động cao nhưng vẫn cho phép tiếp cận sát phần cứng
- Các ngôn ngữ hiện đại khác cũng tuyên bố là ‘nhanh như C’, nhưng trong lập trình mục đích chung thì không có ngôn ngữ nào có thể tự tin nói là nhanh hơn C
- C cho phép kiểm soát chi tiết bộ nhớ và tài nguyên CPU, nên đôi khi đạt hiệu năng nhanh hơn hệ thống tệp 35%
- Ví dụ: Internal vs External BLOBs
1.2. Tính tương thích
- Gần như mọi hệ thống đều có thể gọi thư viện được viết bằng C
- Ví dụ, ngay cả trên Android (dựa trên Java), SQLite vẫn có thể được dùng thông qua adaptor
- Nếu SQLite được viết bằng Java thì sẽ không thể dùng trên iPhone (Objective-C, Swift), khiến tính phổ dụng giảm mạnh
1.3. Mức độ phụ thuộc thấp
- Vì được phát triển dưới dạng thư viện C, nó có rất ít phụ thuộc thời gian chạy
- Ở cấu hình tối thiểu, chỉ dùng những hàm cơ bản nhất của thư viện chuẩn C như memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp()
- Ngay cả ở bản dựng đầy đủ hơn, nó cũng chỉ có một vài phụ thuộc như malloc(), free(), nhập xuất tệp
- Các ngôn ngữ hiện đại thường đòi hỏi nhiều runtime cỡ lớn và hàng nghìn giao diện
1.4. Độ ổn định
- C là một ngôn ngữ cũ, ít thay đổi và khá nhàm chán, nhưng điều đó cũng đồng nghĩa với tính dự đoán được và độ ổn định
- Khi xây dựng một bộ máy cơ sở dữ liệu nhỏ, nhanh và đáng tin cậy như SQLite, một ngôn ngữ có đặc tả không thay đổi thường xuyên là rất phù hợp
- Nếu đặc tả hoặc cách triển khai ngôn ngữ thay đổi liên tục, điều đó sẽ bất lợi cho tính ổn định của SQLite
2. Vì sao không được viết bằng ngôn ngữ hướng đối tượng
- Một số lập trình viên cho rằng nếu không hướng đối tượng thì khó triển khai một hệ thống phức tạp như SQLite, nhưng so với C, thư viện viết bằng C++ hay Java sẽ khó được gọi từ các ngôn ngữ khác hơn
- Để hỗ trợ nhiều ngôn ngữ như Haskell, Java và các ngôn ngữ khác, việc chọn thư viện C là hợp lý
- Hướng đối tượng không phải là ngôn ngữ mà là một mẫu thiết kế, nên không bị giới hạn vào một ngôn ngữ cụ thể
- Ngay cả trong C cũng có thể triển khai các mẫu hướng đối tượng bằng struct và con trỏ hàm
- Hướng đối tượng không phải lúc nào cũng là cấu trúc tối ưu; đôi khi mã thủ tục rõ ràng hơn, dễ quản lý hơn và cũng cho kết quả nhanh hơn
- Vào giai đoạn đầu phát triển SQLite (khoảng năm 2000)
- Java còn non nớt
- C++ thì gặp vấn đề nghiêm trọng về tương thích giữa các trình biên dịch
→ Khi đó, C là lựa chọn thực tế và an toàn nhất
- Ngay cả hiện nay, lợi ích của việc viết lại SQLite vẫn không nhiều
3. Vì sao không được viết bằng “ngôn ngữ an toàn”
- Gần đây, sự quan tâm tới các ngôn ngữ lập trình an toàn như Rust và Go tăng cao, nhưng vào thời điểm SQLite mới được phát triển (trong 10 năm đầu tiên), các ngôn ngữ này còn chưa tồn tại
- Nếu viết lại bằng Go hoặc Rust, có thể sẽ phát sinh nhiều lỗi hơn hoặc hiệu năng bị giảm
- Các ngôn ngữ này chèn thêm mã nhánh (branch) cho việc kiểm tra bộ nhớ và các cơ chế khác; trong khi với chiến lược chất lượng của SQLite, độ phủ nhánh 100% là rất quan trọng, nhưng phần này chưa được đáp ứng
- Các ngôn ngữ an toàn thường dừng chương trình khi xảy ra tình trạng out-of-memory, nhưng SQLite được thiết kế để có thể phục hồi ngay cả trong tình trạng thiếu bộ nhớ
- Rust, Go và các ngôn ngữ tương tự vẫn còn là ngôn ngữ mới, cần thêm thời gian phát triển liên tục
- Vì vậy, đội ngũ phát triển SQLite vẫn ủng hộ sự phát triển của các ngôn ngữ an toàn, nhưng trong việc triển khai SQLite, họ vẫn coi trọng độ ổn định đã được kiểm chứng của C
Dù vậy, trong tương lai vẫn có khả năng được viết lại bằng Rust. Còn khả năng viết bằng Go thì thấp vì Go không ưa assert()
- Tuy nhiên, để có thể được viết bằng Rust thì cần có các điều kiện tiên quyết sau:
- Rust phải trưởng thành hơn và chu kỳ thay đổi phải chậm lại, để trở thành một “ngôn ngữ cũ và nhàm chán”
- Phải chứng minh được rằng có thể tạo ra thư viện phổ dụng có thể được gọi từ nhiều ngôn ngữ
- Phải tạo được mã đối tượng có thể chạy cả trên thiết bị không có hệ điều hành như hệ nhúng
- Phải có công cụ kiểm thử độ phủ nhánh 100% cho binary đã biên dịch
- Phải phục hồi được từ lỗi OOM (thiếu bộ nhớ)
- Rust phải thực hiện được mọi công việc mà C đang xử lý trong SQLite mà không bị suy giảm hiệu năng
- Nếu một người yêu Rust (rustacean) cho rằng các điều kiện trên đã được đáp ứng và SQLite nên được viết lại bằng Rust, thì họ được khuyến khích liên hệ trực tiếp với các nhà phát triển SQLite để trình bày ý kiến
2 bình luận
Ý kiến trên Hacker News
if (i >= array_length) panic("index out of bounds"), và bản thân đoạn mã đó đã được compiler Rust kiểm thử tốt nên có lẽ không cần lo. Tôi muốn biết liệu mình có đang hiểu đúng lập luận này khôngget_unchecked()của Rust thì cũng có thể truy cập không cần bounds check, nhờ đó vẫn an toàn mà lại tăng hiệu năng tài liệu get_uncheckedif condition { panic(err) }như một dạng hàm assert hay khôngC cũng bị xem là rủi ro bảo mật với SQLite; vậy ngay cả khi đã viết test thật kỹ và là một lập trình viên đủ dày dạn kinh nghiệm thì vẫn vậy sao? Vấn đề có thể nằm ở logic và quy trình phát triển, nhưng việc bản thân ngôn ngữ lại là lỗ hổng bảo mật thì tôi thấy khá khó hiểu. Thực ra hầu như không có chương trình nào không phụ thuộc vào hạ tầng được viết bằng C cả.