ฟังก์ชันอินไลน์ v. มาโครใน C ค่าโสหุ้ย (หน่วยความจำ/ความเร็ว) คืออะไร?

ฉันค้นหา Stack Overflow เพื่อหาข้อดี/ข้อเสียของมาโครที่มีลักษณะคล้ายฟังก์ชันกับฟังก์ชันแบบอินไลน์

ฉันพบการสนทนาต่อไปนี้: ข้อดีข้อเสียของ ฟังก์ชันมาโคร/วิธีการแบบอินไลน์ที่แตกต่างกันใน C

...แต่มันไม่ได้ตอบคำถามหลักของฉัน

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

มีค่าใช้จ่ายที่แตกต่างกันขึ้นอยู่กับคอมไพเลอร์หรือไม่? ฉันมีทั้ง icc และ gcc ให้เลือก

ข้อมูลโค้ดของฉันที่ฉันกำลังทำให้เป็นโมดูลคือ:

double AttractiveTerm = pow(SigmaSquared/RadialDistanceSquared,3);
double RepulsiveTerm = AttractiveTerm * AttractiveTerm;
EnergyContribution += 
   4 * Epsilon * (RepulsiveTerm - AttractiveTerm);

เหตุผลของฉันในการเปลี่ยนเป็นฟังก์ชัน/มาโครแบบอินไลน์คือฉันสามารถวางมันลงในไฟล์ c แล้วคอมไพล์ฟังก์ชัน/มาโครอื่น ๆ ที่คล้ายกัน แต่มีเงื่อนไขที่แตกต่างกันเล็กน้อย

e.g.:

double AttractiveTerm = pow(SigmaSquared/RadialDistanceSquared,3);
double RepulsiveTerm = pow(SigmaSquared/RadialDistanceSquared,9);
EnergyContribution += 
   4 * Epsilon * (RepulsiveTerm - AttractiveTerm);

(สังเกตความแตกต่างในบรรทัดที่สอง...)

ฟังก์ชันนี้เป็นส่วนสำคัญของโค้ดของฉันและถูกเรียกหลายพันครั้งต่อขั้นตอนในโปรแกรมของฉัน และโปรแกรมของฉันก็ดำเนินการหลายล้านขั้นตอน ดังนั้น ฉันต้องการให้มีค่าใช้จ่ายน้อยที่สุด เหตุใดฉันจึงเสียเวลากังวลเกี่ยวกับค่าใช้จ่ายของ inlining v. ในการแปลงโค้ดให้เป็นมาโคร

จากการสนทนาครั้งก่อน ฉันทราบถึงข้อดี/ข้อเสียอื่นๆ (ความเป็นอิสระของประเภทและข้อผิดพลาดที่ตามมา) ของมาโครแล้ว... แต่สิ่งที่ฉันอยากรู้มากที่สุดและยังไม่รู้ในขณะนี้คือประสิทธิภาพ

ฉันรู้ว่าทหารผ่านศึก C บางคนจะมีข้อมูลเชิงลึกที่ดีสำหรับฉัน!!


person Jason R. Mick    schedule 07.03.2011    source แหล่งที่มา
comment
ใช้ตัวสร้างโปรไฟล์แล้วตัดสินใจ   -  person Erik    schedule 08.03.2011
comment
ไม่มีคำตอบทั่วไปเหรอ?   -  person Jason R. Mick    schedule 08.03.2011
comment
+1 สำหรับลิงก์ไปยังหน้าแรกของ stackoverflow :) ฮ่าๆ   -  person 0x90    schedule 28.05.2013
comment
ได้รับการเพิ่มประสิทธิภาพ 100%+ โดยการแปลงฟังก์ชันการตรวจสอบอักขระบางตัวเป็น MACRO() ตัวแปรในตัวแยกวิเคราะห์ที่มีการเรียกใช้ฟังก์ชันเหล่านี้บ่อยมาก inline และ __forceinline มีการเพิ่มขึ้นเพียง 50% เทียบกับการเรียกใช้ฟังก์ชันปกติ แต่ MACROs ส่งได้ 100% (ติดตามประสิทธิภาพเป็นรอบ) - ดังนั้น หากฟังก์ชันอินไลน์สั้นและเรียบง่าย ให้ลองใช้มาโครและการวัดประสิทธิภาพ   -  person CodeAngry    schedule 05.04.2014
comment


คำตอบ (9)


การเรียกใช้ฟังก์ชันอินไลน์อาจสร้างการเรียกใช้ฟังก์ชันหรือไม่ก็ได้ ซึ่งโดยทั่วไปจะมีค่าใช้จ่ายเพียงเล็กน้อยมาก สถานการณ์ที่แน่นอนที่ฟังก์ชัน inline ได้รับการอินไลน์จะแตกต่างกันไปขึ้นอยู่กับคอมไพลเลอร์ ส่วนใหญ่ใช้ความพยายามโดยสุจริตในการอินไลน์ฟังก์ชันเล็กๆ (อย่างน้อยเมื่อเปิดใช้งานการปรับให้เหมาะสม) แต่ไม่มีข้อกำหนดให้ทำเช่นนั้น (C99, §6.7.4):

การทำให้ฟังก์ชันเป็นฟังก์ชันอินไลน์แสดงว่าการเรียกใช้ฟังก์ชันนั้นเร็วที่สุดเท่าที่จะเป็นไปได้ ขอบเขตที่ข้อเสนอแนะดังกล่าวมีประสิทธิผลนั้นขึ้นอยู่กับการนำไปปฏิบัติ

มาโครมีโอกาสน้อยที่จะเกิดค่าใช้จ่ายดังกล่าว (แต่อีกครั้ง มีเพียงเล็กน้อยที่จะป้องกันไม่ให้คอมไพเลอร์ทำอะไรบางอย่าง มาตรฐานไม่ได้กำหนดว่าโปรแกรมรหัสเครื่องใดจะต้องขยายออกไป มีเพียงพฤติกรรมที่สังเกตได้ของโปรแกรมที่คอมไพล์แล้ว)

ใช้อะไรก็ได้ที่สะอาดกว่า ประวัติโดยย่อ. หากเป็นเรื่องสำคัญให้ทำสิ่งที่แตกต่างออกไป

นอกจากนี้ สิ่งที่ fizzer พูด; การเรียกไปยัง pow (และการหาร) มักจะมีราคาแพงกว่าค่าใช้จ่ายในการเรียกใช้ฟังก์ชัน การลดสิ่งเหล่านั้นให้เหลือน้อยที่สุดเป็นการเริ่มต้นที่ดี:

double ratio = SigmaSquared/RadialDistanceSquared;
double AttractiveTerm = ratio*ratio*ratio;
EnergyContribution += 4 * Epsilon * AttractiveTerm * (AttractiveTerm - 1.0);

EnergyContribution ประกอบด้วยคำที่มีลักษณะเช่นนี้เท่านั้นใช่หรือไม่ หากเป็นเช่นนั้น ให้ดึง 4 * Epsilon ออกมา และบันทึกการคูณสองครั้งต่อการวนซ้ำ:

double ratio = SigmaSquared/RadialDistanceSquared;
double AttractiveTerm = ratio*ratio*ratio;
EnergyContribution += AttractiveTerm * (AttractiveTerm - 1.0);
// later, once you've done all of those terms...
EnergyContribution *= 4 * Epsilon;
person Stephen Canon    schedule 08.03.2011

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

ฟังก์ชันแบบอินไลน์หรืออย่างอื่นที่คอมไพลเลอร์รู้ และสามารถตัดสินใจได้ว่าจะทำอย่างไรกับมัน คำหลักที่ผู้ใช้ระบุ inline เป็นเพียงข้อเสนอแนะและคอมไพเลอร์อาจเขียนทับคำหลักนั้น เป็นเรื่องเกินจริงที่ในกรณีส่วนใหญ่จะส่งผลให้โค้ดดีขึ้น

ผลข้างเคียงอีกประการหนึ่งของคอมไพเลอร์เมื่อทราบถึงฟังก์ชันต่างๆ ก็คือ คุณสามารถบังคับให้คอมไพเลอร์ทำการตัดสินใจบางอย่างได้ ตัวอย่างเช่น การปิดใช้งานการอินไลน์โค้ดของคุณ ซึ่งอาจช่วยให้คุณดีบักหรือโปรไฟล์โค้ดของคุณได้ดีขึ้น อาจมีกรณีการใช้งานอื่นๆ อีกมากมายที่ฟังก์ชันอินไลน์เปิดใช้งานเทียบกับมาโคร

มาโครนั้นทรงพลังอย่างยิ่ง และเพื่อสำรองข้อมูลนี้ ฉันจะอ้างอิงการทดสอบของ Google และ Google mock มีเหตุผลหลายประการในการใช้มาโคร :D

การดำเนินการทางคณิตศาสตร์อย่างง่ายที่เชื่อมโยงเข้าด้วยกันโดยใช้ฟังก์ชันมักจะถูกอินไลน์โดยคอมไพเลอร์ โดยเฉพาะอย่างยิ่งถ้าฟังก์ชันถูกเรียกใช้เพียงครั้งเดียวในขั้นตอนการแปล ดังนั้น ฉันไม่แปลกใจเลยที่คอมไพเลอร์จะตัดสินใจแบบอินไลน์ให้กับคุณ ไม่ว่าคีย์เวิร์ดนั้นจะระบุไว้ในสภาพอากาศใดก็ตาม

อย่างไรก็ตาม หากคอมไพลเลอร์ไม่มี คุณก็สามารถปรับส่วนของโค้ดด้วยตนเองได้ หากคุณทำให้แบนราบ บางทีมาโครอาจทำหน้าที่เป็นนามธรรมที่ดี เพราะพวกมันนำเสนอความหมายที่คล้ายคลึงกันกับฟังก์ชัน "ของจริง"

ปม

ดังนั้น คุณต้องการให้คอมไพเลอร์ตระหนักถึงขอบเขตทางลอจิคัลบางอย่างเพื่อให้สามารถสร้างโค้ดฟิสิคัลได้ดีขึ้น หรือคุณต้องการบังคับใช้การตัดสินใจกับคอมไพลเลอร์โดยทำให้คอมไพเลอร์เรียบขึ้นด้วยตนเองหรือโดยใช้มาโคร อุตสาหกรรมโน้มตัวไปทางอดีต

ในกรณีนี้ ฉันจะเน้นไปที่การใช้มาโครเพียงเพราะมันรวดเร็วและสกปรก โดยไม่ต้องเรียนรู้เพิ่มเติมมากนัก อย่างไรก็ตาม เนื่องจากมาโครถือเป็นนามธรรมทางวิศวกรรมซอฟต์แวร์ และเนื่องจากคุณเกี่ยวข้องกับโค้ดที่คอมไพเลอร์สร้างขึ้น หากปัญหามีความก้าวหน้าขึ้นเล็กน้อย ฉันจะใช้เทมเพลต C++ เนื่องจากได้รับการออกแบบมาสำหรับข้อกังวลที่คุณกำลังไตร่ตรอง

person Hassan Syed    schedule 08.03.2011

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

inline double cube(double x)
{
    return x * x * x;
}

เป็นสิ่งเดียวที่จะสร้างความแตกต่างอย่างมีนัยสำคัญให้กับประสิทธิภาพของคุณที่นี่

person fizzer    schedule 08.03.2011
comment
ด้วยคอมไพเลอร์ C++ ที่ทันสมัย ​​จะไม่ทำให้การโทรแบบ pow(x, integer) เป็นสิ่งที่คุณอธิบายง่ายขึ้น แทนที่จะใช้อันที่แพงกว่าใช่ไหม - person Tyler Shellberg; 02.11.2019

มาโคร รวมถึงมาโครที่มีลักษณะคล้ายฟังก์ชัน เป็นการแทนที่ข้อความธรรมดา และอาจกัดคุณจนแทบแย่หากคุณไม่จริงๆระมัดระวังกับพารามิเตอร์ของคุณ ตัวอย่างเช่น มาโคร SQUARE ที่ได้รับความนิยมตลอดกาล:

#define SQUARE(x) ((x)*(x))

อาจเป็นหายนะที่รอคอยที่จะเกิดขึ้นหากคุณเรียกมันว่า SQUARE(i++) นอกจากนี้ มาโครที่มีลักษณะคล้ายฟังก์ชันไม่มีแนวคิดเรื่องขอบเขต และไม่รองรับตัวแปรในเครื่อง แฮ็คที่ได้รับความนิยมมากที่สุดก็คือ

#define MACRO(S,R,E,C)                                     \
do                                                         \   
{                                                          \
  double AttractiveTerm = pow((S)/(R),3);                  \
  double RepulsiveTerm = AttractiveTerm * AttractiveTerm;  \
  (C) = 4 * (E) * (RepulsiveTerm - AttractiveTerm);        \
} while(0)

ซึ่งแน่นอนว่าทำให้ยากต่อการกำหนดผลลัพธ์เช่น x = MACRO(a,b);

ทางออกที่ดีที่สุดจากจุดยืน ความถูกต้อง และ ความสามารถในการบำรุงรักษา คือการทำให้มันกลายเป็นฟังก์ชันและระบุ inline แมโครไม่ใช่ฟังก์ชัน และไม่ควรสับสนกับฟังก์ชันเหล่านี้

เมื่อคุณดำเนินการดังกล่าวแล้ว ให้วัดประสิทธิภาพและค้นหาจุดคอขวด ที่เกิดขึ้นจริง ก่อนที่จะแฮ็กข้อมูลนั้น (การเรียกไปที่ pow จะเป็นตัวเลือกสำหรับการปรับปรุงประสิทธิภาพอย่างแน่นอน)

person John Bode    schedule 08.03.2011

โปรดตรวจสอบมาตรฐานการเข้ารหัสของ CERT Secure ที่พูดถึงมาโครและฟังก์ชันอินไลน์ในแง่ของความปลอดภัยและการกระตุ้นจุดบกพร่อง ฉันไม่สนับสนุนให้ใช้มาโครที่มีลักษณะคล้ายฟังก์ชัน เนื่องจาก: - การทำโปรไฟล์น้อยลง - ติดตามได้น้อยลง - ยากต่อการแก้ไข - อาจนำไปสู่จุดบกพร่องที่รุนแรง

person Muhammed Abdul Galeil    schedule 11.10.2012

วิธีที่ดีที่สุดในการตอบคำถามของคุณคือการเปรียบเทียบทั้งสองวิธีเพื่อดูว่าวิธีใดเร็วกว่าในแอปพลิเคชันของคุณ โดยใช้ข้อมูลการทดสอบของคุณ การคาดการณ์เกี่ยวกับประสิทธิภาพนั้นไม่น่าเชื่อถืออย่างฉาวโฉ่ยกเว้นในระดับที่หยาบที่สุด

ที่กล่าวว่าฉันคาดหวังว่าจะไม่มีความแตกต่างอย่างมีนัยสำคัญระหว่างมาโครและการเรียกใช้ฟังก์ชันแบบอินไลน์ อย่างแท้จริง ในทั้งสองกรณี คุณควรลงเอยด้วยรหัสการประกอบเดียวกันภายใต้ประทุน

person Eric Melski    schedule 08.03.2011
comment
ขออภัยที่ไม่มีประสบการณ์ของฉัน - คุณหมายถึงอะไรโดยฟังก์ชันอินไลน์อย่างแท้จริง? ฟังก์ชันทั้งหมดไม่ได้ถูกประกาศด้วยคีย์เวิร์ด inline ที่พยายามจะอินไลน์ใช่หรือไม่ - person Jason R. Mick; 08.03.2011
comment
@Jason: ไม่พวกเขาไม่ได้ การอ้างถึงมาตรฐาน: การสร้างฟังก์ชันให้เป็นฟังก์ชันแบบอินไลน์แสดงให้เห็นว่าการเรียกใช้ฟังก์ชันนั้นเร็วที่สุดเท่าที่จะเป็นไปได้ ขอบเขตที่ข้อเสนอแนะดังกล่าวมีประสิทธิผลนั้นขึ้นอยู่กับการนำไปปฏิบัติ - person Stephen Canon; 08.03.2011
comment
คำหลักแบบอินไลน์เป็นเพียงข้อเสนอแนะสำหรับคอมไพเลอร์ มันอาจจะอินไลน์หรือไม่ก็ได้ - person JayM; 08.03.2011

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

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

จากนั้นทำการสุ่มหยุดชั่วคราวอีกครั้ง ตอนนี้คุณจะเห็นสิ่งอื่นที่ต้องใช้เวลามาก ฉันไม่สามารถเดาได้ว่าจะเป็นอย่างไร และไม่มีใครสามารถคาดเดาได้ แต่คุณก็อาจจะลดสิ่งนั้นลงได้เช่นกัน แค่ทำต่อไปจนกว่าคุณจะทำไม่ได้อีกต่อไป

อาจเกิดขึ้นระหว่างทางที่คุณเลือกใช้แมโคร และอาจเร็วกว่าฟังก์ชันอินไลน์เล็กน้อย นั่นสำหรับคุณที่จะตัดสินเมื่อคุณไปถึงที่นั่น

person Mike Dunlavey    schedule 08.03.2011

อย่างที่คนอื่นบอก ส่วนใหญ่ขึ้นอยู่กับคอมไพเลอร์

ฉันพนันได้เลยว่า "pow" จะทำให้คุณเสียค่าใช้จ่ายมากกว่าอินไลน์หรือมาโครใด ๆ ที่จะช่วยคุณได้ :)

ฉันคิดว่ามันจะสะอาดกว่าถ้าเป็นฟังก์ชันแบบอินไลน์แทนที่จะเป็นมาโคร

การแคชและการวางท่อเป็นจุดที่คุณจะได้รับผลประโยชน์ที่ดีหากคุณใช้งานสิ่งนี้บนโปรเซสเซอร์ที่ทันสมัย เช่น. ลบคำสั่งแยกย่อยเช่น 'ถ้า' สร้างความแตกต่างอย่างมาก (สามารถทำได้ด้วยเทคนิคหลายประการ)

person Keith Nicholas    schedule 08.03.2011

ตามที่ฉันเข้าใจจากบางคนที่เขียนคอมไพเลอร์ เมื่อคุณเรียกใช้ฟังก์ชันจากภายในแล้ว ไม่น่าจะเป็นไปได้ที่โค้ดของคุณจะถูกแทรกในบรรทัด แต่นั่นคือเหตุผลที่คุณไม่ควรใช้มาโคร มาโครจะลบข้อมูลและปล่อยให้คอมไพเลอร์มีตัวเลือกในการปรับให้เหมาะสมน้อยลงมาก ด้วยคอมไพเลอร์แบบมัลติพาสและการปรับแต่งโปรแกรมทั้งหมด พวกเขาจะรู้ว่าการฝังโค้ดของคุณจะทำให้การคาดการณ์สาขาล้มเหลว หรือแคชพลาด หรือมนต์ดำอื่นๆ บังคับให้ CPU สมัยใหม่ต้องทำงานอย่างรวดเร็ว ฉันคิดว่าทุกคนมีสิทธิ์ที่จะชี้ให้เห็นว่าโค้ดข้างต้นนั้นไม่เหมาะสมอยู่แล้ว ดังนั้นนั่นคือสิ่งที่ควรให้ความสำคัญ

person Tavison    schedule 26.03.2011