สุจริตมันเป็นเรื่องไม่สำคัญที่จะเขียนโปรแกรมเพื่อเปรียบเทียบประสิทธิภาพ:
#include <ctime>
#include <iostream>
namespace {
class empty { }; // even empty classes take up 1 byte of space, minimum
}
int main()
{
std::clock_t start = std::clock();
for (int i = 0; i < 100000; ++i)
empty e;
std::clock_t duration = std::clock() - start;
std::cout << "stack allocation took " << duration << " clock ticks\n";
start = std::clock();
for (int i = 0; i < 100000; ++i) {
empty* e = new empty;
delete e;
};
duration = std::clock() - start;
std::cout << "heap allocation took " << duration << " clock ticks\n";
}
มันบอกว่าสอดคล้องโง่หลอกเด็กของจิตใจของเล็ก ๆ น้อย ๆ เห็นได้ชัดว่าการเพิ่มประสิทธิภาพคอมไพเลอร์เป็น hobgoblins ในใจของโปรแกรมเมอร์หลายคน การสนทนานี้มักจะอยู่ที่ด้านล่างของคำตอบ แต่คนดูเหมือนจะไม่ใส่ใจที่จะอ่านจนถึงตอนนี้ดังนั้นฉันจึงย้ายมาที่นี่เพื่อหลีกเลี่ยงคำถามที่ฉันตอบไปแล้ว
คอมไพเลอร์ปรับให้เหมาะสมอาจสังเกตว่ารหัสนี้ไม่ทำอะไรเลยและอาจปรับให้เหมาะสมทั้งหมด มันเป็นหน้าที่ของเครื่องมือเพิ่มประสิทธิภาพในการทำสิ่งเช่นนั้นและการต่อสู้กับเครื่องมือเพิ่มประสิทธิภาพนั้นเป็นการทำธุระของคนโง่
ฉันขอแนะนำให้รวบรวมรหัสนี้ด้วยการปิดการเพิ่มประสิทธิภาพเนื่องจากไม่มีวิธีที่ดีในการหลอกเครื่องมือเพิ่มประสิทธิภาพที่ใช้งานอยู่ในปัจจุบันหรือจะใช้ในอนาคต
ใครก็ตามที่เปิดใช้งานเครื่องมือเพิ่มประสิทธิภาพแล้วบ่นเกี่ยวกับการต่อสู้มันควรจะถูกเยาะเย้ยสาธารณะ
ถ้าฉันสนใจความแม่นยำระดับนาโนวินาทีฉันก็จะไม่ใช้ std::clock()
ฉันจะไม่ใช้ ถ้าฉันต้องการเผยแพร่ผลลัพธ์เป็นวิทยานิพนธ์ระดับปริญญาเอกฉันจะทำเรื่องใหญ่กว่านี้และฉันอาจเปรียบเทียบ GCC, Tendra / Ten15, LLVM, Watcom, Borland, Visual C ++, Digital Mars, ICC และคอมไพเลอร์อื่น ๆ เนื่องจากการจัดสรรฮีปใช้เวลานานกว่าการจัดสรรสแต็กนับร้อยครั้งและฉันไม่เห็นว่ามีประโยชน์ใด ๆ เกี่ยวกับการตรวจสอบคำถามอีกต่อไป
เครื่องมือเพิ่มประสิทธิภาพมีภารกิจในการกำจัดรหัสที่ฉันกำลังทดสอบ ฉันไม่เห็นเหตุผลใด ๆ ที่จะบอกให้เครื่องมือเพิ่มประสิทธิภาพทำงานและลองหลอกเครื่องมือเพิ่มประสิทธิภาพให้เป็นไปไม่ได้เพิ่มประสิทธิภาพจริง ๆ แต่ถ้าฉันเห็นคุณค่าในการทำเช่นนั้นฉันจะทำอย่างน้อยหนึ่งอย่างต่อไปนี้:
เพิ่มสมาชิกข้อมูลไปยังempty
และเข้าถึงสมาชิกข้อมูลนั้นในลูป แต่ถ้าฉันเคยอ่านจากสมาชิกข้อมูลเครื่องมือเพิ่มประสิทธิภาพสามารถทำการพับแบบคงที่และเอาลูปออก ถ้าฉันเพิ่งเขียนไปยังสมาชิกของข้อมูลเครื่องมือเพิ่มประสิทธิภาพอาจข้ามทั้งหมดยกเว้นการวนซ้ำครั้งสุดท้ายของลูป นอกจากนี้คำถามไม่ใช่ "การจัดสรรสแต็กและการเข้าถึงข้อมูลกับการจัดสรรฮีปและการเข้าถึงข้อมูล"
ประกาศe
volatile
, แต่volatile
มักจะถูกรวบรวมอย่างไม่ถูกต้อง (PDF)
นำที่อยู่e
ภายในวง (และอาจกำหนดให้กับตัวแปรที่มีการประกาศextern
และกำหนดไว้ในไฟล์อื่น) แต่ถึงแม้ในกรณีนี้คอมไพเลอร์อาจสังเกตเห็นว่า - บนสแต็กอย่างน้อย - e
จะถูกจัดสรรที่ที่อยู่หน่วยความจำเดียวกันเสมอ ฉันได้รับการวนซ้ำทั้งหมด แต่ไม่มีการจัดสรรวัตถุจริงๆ
นอกเหนือจากที่เห็นได้ชัดการทดสอบนี้มีข้อบกพร่องที่จะวัดการจัดสรรและการจัดสรรคืนและคำถามเดิมไม่ได้ถามเกี่ยวกับการจัดสรรคืน แน่นอนว่าตัวแปรที่จัดสรรไว้ในสแต็กจะถูกจัดสรรคืนอัตโนมัติเมื่อสิ้นสุดขอบเขตดังนั้นจึงไม่เรียกdelete
(1) เอียงตัวเลข (การจัดสรรคืนสแต็กจะรวมอยู่ในจำนวนที่เกี่ยวกับการจัดสรรสแต็ก 2) ทำให้หน่วยความจำรั่วไหลไม่ดีเว้นแต่เราจะทำการอ้างอิงไปยังตัวชี้และการโทรใหม่delete
หลังจากเราได้ทำการวัดเวลาของเราแล้ว
บนเครื่องของฉันโดยใช้ g ++ 3.4.4 บน Windows ฉันได้รับ "0 นาฬิกา ticks" สำหรับทั้งการจัดสรรสแต็คและฮีปสำหรับการจัดสรรน้อยกว่า 100,000 การจัดสรรและจากนั้นฉันได้รับ "0 นาฬิกาเห็บ" สำหรับการจัดสรรสแต็ค "สำหรับการจัดสรรฮีป เมื่อฉันวัดการจัดสรร 10,000,000 ครั้งการจัดสรรสแต็กจะใช้เวลา 31 นาฬิกาและการจัดสรรฮีปใช้เวลา 1562 นาฬิกา
ใช่คอมไพเลอร์ปรับให้เหมาะสมอาจช่วยสร้างวัตถุว่างได้ หากฉันเข้าใจอย่างถูกต้องมันอาจกำจัดลูปแรกทั้งหมด เมื่อฉันเพิ่มค่าการจัดสรรซ้ำไปเป็น 10,000,000 การจัดสรรสแต็กใช้เวลา 31 นาฬิกาและการจัดสรรฮีปใช้เวลา 1562 นาฬิกา ฉันคิดว่ามันปลอดภัยที่จะบอกว่าโดยไม่บอก g ++ เพื่อให้สามารถใช้งานได้ดีที่สุด g ++ ไม่ได้ช่วยสร้างคอนสตรัคชัน
ในปีที่ผ่านมาตั้งแต่ฉันเขียนสิ่งนี้การตั้งค่าใน Stack Overflow เป็นการโพสต์ประสิทธิภาพจากการสร้างที่ปรับให้เหมาะสม โดยทั่วไปแล้วฉันคิดว่ามันถูกต้อง อย่างไรก็ตามฉันคิดว่ามันเป็นเรื่องโง่ที่จะขอให้คอมไพเลอร์ทำการปรับรหัสให้เหมาะสมเมื่อคุณไม่ต้องการใช้รหัสนั้น มันทำให้ฉันรู้สึกคล้ายกับการจ่ายเงินพิเศษสำหรับบริการจอดรถ แต่ปฏิเสธที่จะมอบกุญแจ ในกรณีนี้โดยเฉพาะฉันไม่ต้องการให้เครื่องมือเพิ่มประสิทธิภาพทำงาน
การใช้เบนช์มาร์กรุ่นที่แก้ไขเล็กน้อย (เพื่อระบุจุดที่ถูกต้องที่โปรแกรมดั้งเดิมไม่ได้จัดสรรบางอย่างบนสแต็คแต่ละครั้งผ่านลูป) และการคอมไพล์โดยไม่มีการเพิ่มประสิทธิภาพ แต่การเชื่อมโยงไปยังรีลีสไลบรารี ไม่ต้องการรวมการชะลอตัวใด ๆ ที่เกิดจากการลิงก์ไปยังไลบรารีการดีบัก):
#include <cstdio>
#include <chrono>
namespace {
void on_stack()
{
int i;
}
void on_heap()
{
int* i = new int;
delete i;
}
}
int main()
{
auto begin = std::chrono::system_clock::now();
for (int i = 0; i < 1000000000; ++i)
on_stack();
auto end = std::chrono::system_clock::now();
std::printf("on_stack took %f seconds\n", std::chrono::duration<double>(end - begin).count());
begin = std::chrono::system_clock::now();
for (int i = 0; i < 1000000000; ++i)
on_heap();
end = std::chrono::system_clock::now();
std::printf("on_heap took %f seconds\n", std::chrono::duration<double>(end - begin).count());
return 0;
}
แสดง:
on_stack took 2.070003 seconds
on_heap took 57.980081 seconds
บนระบบของฉันเมื่อคอมไพล์ด้วยบรรทัดคำสั่ง cl foo.cc /Od /MT /EHsc
ในระบบของฉันเมื่อรวบรวมกับบรรทัดคำสั่ง
คุณอาจไม่เห็นด้วยกับวิธีการของฉันในการสร้างงานที่ไม่เหมาะ ไม่เป็นไร: อย่าลังเลที่จะปรับเปลี่ยนมาตรฐานมากเท่าที่คุณต้องการ เมื่อฉันเปิดการปรับให้เหมาะสมฉันจะได้รับ:
on_stack took 0.000000 seconds
on_heap took 51.608723 seconds
ไม่ใช่เพราะการจัดสรรสแต็กนั้นเกิดขึ้นทันที แต่เนื่องจากคอมไพเลอร์ที่มีค่าครึ่งเดียวสามารถสังเกตเห็นว่าon_stack
ไม่มีประโยชน์ใด ๆ และสามารถปรับให้เหมาะสม GCC บนแล็ปท็อป Linux ของฉันยังสังเกตเห็นว่าon_heap
ไม่มีประโยชน์อะไรเลยและปรับให้เหมาะสมเช่นกัน:
on_stack took 0.000003 seconds
on_heap took 0.000002 seconds