อย่างที่คนอื่นพูดคุณควรวัดประสิทธิภาพโปรแกรมของคุณก่อนและอาจไม่พบความแตกต่างในทางปฏิบัติ
ถึงกระนั้นจากระดับแนวความคิดฉันคิดว่าฉันจะอธิบายบางสิ่งที่ทำให้สับสนในคำถามของคุณ ประการแรกคุณถาม:
ค่าใช้จ่ายในการใช้งานฟังก์ชั่นยังคงมีความสำคัญในคอมไพเลอร์สมัยใหม่หรือไม่?
สังเกตคำสำคัญ "ฟังก์ชั่น" และ "คอมไพเลอร์" คำพูดของคุณแตกต่างกันเล็กน้อย:
โปรดจำไว้ว่าค่าใช้จ่ายของการเรียกใช้วิธีการอาจมีความสำคัญขึ้นอยู่กับภาษา
นี่คือวิธีการพูดคุยในแง่ของวัตถุ
ในขณะที่ "ฟังก์ชั่น" และ "วิธีการ" มักจะใช้แทนกันได้มีความแตกต่างเมื่อมันมาถึงค่าใช้จ่ายของพวกเขา (ซึ่งคุณถามเกี่ยวกับ) และเมื่อมันมาถึงการรวบรวม (ซึ่งเป็นบริบทที่คุณให้)
โดยเฉพาะอย่างยิ่งที่เราต้องรู้เกี่ยวกับการจัดส่งแบบคงที่เทียบกับการจัดส่งแบบไดนามิก ฉันจะเพิกเฉยต่อการเพิ่มประสิทธิภาพในขณะนี้
ในภาษาเช่น C ที่เรามักจะเรียกฟังก์ชั่นที่มีการจัดส่งแบบคงที่ ตัวอย่างเช่น:
int foo(int x) {
return x + 1;
}
int bar(int y) {
return foo(y);
}
int main() {
return bar(42);
}
เมื่อคอมไพเลอร์เห็นการเรียกfoo(y)
มันจะรู้ว่าฟังก์ชั่นfoo
ชื่อนั้นหมายถึงอะไรดังนั้นโปรแกรมเอาต์พุตสามารถข้ามไปยังfoo
ฟังก์ชั่นได้ซึ่งค่อนข้างถูก นั่นคือความหมายของการส่งแบบคงที่
อีกทางเลือกหนึ่งคือการจัดส่งแบบไดนามิกที่คอมไพเลอร์ไม่ทราบว่าจะเรียกใช้ฟังก์ชันใด ตัวอย่างเช่นนี่คือรหัส Haskell บางส่วน (เนื่องจากค่าเทียบเท่า C จะยุ่ง!):
foo x = x + 1
bar f x = f x
main = print (bar foo 42)
ต่อไปนี้เป็นbar
ฟังก์ชั่นที่โทรโต้แย้งf
ซึ่งอาจจะเป็นอะไร ดังนั้นคอมไพเลอร์จึงไม่สามารถรวบรวมbar
คำสั่งการกระโดดอย่างรวดเร็วเพราะมันไม่รู้ว่าจะข้ามไปที่ใด แต่รหัสที่เราสร้างขึ้นมาbar
นั้นจะf
เป็นการค้นหาที่ฟังก์ชันที่ชี้ไปแล้วข้ามไปที่มัน นั่นคือความหมายของการจัดส่งแบบไดนามิก
ทั้งสองตัวอย่างเหล่านั้นมีการฟังก์ชั่น คุณพูดถึงวิธีการต่างๆซึ่งอาจถือได้ว่าเป็นรูปแบบเฉพาะของฟังก์ชั่นที่จัดส่งแบบไดนามิก ตัวอย่างเช่นต่อไปนี้เป็น Python:
class A:
def __init__(self, x):
self.x = x
def foo(self):
return self.x + 1
def bar(y):
return y.foo()
z = A(42)
bar(z)
การy.foo()
เรียกใช้การจัดส่งแบบไดนามิกเนื่องจากเป็นการค้นหาค่าของfoo
คุณสมบัติในy
วัตถุและเรียกสิ่งที่พบ ไม่รู้ว่าy
จะมีคลาสA
หรือA
คลาสมีfoo
เมธอดดังนั้นเราจึงไม่สามารถกระโดดข้ามไปที่มันได้
ตกลงนั่นเป็นแนวคิดพื้นฐาน โปรดทราบว่าการจัดส่งแบบสแตติกนั้นเร็วกว่าการจัดส่งแบบไดนามิกโดยไม่คำนึงว่าเราจะรวบรวมหรือตีความ ทุกอย่างเท่าเทียมกัน การลงทะเบียนจะมีค่าใช้จ่ายเพิ่มเติมทั้งสองทาง
แล้วสิ่งนี้จะส่งผลกระทบต่อคอมไพเลอร์ที่ทันสมัยและปรับให้เหมาะสมได้อย่างไร
สิ่งแรกที่ควรทราบคือการจัดส่งแบบคงที่สามารถปรับให้เหมาะสมมากขึ้น: เมื่อเรารู้ว่าฟังก์ชันใดที่เรากำลังกระโดดไปสามารถทำสิ่งต่าง ๆ เช่นอินไลน์ ด้วยการจัดส่งแบบไดนามิกเราไม่ทราบว่าเรากระโดดจนถึงเวลาดำเนินการดังนั้นจึงไม่มีการเพิ่มประสิทธิภาพมากนักที่เราสามารถทำได้
ประการที่สองเป็นไปได้ในบางภาษาที่จะอนุมานว่าการกระจายแบบไดนามิกบางอย่างจะจบลงด้วยการกระโดดและด้วยเหตุนี้จึงเพิ่มประสิทธิภาพให้กับการกระจายแบบคงที่ สิ่งนี้ช่วยให้เราสามารถทำการเพิ่มประสิทธิภาพอื่น ๆ เช่นอินไลน์และอื่น ๆ
ในตัวอย่างของ Python ข้างต้นการอนุมานนั้นค่อนข้างสิ้นหวังเนื่องจาก Python อนุญาตให้โค้ดอื่นแทนที่คลาสและคุณสมบัติได้ดังนั้นจึงเป็นการยากที่จะอนุมานได้ว่าจะเก็บไว้ในทุกกรณี
หากภาษาของเราทำให้เรามีข้อ จำกัด มากขึ้นตัวอย่างเช่นโดยการ จำกัดy
ชั้นเรียนA
โดยใช้คำอธิบายประกอบดังนั้นเราสามารถใช้ข้อมูลนั้นเพื่อสรุปฟังก์ชั่นเป้าหมาย ในภาษาที่มีคลาสย่อย (ซึ่งเกือบทั้งหมดเป็นภาษาที่มีคลาส!) ที่จริงแล้วไม่เพียงพอเนื่องจากy
จริง ๆ แล้วอาจมีคลาส (ย่อย) ที่แตกต่างกันดังนั้นเราจึงต้องการข้อมูลเพิ่มเติมเช่นfinal
คำอธิบายประกอบของ Java เพื่อทราบว่าจะเรียกฟังก์ชันใด
Haskell ไม่ได้เป็นภาษา OO แต่เราสามารถอนุมานค่าของf
โดย inlining bar
(ซึ่งคงส่ง) ลงmain
แทนสำหรับfoo
y
เนื่องจากเป้าหมายของfoo
in in main
เป็นที่รู้จักกันในแบบคงที่การเรียกจะถูกส่งแบบสแตติกและอาจได้รับการอินไลน์และปรับให้เหมาะสมอย่างสมบูรณ์ (เนื่องจากฟังก์ชั่นเหล่านี้มีขนาดเล็กคอมไพเลอร์มีแนวโน้มที่จะอินไลน์พวกมัน )
ดังนั้นราคาลดลงมาที่:
- ภาษาส่งการโทรของคุณแบบคงที่หรือแบบไดนามิกหรือไม่?
- หากเป็นภาษาหลังให้ใช้งานเพื่ออนุมานเป้าหมายโดยใช้ข้อมูลอื่น ๆ (เช่นประเภทคลาสคำอธิบายประกอบอินไลน์ ฯลฯ ) หรือไม่
- การจัดส่งแบบสแตติก (เชิงอนุมานหรืออย่างอื่น) จะเพิ่มประสิทธิภาพได้อย่างไร
หากคุณใช้ภาษา "ไดนามิกมาก" ด้วยการแจกจ่ายแบบไดนามิกจำนวนมากและมีการรับประกันเล็กน้อยสำหรับคอมไพเลอร์การโทรทุกครั้งจะต้องเสียค่าใช้จ่าย หากคุณใช้ภาษาที่ "คงที่" แล้วคอมไพเลอร์ที่เป็นผู้ใหญ่จะสร้างโค้ดที่รวดเร็วมาก หากคุณอยู่ระหว่างนั้นก็สามารถขึ้นอยู่กับรูปแบบการเข้ารหัสของคุณและวิธีการใช้งานที่ชาญฉลาด