C ดีที่สุด (2025)
(sqlite.org)- SQLite เป็น เอนจินฐานข้อมูลขนาดเบาที่พัฒนาด้วยภาษา C มาตั้งแต่ปี 2000 และไม่มีแผนจะเขียนใหม่ด้วยภาษาอื่น
- C ถูกมองว่าเป็นภาษาที่เหมาะกับ SQLite มากที่สุดในด้าน ประสิทธิภาพ, ความเข้ากันได้, การพึ่งพาส่วนประกอบต่ำ, และ ความเสถียร
- ภาษาเชิงวัตถุ (เช่น C++, Java) มี ข้อจำกัดในการเรียกใช้ข้ามภาษา สูง และแนวทางเชิงกระบวนการอาจเรียบง่ายและเร็วกว่า
- ภาษาแบบ ‘ปลอดภัย’ อย่าง Rust หรือ Go ยังไม่สุกงอมพอ และยังไม่สอดคล้องกับกลยุทธ์คุณภาพของ SQLite (เช่น การทดสอบ branch 100%, การกู้คืนจาก OOM)
- ในอนาคตยังเปิดโอกาสให้ย้ายไป Rust ได้ แต่ต้องมีเงื่อนไขหลายอย่างครบถ้วน เช่น ความสุกงอม·ความเข้ากันได้·ประสิทธิภาพ·การรองรับเครื่องมือ
1. เหตุผลที่ C เป็นตัวเลือกที่ดีที่สุด
- SQLite ถูก พัฒนาด้วยภาษา C มาตั้งแต่แรก เมื่อวันที่ 29 พฤษภาคม 2000 และจนถึงตอนนี้ก็ยังไม่มีแผนจะเปลี่ยนไปใช้ภาษาอื่น
- เหตุผลที่ C เหมาะกับการพัฒนา SQLite คือ ประสิทธิภาพ, ความเข้ากันได้, การพึ่งพาต่ำ, ความเสถียร
1.1 ประสิทธิภาพ
- SQLite เป็น ไลบรารีระดับล่างที่ถูกใช้งานอย่างเข้มข้น จึงจำเป็นต้องมีความเร็วสูง
- ตัวอย่างเช่น มีการพิสูจน์ประสิทธิภาพไว้ในเอกสาร “Internal Versus External BLOBs” และ “35% Faster Than The Filesystem”
- C ให้การควบคุมที่ใกล้ฮาร์ดแวร์มากจนถูกเรียกว่า ‘portable assembly language’ ขณะเดียวกันก็ยังคงความสามารถในการพกพาข้ามแพลตฟอร์ม
- แม้ภาษาอื่นจะอ้างว่า “เร็วพอๆ กับ C” แต่ ไม่มีภาษาไหนอ้างว่าเร็วกว่า C
1.2 ความเข้ากันได้
- แทบทุกระบบมีความสามารถในการ เรียกใช้ไลบรารีที่เขียนด้วย C
- ตัวอย่างเช่น Android สามารถเรียกใช้ SQLite จากแอปพลิเคชัน Java ผ่าน adapter ได้
- หาก SQLite เขียนด้วย Java ก็ จะไม่สามารถใช้งานในแอป iPhone ที่ใช้ Objective-C หรือ Swift ได้
1.3 การพึ่งพาต่ำ
- ไลบรารีที่เขียนด้วย C มี runtime dependency ต่ำมาก
- ในการตั้งค่าขั้นต่ำ SQLite ต้องใช้เพียงฟังก์ชันต่อไปนี้จาก standard C library
- memcmp(), memcpy(), memmove(), memset(), strcmp(), strlen(), strncmp()
- แม้เป็นการ build แบบเต็ม ก็ยังใช้เพียง malloc(), free() และการอ่านเขียนไฟล์เป็นหลัก
- ในทางกลับกัน ภาษาสมัยใหม่มักต้องการ runtime ขนาดหลาย MB และอินเทอร์เฟซนับพันรายการ
1.4 ความเสถียร
- C เป็น ภาษาที่เก่าและเปลี่ยนแปลงน้อย จึงให้พฤติกรรมที่ชัดเจนและคาดเดาได้
- สำหรับการพัฒนาเอนจินฐานข้อมูลที่ เล็ก เร็ว และเชื่อถือได้ อย่าง SQLite การที่สเปกภาษาจะไม่เปลี่ยนบ่อยถือเป็นเรื่องสำคัญ
2. เหตุผลที่ไม่ได้เขียนด้วยภาษาเชิงวัตถุ
-
นักพัฒนาบางคนอาจคิดว่าระบบซับซ้อนจะต้องพัฒนาด้วยภาษาเชิงวัตถุเท่านั้น แต่ SQLite ไม่ได้เป็นเช่นนั้น
-
ไลบรารีที่เขียนด้วย C++ หรือ Java มักใช้งานได้เฉพาะกับแอปพลิเคชันที่เขียนด้วยภาษาเดียวกัน
- ขณะที่ ไลบรารี C สามารถถูกเรียกใช้ได้จากแทบทุกภาษา
-
การเขียนเชิงวัตถุเป็น รูปแบบการออกแบบ ไม่ใช่ตัวภาษาเอง และสามารถจัดโครงสร้างแบบเชิงวัตถุใน C ได้เช่นกัน
-
เชิงวัตถุไม่ได้ดีที่สุดเสมอไป และในบางกรณี โค้ดแบบเชิงกระบวนการอาจเรียบง่ายกว่า ดูแลรักษาง่ายกว่า และมีประสิทธิภาพดีกว่า
-
ในช่วงเริ่มต้นการพัฒนา SQLite (ต้นทศวรรษ 2000) Java ยังไม่สุกงอม และ C++ ก็มี ปัญหาความเข้ากันได้ระหว่างคอมไพเลอร์ อย่างรุนแรง
- ในเวลานั้น C จึงเป็นตัวเลือกที่ดีกว่าอย่างชัดเจน และแม้ตอนนี้ก็แทบไม่มีประโยชน์ที่จะเขียนใหม่
3. เหตุผลที่ไม่ได้เขียนด้วย ‘ภาษาแบบปลอดภัย’
- ช่วงหลังมานี้ ภาษาแบบ ‘ปลอดภัย’ อย่าง Rust และ Go ได้รับความสนใจมากขึ้น แต่ SQLite ก็ยังคงพัฒนาด้วย C
- ในช่วง 10 ปีแรกของ SQLite ยังไม่มีภาษาแบบปลอดภัยให้ใช้
- แม้จะสามารถเขียนใหม่ด้วย Go หรือ Rust ได้ แต่ก็มี ความเสี่ยงต่อบั๊กใหม่และประสิทธิภาพที่ลดลง
- ภาษาแบบปลอดภัยจะเพิ่ม branch พิเศษ เช่น การตรวจสอบขอบเขตของอาร์เรย์
- ในโค้ดที่ถูกต้อง branch เหล่านี้จะไม่ถูกเรียกใช้ จึง ไม่สามารถทดสอบ branch ได้ครบ 100%
- ภาษาแบบปลอดภัยส่วนใหญ่จะ หยุดโปรแกรมเมื่อหน่วยความจำไม่พอ (OOM)
- แต่ SQLite ถูกออกแบบให้ กู้คืนได้ตามปกติแม้เกิด OOM จึงขัดกับพฤติกรรมดังกล่าว
- ภาษาแบบปลอดภัยที่มีอยู่ล้วน ยังใหม่และเปลี่ยนแปลงเร็ว
- ขณะที่ SQLite ชอบใช้ ภาษาที่เก่าและมีเสถียรภาพ
4. ความเป็นไปได้ในการย้ายไป Rust
- SQLite อาจถูกเขียนใหม่ด้วย Rust ได้ แต่ แทบเป็นไปไม่ได้ที่จะย้ายไป Go
- เหตุผล: Go ไม่ชอบ assert()
- เงื่อนไขเบื้องต้น สำหรับการย้ายไป Rust
- Rust ต้องสุกงอมมากขึ้น และความเร็วในการเปลี่ยนแปลงต้องช้าลงจนกลายเป็น ‘ภาษาที่เก่าและเสถียร’
- Rust ต้องสามารถสร้าง ไลบรารีเอนกประสงค์ที่ทุกภาษาสามารถเรียกใช้ได้
- Rust ต้องสร้าง object code ที่ทำงานได้แม้บน อุปกรณ์ฝังตัวที่ไม่มีระบบปฏิบัติการ
- ต้องมีชุดเครื่องมือที่รองรับ การทดสอบ branch coverage 100%
- ต้องมีกลไก กู้คืนจาก OOM
- ต้องพิสูจน์ได้ว่าสามารถพัฒนาโดย ไม่สูญเสียประสิทธิภาพในระดับเดียวกับ C
- นักพัฒนาที่คิดว่า Rust ผ่านเงื่อนไขเหล่านี้แล้ว สามารถ ติดต่อทีม SQLite โดยตรงเพื่อพูดคุยได้
5. บทสรุป
- SQLite เป็น เอนจินฐานข้อมูลที่เล็ก เร็ว และเชื่อถือได้ และคุณสมบัติของภาษา C ก็สอดคล้องกับเป้าหมายนั้น
- การเปลี่ยนไปใช้ภาษาเชิงวัตถุหรือภาษาแบบปลอดภัย ไม่มีประโยชน์เชิงปฏิบัติในด้านความเข้ากันได้ ประสิทธิภาพ และการควบคุมคุณภาพ
- ความเรียบง่ายและความเสถียร ของ C คือรากฐานที่สนับสนุนการดูแลรักษาระยะยาวและความน่าเชื่อถือของ SQLite
8 ความคิดเห็น
ยังไงก็เป็นโปรเจกต์ที่ไม่รับ PR อยู่แล้วนี่นะ ก็ใช้สิ่งที่พวกเขาอยากใช้กันไปนั่นแหละ
ถ้าเป็นสถานการณ์ที่ต้องการความกะทัดรัด ก็ไม่มีภาษาไหนมาแทน c, c++ หรือ rust ได้ครับ เพียงแต่คงมีนักพัฒนาไม่มากนักที่จะอินกับการพัฒนาโดยต้องคอยกังวลเรื่อง overflow หรือการถูกแฮ็กในระดับบิตบน struct หรือ map
ชื่อเรื่องกระตุ้นอารมณ์เกินไปนะครับ ถ้าไปดูบทความต้นฉบับ จะเห็นว่าเป็นบทความเกี่ยวกับเหตุผลว่าทำไม C ถึงเหมาะที่สุดสำหรับการพัฒนา sqlite ทุกคนใจเย็น ๆ กันหน่อยนะครับ
ไม่ใช่แค่นั้นนะ แม้แต่ตัวบทความเองก็ดูเหมือนจะเขียนไว้ตั้งแต่ 7 ปีก่อนแล้วใช่ไหม? เหมือนว่าจะมีการเติมเนื้อหาด้านท้ายเข้าไปแล้วอัปเดตบางส่วนในปี 2025... 🤦
สิ่งสำคัญคือการเลือกใช้ภาษาที่เหมาะกับสถานการณ์การพัฒนาที่หลากหลายได้ การตั้งชื่อแบบนั้นราวกับว่ามีภาษาใดภาษาหนึ่งที่ดีเสมอ แสดงให้เห็นว่าระดับความคิดแค่ม.ต้น...
ผมคิดว่าข้อดีที่ใหญ่ที่สุดของ C คือมันแตะต้องแก่นแท้ที่ว่า "คอมพิวเตอร์คือชุดของบิต" ได้โดยตรง ปรัชญาที่เรียบง่ายของ C และการทำ
reinterpret castingแบบสุดโต่ง ทำให้ผู้ใช้มักพอจะรู้ได้เกือบตลอดว่ามันจะถูกแปลเป็นแมชชีนโค้ดแบบไหน น่าดึงดูดตรงนี้แหละ ไม่ใช่ว่าเพราะเป็น C เลยถูกเรียกได้จากทุกภาษา แต่เป็นเพราะ ABI ต่างหากที่เรียกได้ และใน C มันแค่ทำให้คาดเดาได้ว่าอินพุตและเอาต์พุตเป็นชุดบิตแบบใด (หรือควรจะต้องคาดเดาได้) ผมยังคิดเสมอว่าเวลาถกกันเรื่องความเป็นไปได้ในการนำไปสร้างจริง การแยกให้ออกว่าสิ่งนี้เป็นไปไม่ได้ในระดับเครื่องทัวริง หรือเป็นไปไม่ได้แค่ในภาษาและเฟรมเวิร์กที่เราใช้อยู่ตอนนี้ เป็นเรื่องสำคัญมากความคิดเห็นจาก Hacker News
ผมคิดว่าไม่มีความจำเป็นต้อง หาเหตุผลมารองรับ ว่าทำไมทุกโปรเจ็กต์หรือโปรแกรมเมอร์ถึงไม่ได้ใช้ Rust หรือ Zig
บน Hacker News หรือแพลตฟอร์มอื่น ๆ มีแนวโน้มที่จะผลักดันภาษาเหล่านี้มากเกินไป
ถ้าทำผลงานที่ดีพอด้วย C ได้และผู้ใช้ก็พอใจ ก็ไม่มีเหตุผลให้คนนอกมาวิพากษ์วิจารณ์
เป็นกระแสที่ธรรมชาติอยู่แล้วที่คนซึ่งสนใจความก้าวหน้าทางเทคโนโลยีจะสำรวจ ความเป็นไปได้ของภาษาใหม่
เพียงแต่ถึงแม้คนนอกจะมีเสรีภาพในการแสดงความคิดเห็น แต่โปรเจ็กต์ก็ไม่ได้มีหน้าที่ต้องรับฟัง
Rust ช่วยลดการเผยตัวของบั๊กได้ แต่บั๊กประเภทเดิมก็ยังคงมีอยู่
นักพัฒนา C มักจะไวต่อ race condition มากกว่า ขณะที่ Rust มีความเสี่ยงที่จะเชื่อใจคำอธิบายว่า "ปลอดภัย" มากเกินไป
อีกทั้งใน Rust การแก้ไขก็ไม่ได้ง่ายเสมอไป ทำให้ ภาระในการรีแฟกเตอร์ อาจเพิ่มขึ้น
สุดท้ายแล้ว Rust เป็นภาษาที่น่าสนใจ แต่ไม่ใช่ยาวิเศษ และไม่ควรถูกยัดเยียด
ภาษาอย่าง Rust หรือ Zig มีเจตนาจะหลุดออกจากแพตเทิร์นเชิงวัตถุแบบดั้งเดิม
OOP เคยมีเสน่ห์ในฐานะกรอบแนวคิดที่ให้ความรู้สึกเหมือน "บรรลุธรรม" แต่ในทางปฏิบัติมักเพิ่มความซับซ้อนและทำลาย ความเป็นโมดูลาร์
เหมือนกับที่การใช้สว่านไฟฟ้าแทนสว่านมือเป็นเรื่องธรรมดา ถ้ามีเครื่องมือที่ดีกว่าก็ย่อมเป็นธรรมชาติที่จะใช้มัน
แต่เครื่องมือและการศึกษาสำหรับการเขียนโค้ด C อย่างปลอดภัยก็ควรพัฒนาให้เพียงพอด้วย
ผมเป็น Rustacean ค่อนข้างจริงจัง แต่ก็คิดว่าการเขียนทุกโปรเจ็กต์ใหม่ด้วย Rust ไม่ใช่เรื่องสมเหตุสมผล
ถ้าย้ายโปรเจ็กต์ C ที่ผ่านการพิสูจน์มาอย่างดีแล้วไปเป็น Rust ในระยะสั้นกลับมีโอกาสที่ บั๊กจะเพิ่มขึ้น มากกว่า
แต่บางส่วนก็กำลังท้าทายตัวเองด้วยการ rewrite เป็น Rust เช่น Limbo: โปรเจ็กต์ rewrite SQLite ทั้งหมดด้วย Rust
หนึ่งในข้อดีสำคัญของ SQLite คือหลายโปรเซสสามารถเข้าถึงพร้อมกันได้ และถ้าจุดนี้หายไป ขอบเขตการใช้งานก็จะแคบลง
ลองสร้างเวอร์ชันใหม่ขึ้นมาเองและทดลองดูว่าจะสำเร็จหรือไม่ก็ได้
ผมมีประสบการณ์ย้าย RediSearch ไป Rust
นั่นเป็นเพราะช่วงหลังมี ช่องโหว่ CVE เยอะ
ถ้า SQLite ไม่มีปัญหาแบบนี้ เหตุผลที่จะย้ายไป Rust ก็อ่อนลง
ผมคิดว่ากว่าจะเข้าใจจุดแข็งและข้อจำกัดของ Rust จริง ๆ คงต้องใช้เวลาหลายสิบปี
โดยเฉพาะใน แอปพลิเคชัน GUI ประโยชน์ของ Rust ยังไม่ชัดเจน
ถ้า Rust จะได้ความน่าเชื่อถือระดับเดียวกัน อาจต้องรอถึงราวปี 2040
อย่างที่ Linus พูดไว้ Rust จำเป็นต้องมีกลไก กู้คืนจาก OOM (Out of Memory)
เนื้อหาที่เกี่ยวข้องดูได้ที่ ลิงก์การสนทนา LKML
ในโค้ด embedded หรือ kernel ก็สามารถปิดความสามารถในการจัดสรรได้ทั้งหมด
กล่าวคือ Rust ให้การควบคุมหน่วยความจำได้อย่างเต็มที่อยู่แล้ว
คำกล่าวที่ว่า “ไม่มีภาษาอเนกประสงค์ไหนเร็วกว่า C” เป็นการเปรียบเทียบแบบแคบ ๆ ที่มองข้าม เวลาของนักพัฒนา
แทนที่จะใช้เวลา 5 ชั่วโมงเขียนโปรแกรมที่รัน 4 วินาทีด้วย C การใช้ภาษาอื่นเขียนเสร็จใน 5 นาทีแล้วรัน 5 วินาทีอาจสมจริงกว่า
ยิ่งมีผู้ใช้มาก ความต่างด้านความเร็วเพียงเล็กน้อยก็ยิ่งสะสมเป็นคุณค่าได้มาก
Rust ยังห่างไกลจากการเป็นภาษาที่ "น่าเบื่อแต่เสถียร"
C ถูกดูแลโดย คณะกรรมการสายอนุรักษนิยม ที่รักษาความเข้ากันได้อย่างเข้มงวด ในขณะที่ Rust ให้ นวัตกรรมมาก่อนความเข้ากันได้ เพื่อแก้ปัญหา
โค้ดเวอร์ชันเก่าก็ยัง build ต่อได้ด้วยคอมไพเลอร์ใหม่
ผมคิดว่านี่ดีกว่าวิธีแบบ C++ ที่ยังต้องแบกฟีเจอร์เก่าไว้ตลอด
ตรงกันข้าม ภาษาแบบ committee-designed อย่าง C++ หรือ Common Lisp กลับมีความซับซ้อนมากขึ้น
Rust เองก็มีขนาดใหญ่ จึงควรใช้อย่างระมัดระวังใน embedded หรือระบบแกนหลัก
ผมเห็นด้วยกับท่าทีแบบ “อย่าไปพังสิ่งที่ทำงานดีอยู่แล้ว”
ประวัติศาสตร์ของการพัฒนาภาษาเหมือนเป็นการวนซ้ำของการแก้ปัญหาแล้วสร้าง ความซับซ้อนใหม่ ขึ้นมา
C คือกรณีตัวแทนของปรัชญา “Worse is Better” ซึ่งประสบความสำเร็จมาหลายสิบปีเพราะความเรียบง่าย
ตรงกันข้าม Rust มุ่งไปที่ “Right Thing™”
ทุกวันนี้ในสภาพแวดล้อมส่วนใหญ่ภาระในการลงมือทำเองลดลงแล้ว มันจึงอาจเป็นทางเลือกที่ดีกว่า
แต่ก็ไม่มีความจำเป็นต้องย้ายโปรเจ็กต์ที่ประสบความสำเร็จอยู่แล้ว
ถ้าเป็นโปรเจ็กต์ใหม่ Rust มีโอกาสจะเป็นตัวเลือกที่ดีกว่า
ความเรียบง่ายและความเสถียร ของ C เป็นข้อดีที่ถูกประเมินต่ำเกินไป
ผมคิดว่าแทนที่จะเปลี่ยนตัวภาษาไปเรื่อย ๆ ควรขัดเกลามาตรฐานไลบรารีหรือ ecosystem มากกว่า
ไม่ใช่แค่ความเรียบง่าย แต่ การรับประกันพฤติกรรมที่แน่นอน ก็สำคัญด้วย
ตัวอย่างเช่น designated initializer, compound literal, alignas, memset_explicit ซึ่งผมชอบมาก
โดยส่วนตัวผมยังคิดว่า C ยังคงดีที่สุด
Rust มีไอเดียดี ๆ มากมาย แต่ก็เป็น ภาษาที่มีข้อเสียชัดเจน เช่นกัน
ตอนนี้บรรยากาศยังไม่ค่อยเอื้อต่อการพูดคุยถึงปัญหาของ Rust อย่างเยือกเย็นนัก
เช่นอาจเป็นเรื่อง learning curve หรือ ความซับซ้อนของแพ็กเกจจิง
คำว่า “ทุกระบบสามารถเรียกใช้ C library ได้” ไม่เป็นความจริงอีกต่อไปแล้ว
Rust และ Zig ก็รองรับข้อกำหนดนี้เช่นกัน
แต่ standard library ของ Rust มักจะ panic แทนการกู้คืนเมื่อเกิด OOM และเอกสารก็ยังไม่เพียงพอ
extern "C"หรือexportอย่างชัดเจนจึงจะเข้ากันได้กับ C ABIถ้าไม่ทำ ABI จะไม่ถูกกำหนดไว้ ซึ่งอาจ ไม่เสถียรกว่า C++ เสียอีก
โดยเฉพาะใน Linux distribution ที่แจกจ่าย Rust crate ปัญหานี้ยิ่งใหญ่ขึ้นได้