SQLite はなぜ C で書かれたのか
(sqlite.org)- SQLite は、性能、互換性、少ない依存関係、安定性を理由に、初期(2000年)から C 言語で開発されている
- C はほぼすべての OS と言語から利用でき、特に 低レベルライブラリ として高速な動作を支える
- オブジェクト指向言語ではなく C を選んだ理由は、拡張性、さまざまな言語からの呼び出しやすさ、そして開発当時の C++ と Java の未成熟さ にある
- SQLite は 依存関係がほとんどない単一ファイル構造 を持ち、C 標準ライブラリの最小限の関数だけを使用する
- Rust や Go のような "安全な言語" での再実装も議論されているが、品質管理、性能、ライブラリとしての呼び出しやすさなどの面で 依然として C が優位 である
1. C が最適な選択である理由
- SQLite は 2000年5月29日の初回開発以来、現在まで C 言語で維持 されている
- 現時点では他の言語に書き換える計画はない
- C は ハードウェアに近い制御力 を持ちながら移植性にも優れ、「ポータブルなアセンブリ言語」と呼ばれる
- 他の言語が「C と同じくらい速い」と主張することはあっても、C より速いと主張する言語はない
1.1. 性能
- SQLite のような 低レベルライブラリ は頻繁に呼び出されるため、非常に高速に動作することが求められる
- C 言語は高速なコードを書くのに適しており、移植性が高いうえにハードウェアへ密接にアクセスできる
- 他の現代的な言語も「C と同じくらい速い」と主張するが、汎用プログラミングにおいて C より速いと断言できる言語はない
- C はメモリと CPU 資源を細かく制御できるため、ファイルシステムより 35% 高速な性能 を示すこともある
1.2. 互換性
- ほぼすべてのシステムが C で書かれたライブラリ を呼び出せる
- たとえば Android(Java ベース)でもアダプタを通じて SQLite を利用できる
- もし SQLite が Java で書かれていたなら、iPhone(Objective-C, Swift)では使えず、汎用性が大きく低下していただろう
1.3. 低い依存関係
- C ライブラリ として開発されているため、ランタイム依存関係が非常に少ない
- 最小構成では標準 C ライブラリのごく基本的な関数(memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp())だけを使う
- より完全なビルドでも malloc(), free(), ファイル入出力など、少数の依存関係しか持たない
- 現代的な言語では、多数の巨大なランタイム と 何千ものインターフェース が必要になることが多い
1.4. 安定性
- C は 古く、変化の少ない退屈な言語 だが、それはそのまま 予測可能性と安定性 を意味する
- SQLite のような 小さく、高速で、信頼できるデータベースエンジン を作るには、仕様が頻繁に変わらない言語が適している
- 言語仕様や実装がたびたび変わると、SQLite の安定性には不利になる
2. なぜオブジェクト指向言語で書かれなかったのか
- 一部の開発者は、オブジェクト指向でなければ SQLite のような複雑なシステムを実装しにくいと考えるが、C と比べて C++ や Java でライブラリを作ると、他の言語から呼び出しにくくなる
- Haskell、Java など多様な言語を支援するために、C ライブラリを選ぶ ことは妥当だった
- オブジェクト指向は言語ではなく設計パターン であり、特定の言語に限定されるものではない
- C でも構造体と関数ポインタでオブジェクト指向パターンを実装できる
- オブジェクト指向が常に最適な構造とは限らず、手続き型コードのほうが明快で管理しやすく、より速い結果を得られることもある
- SQLite 開発初期(2000年ごろ)には
- Java は未成熟で
- C++ は コンパイラ間の互換性問題が深刻 だった
→ 当時は C が 最も実用的で安全な選択 だった
- 現在でも SQLite を書き換えるだけの利点は乏しい
3. なぜ "安全な言語" で書かれなかったのか
- 近年は Rust や Go のような 安全なプログラミング言語 への関心が高いが、SQLite が最初に開発された当時(最初の10年間)は存在していなかった
- Go や Rust で書き直した場合、バグが増えたり、性能が低下したりする可能性がある
- これらの言語はメモリチェックなどの追加分岐(branch)コードを挿入するが、SQLite の品質戦略では 100% ブランチカバレッジ が重要であり、この点を満たせていない
- 安全な言語は out-of-memory 状況で主にプログラムを停止させるが、SQLite は メモリ不足の状況からでも復旧できるよう設計 されている
- Rust、Go などは依然として新しい言語であり、継続的な発展が必要である
- そのため SQLite 開発チームは安全な言語の発展を歓迎しつつも、SQLite の実装では依然として 実証済みの C の安定性 を重視している
それでも、いつか Rust で再実装される可能性 はある。Go は assert() を好まないため、Go で書かれる可能性は低い
- ただし Rust で書かれるには前提条件がある:
- Rust がより成熟し、変化の周期が遅くなって「古くて退屈な言語」になること
- 複数の言語から呼び出せる汎用ライブラリ を作れることが証明されること
- 組み込みなど OS のないデバイスでも動作するオブジェクトコード を生成できること
- コンパイル済みバイナリに対する 100% ブランチカバレッジのテストツール が整備されること
- OOM(メモリ不足)エラーから復旧可能 であること
- SQLite で C が処理しているすべての作業を 性能低下なしに Rust が実行できること
- もし Rust 愛好家(rustacean)が、上記の条件はすでに満たされており、SQLite を Rust で再コーディングすべきだと考えるなら、SQLite 開発者に直接連絡して意見を伝えてみるとよい
2件のコメント
Hacker Newsの意見
if (i >= array_length) panic("index out of bounds")のような防御コードを追加するが、そのコード自体はRustコンパイラが十分にテストしているのだから心配はいらないのではないか。この理屈を自分が正しく理解しているのか気になるget_unchecked()のような方法を使えば、bounds checkなしのアクセスも可能で、これによって安全性を保ちつつ性能を高められる get_uncheckedドキュメントif condition { panic(err) }構文をassert関数のように使えるのではないかと気になるCはSQLiteにとってもセキュリティリスクだ、という一節がありますが、十分にテストを書き、十分に熟練した開発者であっても同じなのでしょうか。ロジックや開発プロセスに問題がある可能性はあると思いますが、言語そのものがセキュリティ脆弱性だというのは理解しにくいです。実際、Cで書かれたインフラに依存しないプログラムはほとんどないのではないでしょうか。