ความแตกต่างระหว่าง Compiler กับ Interpreter
คอมไพเลอร์ (Compiler) และ อินเทอร์พรีเตอร์ (Interpreter) คือโปรแกรมที่แปลซอร์สโค้ด (ไฟล์ที่มีโปรแกรม) เป็นรหัสเครื่องที่โปรเซสเซอร์ สามารถเข้าใจ ได้ คอมไพเลอร์แปลซอร์สโค้ดโดยรวมและดำเนินการเทคนิคการเพิ่มประสิทธิภาพต่างๆ ก่อนทำการแมปกับ machine code ที่เรียกใช้งานได้ แต่อินเทอร์พรีเตอร์ แปลซอร์สโค้ดตามความจำเป็นในรันไทม์โดยจะจับคู่ซอร์สโค้ดกับ machine code เกือบจะในทันที
ขอบเขต
- บทความนี้กล่าวถึงการแนะนำคอมไพเลอร์และอินเทอร์พรีเตอร์ โดยจะครอบคลุมถึงกลไกต่างๆ เช่น วิธีการ ทำงาน ของ สิ่งเหล่านี้พื้นที่ออกแบบและวิธีการต่างๆ ในการสร้างอินเทอร์พรีเตอร์และคอมไพเลอร์
- บทความนำเสนอความแตกต่างระหว่างคอมไพเลอร์และอินเทอร์พรีเตอร์พร้อมประเด็นสำคัญเกี่ยวกับข้อดีและข้อเสีย และแนวคิดภายในเพื่อทำความเข้าใจการทำงานของคอมไพเลอร์และอินเทอร์พรีเตอร์
บทนำ
ภาษาการเขียนโปรแกรมได้รับการออกแบบเพื่อแสดงวิธีแก้ปัญหาเชิงคำนวณสำหรับปัญหาเฉพาะโดเมนที่อาจใช้การคำนวณทางคณิตศาสตร์อย่างง่ายหรืออาจเป็น ความท้าทายด้าน อัลกอริทึมที่ซับซ้อนแต่โดยรวมแล้วเป้าหมายคือการมีภาษาที่มนุษย์อ่านได้ ในทางกลับกัน คอมพิวเตอร์จะรันคำสั่งในภาษาแม่ของมัน ซึ่งเป็นลำดับของไบนารีที่เข้ารหัสการทำงาน โลกของคอมพิวเตอร์และมนุษย์เชื่อมโยงกันด้วยโปรแกรมที่แปลภาษา X เป็น Y
คอมไพเลอร์และอินเทอร์พรีเตอร์ มีหน้าที่แสดงความหมายของโปรแกรมและแปลเป็นการแสดงว่าคอมพิวเตอร์ของคุณสามารถดำเนินการได้ พอจะพูดได้ว่ารหัสคือข้อมูลข้อความที่ได้รับการแปลเป็นภาษาเครื่อง ขั้นตอนการแปลอาจแตกต่างกันไปขึ้นอยู่กับการใช้งาน
คอมไพเลอร์สร้างการแสดงแทนโปรแกรมที่มีความหมาย จากนั้นเปลี่ยนเป็นชุดคำสั่งที่โปรเซสเซอร์เฉพาะสามารถดำเนินการได้ แต่อินเทอร์พรีเตอร์ แปลซอร์สโค้ดเมื่อจำเป็นและดำเนินการเกือบจะในทันที ในบทความเพิ่มเติม เราจะสำรวจความแตกต่างโดยละเอียดระหว่างคอมไพเลอร์และอินเทอร์พรีเตอร์
คอมไพเลอร์คืออะไร?
คอมไพเลอร์คือโปรแกรมที่สร้างแทนความหมายของโค้ดและสร้างชุดคำสั่งที่คอมพิวเตอร์สามารถดำเนินการได้ คอมไพเลอร์แปลซอร์สโค้ดโดยรวม โปรแกรมที่ สร้างโดยคอมไพเลอร์มักจะทำงานได้เร็วกว่าโปรแกรมที่สร้างโดยอินเทอร์พรีเตอร์ คอมไพเลอร์ต้องการข้อมูลที่จำเป็นเพื่อเปิดใช้งานการเพิ่มประสิทธิภาพขั้นสูงและแสดง การ แสดงโค้ดที่มีประสิทธิภาพ กระบวนการปรับให้เหมาะสมสามารถรับค่าในนิพจน์ในระหว่างการคอมไพล์
การคอมไพล์เป็นลำดับของการแปลงที่แปลภาษาต้นฉบับเป็นภาษาเป้าหมาย ภาษาเป้าหมายอาจอยู่ในรูปแบบของภาษาการเขียนโปรแกรมอื่น เนื่องจากคอมไพเลอร์บางตัว เช่น Dart สามารถแปลเป็น JavaScript ได้ ในขณะเดียวกัน คอมไพเลอร์อื่น ๆ เช่น Java จะสร้าง bytecode ซึ่งได้รับการตีความโดย JVM (Java Virtual Machine) เพื่อสร้างชุดคำสั่งที่โปรเซสเซอร์สามารถดำเนินการได้
ที่กล่าวว่าเป็นมูลค่าการกล่าวขวัญว่าคอมไพเลอร์อาจถูกนำไปใช้เพื่อวัตถุประสงค์ในการออกแบบที่แตกต่างกัน การออกแบบคอมไพเลอร์และตัวอย่างบางส่วนของภาษาโปรแกรมที่ใช้ได้แสดงไว้ด้านล่าง:
ชนิด | เป้าหมายการออกแบบ | ตัวอย่าง |
---|---|---|
Cross-compiler | สร้างรหัสปฏิบัติการสำหรับแพลตฟอร์มอื่น | Microsoft C, MinGW |
Transpiler | แปลซอร์สโค้ดระหว่างภาษาระดับสูง | Cfront, ScriptSharp |
Decompiler | แปลภาษาระดับต่ำเป็นภาษาระดับสูง | Disassembler |
Compiler-Compiler | สร้าง parser อินเทอร์พรีเตอร์หรือคอมไพเลอร์จากรูปแบบหนึ่งของคำอธิบายอย่างเป็นทางการของภาษาที่ใช้ไวยากรณ์ | ANTLR, Bison |
Phases of Compilation
การออกแบบ คอมไพเลอร์มักจะเป็นไปตามสถาปัตยกรรมที่เรียบง่ายที่ประกอบด้วย Front-end , Middle-end และ Back-end โปรดทราบว่าส่วนนี้ใช้เป็นแนวทางในการพัฒนาคอมไพเลอร์ เราไม่ได้ตั้งเป้าที่จะให้ครอบคลุมในแต่ละขั้นตอนที่กล่าวถึง

ส่วนประกอบ ส่วนหน้าจะสแกนและตรวจสอบไวยากรณ์ (syntax) และความหมาย (semantics) ที่แสดงในโปรแกรม ส่วนหน้าจัดการการระบุแต่ละโทเค็นจากไฟล์ข้อความ (source code) การตรวจสอบความถูกต้องของประโยคดำเนินการวิเคราะห์เชิงความหมายและสร้างการนำเสนอที่เป็น representation ของซอร์สโค้ดในรูปแบบของ Abstract Syntax Tree.

Front-end components ประกอบด้วยชุดการแปลงและการวิเคราะห์ดังต่อไปนี้:
1.Preprocessing บรรทัดของโค้ดที่ขึ้นต้นด้วย อักขระ #ตัวกำลังถูกประมวลผลล่วงหน้าในเฟสนี้ ขั้นตอนการรวบรวม เหล่านี้ เกี่ยวข้องกับการแทนที่มาโคร (ส่วนของโค้ด) เป็น รูปแบบวากยสัมพันธ์ที่ถูกต้องการรวมไฟล์เพราะเมื่อเรานำเข้าไลบรารี มันไม่มี โค้ดจริง เช่น #include และคำสั่งคอมไพล์แบบมีเงื่อนไข (เทคนิคในการดำเนินการหรือไม่รันคำสั่งเฉพาะของโค้ดตามเงื่อนไขบางประการ) ซึ่งส่งผลให้โปรแกรมใน รูปแบบ ปกติที่ไม่มีคำสั่งก่อนการประมวลผลใดๆ
2.Lexical analysis กระบวนการนี้แบ่งซอร์สโค้ดออกเป็นลำดับของโทเค็นคำศัพท์ กล่าวอีกนัยหนึ่งการวิเคราะห์ศัพท์เป็นกระบวนการในการระบุประเภทของไวยากรณ์บางประเภทในลักษณะเดียวกับที่คุณระบุส่วนของคำพูดในประโยคนี้ ด้วยเหตุนี้ จึงประกอบด้วยการดำเนินการย่อยสองอย่าง:การสแกนและการประเมิน
- การสแกนเป็นกระบวนการแบ่งข้อความออกเป็นโทเค็นและจัดหมวดหมู่โทเค็นตามคลาสของ หน่วย วากยสัมพันธ์เช่น โทเค็นอาจเป็นค่าคงที่ประเภทตัวระบุตัวดำเนินการอักขระพิเศษคีย์เวิร์ดเป็นต้น
- การประเมินเกี่ยวข้องกับการแปลง lexemes (ลำดับของอักขระที่ตรงกับรูปแบบ) เป็นค่าที่ประมวลผล เช่น 5+3 -> 8
3.Syntax analysis การวิเคราะห์ไวยากรณ์ตรวจสอบลำดับโทเค็นและระบุโครงสร้างไวยากรณ์ของโปรแกรม เมื่อสิ้นสุดระยะนี้ โครงสร้างไวยากรณ์ (หรือที่เรียกว่า parse tree) จะถูกสร้างขึ้น
void function(int a, int b) {
return a + b * (a - b);
}

4.Semantic Analysis เฟสนี้มีงานในการใส่คำอธิบายประกอบข้อมูลเชิงความหมายให้กับแผนผังไวยากรณ์ ซึ่งส่งผลให้เกิดการสร้างตารางสัญลักษณ์ ตารางสัญลักษณ์ประกอบด้วยหน่วยที่แยกวิเคราะห์ซึ่งสัมพันธ์กับข้อมูลเกี่ยวกับลักษณะที่ปรากฏของพวกเขาในแหล่งที่มา มาดูตัวอย่างกัน:
double sum_of_square(double a, double b) {
return a*a + b*b;
}
Symbol Name | Type | Scope |
---|---|---|
sum_of_square | function, double | local |
a | double | function parameter |
b | double | function parameter |
เมื่อดูตัวอย่างของตารางสัญลักษณ์ จะมีข้อมูลประเภทและขอบเขตของวัตถุ การวิเคราะห์เชิงความหมายอาจเกี่ยวข้องกับการตรวจสอบประเภท การกำหนดที่แน่นอน หรือการผูกวัตถุ ซึ่งเป็นพื้นฐานสำหรับการตรวจสอบว่าโค้ดของคุณมีความหมายหรือไม่ ตัวอย่างเช่น การเพิ่มสตริงด้วยจำนวนเต็มหมายความว่าอย่างไร JavaScript ยอมให้คำสั่งที่ไม่มีความหมายนี้เกิดขึ้น ซึ่งทำให้มีข้อบกพร่องมากขึ้น
คอมไพเลอร์บางตัวพิสูจน์ความแปรปรวน (คุณสมบัติของอ็อบเจกต์ทางคณิตศาสตร์ซึ่งระบุว่าอ็อบเจกต์จะยังคงไม่เปลี่ยนแปลงหลังจากการดำเนินการบางอย่างหรือการแปลงบางประเภท) และคุณสมบัติอื่นๆ ซึ่งส่งผลให้ระบบแข็งแกร่งและเชื่อถือได้

ค่ากลางจะปรับโครงสร้างการแยกวิเคราะห์ที่สร้างให้เหมาะสม – ซึ่งแสดงในรูปแบบสื่อกลาง (IR) งานส่วนใหญ่ในเลเยอร์ระดับกลางนั้นมาจากเฟรมเวิร์กของคอมไพเลอร์ส่วนใหญ่ เช่น LLVM ระยะนี้อาจรวมถึงสิ่งต่อไปนี้:
การ วิเคราะห์ – กระบวนการนี้จะรวบรวมข้อมูลโปรแกรมจากแผนผังการแยกวิเคราะห์ และตรวจสอบบางกรณีที่การเพิ่มประสิทธิภาพอาจเกิดขึ้น
การเพิ่มประสิทธิภาพ – เปลี่ยน IR เป็นรูปแบบที่เทียบเท่าได้เร็วขึ้น

คอมโพเนนต์แบ็คเอนด์ของคอมไพเลอร์จะจัดการการแมปของการปรับให้เหมาะสมของสถาปัตยกรรม IR กับ CPU โดยเฉพาะและการสร้างโค้ด ซึ่งรวมถึงสิ่งต่อไปนี้:
การเพิ่มประสิทธิภาพขึ้นอยู่กับเครื่อง – สิ่งนี้เกี่ยวข้องกับชุดของการปรับให้เหมาะสมที่สถาปัตยกรรม CPU อนุญาต
การสร้างโค้ด – จะแปลคำสั่งแอสเซมบลีที่สร้างขึ้นหลังจากการเพิ่มประสิทธิภาพที่ขึ้นกับเครื่องเป็นภาษาเครื่องดั้งเดิมของระบบเป้าหมาย
พูดนอกเรื่อง:
C และ Java เป็นตัวเลือกที่น่าสนใจของภาษาที่คอมไพล์ซึ่งทั้งคู่สร้างไฟล์ปฏิบัติการได้เมื่อคอมไพล์ โปรแกรม AC จะคอมไพล์ไปยังรหัสแอสเซมบลีซึ่งเมื่อดำเนินการ จะแจ้งให้ประกอบโค้ดอ็อบเจ็กต์ที่เคลื่อนย้ายได้ ในทางกลับกัน Java จะคอมไพล์เป็นโค้ดไบต์ซึ่งเป็นการแสดงคำสั่งระดับกลางของคำสั่งเหมือนแอสเซมบลีที่ตีความโดย JVM เมื่อดำเนินการ
ในกรณีของ Java เราสังเกตว่าอาจใช้ล่ามสำหรับการแม็พโค้ดไบต์ระดับต่ำกับคำสั่งแอสเซมบลีที่สัมพันธ์กับเครื่องเป้าหมายในลักษณะนี้ มันทำให้จาวาพกพาสะดวกและประสิทธิภาพ ส่วนเฉพาะด้านล่างจะกล่าวถึงอินเทอร์พรีเตอร์อย่างละเอียดยิ่งขึ้น
อินเทอร์พรีเตอร์คืออะไร?
อินเทอร์พรีเตอร์สร้างรหัสเครื่องโดยการแปลรหัสแต่ละบรรทัดทีละรายการ เนื่องจากอินเทอร์พรีเตอร์แปลโปรแกรมของคุณขณะรันไทม์ จึงมีโอกาสน้อยกว่าสำหรับการดำเนินการปรับให้เหมาะสม ในขณะเดียวกัน การแปลโปรแกรมขณะรันไทม์ส่งผลให้ระบบประเภทไดนามิกมีความยืดหยุ่นและง่ายต่อการจัดการข้อผิดพลาด เนื่องจากภาษาที่คอมไพล์ขึ้นชื่อในเรื่องข้อความแสดงข้อผิดพลาดที่เป็นความลับ
ในภาษาที่คอมไพล์แล้ว กระบวนการคอมไพล์ซ้ำอาจต้องเริ่มการคอมไพล์ใหม่ทั้งหมด แม้ว่าจะมีการเปลี่ยนแปลงโค้ดเพียงเล็กน้อยก็ตาม กระบวนการนี้อาจใช้เวลานานถึง 30 – 40 นาทีสำหรับโปรเจ็กต์ขนาดใหญ่ในคอมไพเลอร์บางตัว คอมไพเลอร์สมัยใหม่ได้ปรับให้เหมาะสมสำหรับสิ่งนี้ (เช่น Dart VM Hot Reload เพื่อลดเวลาในการพัฒนาและเพิ่มประสิทธิภาพสูงสุด แต่การกำหนดคุณลักษณะของภาษาการเขียนโปรแกรมที่ตีความนั้นมีไว้สำหรับการสร้างต้นแบบที่รวดเร็วและการพัฒนาที่คล่องตัว
อินเทอร์พรีเตอร์มาพร้อมกับเป้าหมายการออกแบบที่แตกต่างกัน ให้เราดูอินเทอร์พรีเตอร์และเขียนวัตถุประสงค์:
ชนิด | เป้าหมายการออกแบบ | ตัวอย่าง |
---|---|---|
Bytecode interpreter | แปล bytecode และแมปกับภาษาเครื่อง | CLISP, .NET |
Threaded code interpreter | แมปพอยน์เตอร์ที่รวบรวมจากภาษาระดับสูงไปยังคำสั่งเครื่องเฉพาะ | ไม่มี |
Just-In-Time (JIT) | การแสดงภาษาระดับกลางถูกคอมไพล์เป็นรหัสเครื่องดั้งเดิมระหว่างรันไทม์ | Julia, PyPy |
Phases of Interpretation

ดังที่เราได้กล่าวไปแล้วในส่วนที่แล้ว พื้นที่การออกแบบของอินเทอร์พรีเตอร์อาจมีขอบเขตขึ้นอยู่กับเป้าหมาย ตั้งแต่การแปลภาษาระดับสูงโดยตรงไปจนถึงโค้ดเนทีฟ ไปจนถึงการแปลโค้ดอ็อบเจ็กต์ที่เพิ่มประสิทธิภาพคอมไพเลอร์เป็นโค้ดเครื่อง ดังนั้นประสิทธิภาพอาจขึ้นอยู่กับการใช้งานและการออกแบบ
เนื่องจากเราได้พูดถึงรูปแบบการแปลที่ซับซ้อนมากขึ้นแล้ว (compilation) การตีความจึงเป็นแบบจำลองที่ง่ายกว่าที่จะลบส่วนจำนวนมากของ compilation

- การประมวลผล ล่วงหน้า : เกี่ยวข้องกับการใส่คำอธิบายประกอบบรรทัดของโค้ด และการแทนที่มาโครเพื่อปรับการแสดงโค้ดให้เป็นมาตรฐาน
- การวิเคราะห์คำศัพท์ : มันเกี่ยวข้องกับการแทนที่โทเค็นและจับคู่โทเค็นกับหมวดหมู่ของไวยากรณ์ที่พวกเขาอยู่ เช่น การค้นหาชุดของตัวแปรทั้งหมดในโปรแกรม
- การวิเคราะห์ไวยากรณ์ : เกี่ยวข้องกับการตรวจสอบไวยากรณ์ที่ไม่ถูกต้องที่แสดงในโปรแกรม
- การวิเคราะห์เชิงความหมาย : มันเกี่ยวข้องกับการตรวจสอบการดำเนินการที่มีความหมายและกำจัดอินสแตนซ์ที่เป็นไปได้ของนิพจน์ที่ไม่มีความหมาย เช่น string + intล่ามบางคนอาจเรียกการโปรโมตประเภทซึ่งภาษาที่พิมพ์อย่างแรงจะเรียกใช้ข้อผิดพลาดรันไทม์ (ลองใช้ใน JavaScript และ Python
- การสร้างรหัสเครื่อง : การสร้างรหัสอาจเกี่ยวข้องกับกระบวนการคอมไพล์หรือการจับคู่โดยตรงกับคำสั่งเครื่อง ขึ้นอยู่กับการออกแบบที่ตั้งใจไว้ของภาษาการเขียนโปรแกรม
กรณีของ Python และ Julia
ทั้ง Python และ Julia เป็นตัวเลือกที่น่าสนใจในการพูดคุยเกี่ยวกับอินเทอร์พรีเตอร์ การใช้งานเริ่มต้นของ Python เรียกว่า CPython ซึ่งเกี่ยวข้องกับกระบวนการคอมไพล์สำหรับการแมปซอร์สโค้ดกับ C-bindings ดั้งเดิม เนื่องจาก Python เป็นภาษาไดนามิก คอมไพเลอร์จึงไม่สามารถคาดเดาอะไรเกี่ยวกับข้อมูลประเภทได้ ดังนั้นทุกอย่างจึงแสดงด้วยประเภทสากลที่เรียกว่า Python Object ซึ่งส่งผลให้โอกาสในการเพิ่มประสิทธิภาพน้อยลง
ในทางตรงกันข้าม คอมไพเลอร์ JIT ของ Julia จะปรับรหัสเครื่องที่สร้างขึ้นให้เหมาะสม เนื่องจากสามารถระบุข้อมูลประเภท (among other things) ในภาษาได้ หากเราเปรียบเทียบ Python กับ Julia เราจะสังเกตเห็นความแตกต่างโดยเฉลี่ยและการเริ่มต้นใช้งานโค้ด การเรียกคอมไพเลอร์ JIT จะต้องใช้เวลามากขึ้นในการดำเนินการในตอนแรก เนื่องจากโค้ดต้องได้รับการคอมไพล์และปรับให้เหมาะสมสำหรับการสร้างโค้ดเครื่องที่มีประสิทธิภาพ ซึ่งมีค่าใช้จ่าย ในขณะเดียวกัน Python จะแปลโค้ดทันทีโดยใช้เวลารวบรวมน้อยที่สุด อย่างไรก็ตาม โดยเฉลี่ยแล้ว คาดว่าโค้ด JIT-ed จะทำงานได้ดีกว่าโค้ดอินเทอร์พรีเตอร์
credit : https://www.scaler.com/topics/c/