ตัวแปรสมาชิกที่ไม่ได้ใช้ใช้หน่วยความจำหรือไม่?


92

การเตรียมใช้งานตัวแปรสมาชิกและไม่อ้างถึง / ใช้งานจะใช้ RAM เพิ่มเติมในระหว่างรันไทม์หรือคอมไพเลอร์ไม่สนใจตัวแปรนั้น

struct Foo {
    int var1;
    int var2;

    Foo() { var1 = 5; std::cout << var1; }
};

ในตัวอย่างข้างต้นสมาชิก 'var1' จะได้รับค่าที่แสดงในคอนโซล อย่างไรก็ตาม 'Var2' ไม่ได้ใช้เลย ดังนั้นการเขียนลงในหน่วยความจำระหว่างรันไทม์จะเป็นการสิ้นเปลืองทรัพยากร คอมไพลเลอร์นำสถานการณ์เหล่านี้มาใช้ในบัญชีและไม่สนใจตัวแปรที่ไม่ได้ใช้หรืออ็อบเจ็กต์ Foo มีขนาดเท่ากันเสมอไม่ว่าจะใช้สมาชิกหรือไม่


25
ขึ้นอยู่กับคอมไพเลอร์สถาปัตยกรรมระบบปฏิบัติการและการปรับให้เหมาะสมที่ใช้
นกฮูก

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

2
@ แอนดี้พวกเขาไม่ได้ทำอะไรเลยเนื่องจากมีการประเมินที่อยู่ของสมาชิกข้อมูลต่อไปนี้ ซึ่งหมายความว่าการมีอยู่ของสมาชิก padding เหล่านั้นมีพฤติกรรมที่สังเกตได้ในโปรแกรม ที่นี่var2ไม่
YSC

4
ฉันจะแปลกใจถ้าคอมไพเลอร์สามารถปรับให้เหมาะสมได้เนื่องจากหน่วยคอมไพเลอร์ใด ๆ ที่กำหนดแอดเดรสโครงสร้างดังกล่าวอาจเชื่อมโยงกับหน่วยคอมไพล์อื่นโดยใช้โครงสร้างเดียวกันและคอมไพเลอร์ไม่สามารถรู้ได้ว่ายูนิตคอมไพเลอร์แยกนั้นอยู่ที่สมาชิกหรือไม่
Galik

2
@geza sizeof(Foo)ไม่สามารถลดลงตามคำจำกัดความ - หากคุณพิมพ์sizeof(Foo)จะต้องให้ผล8(บนแพลตฟอร์มทั่วไป) คอมไพเลอร์สามารถเพิ่มประสิทธิภาพพื้นที่ที่ใช้โดยvar2(ไม่ว่าจะผ่านnewหรือบนสแต็กหรือในการเรียกใช้ฟังก์ชัน ... ) ในบริบทใดก็ตามที่พวกเขาพบว่ามันสมเหตุสมผลแม้ว่าจะไม่มี LTO หรือการเพิ่มประสิทธิภาพโปรแกรมทั้งหมดก็ตาม ในกรณีที่เป็นไปไม่ได้พวกเขาจะไม่ทำเช่นเดียวกับการเพิ่มประสิทธิภาพอื่น ๆ ฉันเชื่อว่าการแก้ไขคำตอบที่ยอมรับทำให้มีโอกาสน้อยที่จะเข้าใจผิด
Max Langhof

คำตอบ:


107

ซีสีทอง ++ "ตามที่ถ้า" กฎ1กล่าวว่าถ้าติดตามพฤติกรรมของโปรแกรมที่ไม่ได้ขึ้นอยู่กับการดำรงอยู่ของข้อมูลสมาชิกที่ไม่ได้ใช้คอมไพเลอร์ที่ได้รับอนุญาตในการเพิ่มประสิทธิภาพมันออกไป

ตัวแปรสมาชิกที่ไม่ได้ใช้ใช้หน่วยความจำหรือไม่?

ไม่ (หากไม่ได้ใช้งาน "จริงๆ")


มีคำถามสองข้อในใจ:

  1. พฤติกรรมที่สังเกตได้จะไม่ขึ้นอยู่กับการดำรงอยู่ของสมาชิกเมื่อใด
  2. สถานการณ์แบบนั้นเกิดขึ้นในโปรแกรมชีวิตจริงหรือไม่?

เริ่มจากตัวอย่าง

ตัวอย่าง

#include <iostream>

struct Foo1
{ int var1 = 5;           Foo1() { std::cout << var1; } };

struct Foo2
{ int var1 = 5; int var2; Foo2() { std::cout << var1; } };

void f1() { (void) Foo1{}; }
void f2() { (void) Foo2{}; }

หากเราขอให้gcc รวบรวมหน่วยการแปลนี้ผลลัพธ์จะออกมา:

f1():
        mov     esi, 5
        mov     edi, OFFSET FLAT:_ZSt4cout
        jmp     std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
f2():
        jmp     f1()

f2เป็นเช่นเดียวกับและหน่วยความจำที่ไม่เคยใช้ในการเก็บจริงf1 Foo2::var2( เสียงดังก็ทำคล้าย ๆ กัน )

อภิปรายผล

บางคนอาจบอกว่าสิ่งนี้แตกต่างกันด้วยเหตุผลสองประการ:

  1. นี่เป็นตัวอย่างที่ไม่สำคัญเกินไป
  2. โครงสร้างได้รับการปรับให้เหมาะสมทั้งหมดจะไม่นับ

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

bool insert(std::set<int>& set, int value)
{
    return set.insert(value).second;
}

นี่คือตัวอย่างที่แท้จริงของข้อมูลสมาชิก (ที่นี่std::pair<std::set<int>::iterator, bool>::first) ที่ไม่ได้ใช้งาน เดาอะไร? ได้รับการปรับให้เหมาะสมที่สุด ( ตัวอย่างที่ง่ายกว่าด้วยชุดดัมมี่หากการประกอบนั้นทำให้คุณร้องไห้)

ตอนนี้น่าจะเป็นเวลาที่เหมาะสมที่สุดในการอ่านคำตอบที่ยอดเยี่ยมของ Max Langhof ( โปรดโหวตให้ฉันด้วย) มันอธิบายว่าทำไมในท้ายที่สุดแนวคิดของโครงสร้างจึงไม่สมเหตุสมผลในระดับการประกอบที่คอมไพเลอร์เอาท์พุต

"แต่ถ้าฉันทำ X ความจริงที่ว่าสมาชิกที่ไม่ได้ใช้นั้นได้รับการปรับให้เหมาะสมก็เป็นปัญหา!"

มีความคิดเห็นจำนวนมากที่โต้แย้งว่าคำตอบนี้ต้องผิดเพราะการดำเนินการบางอย่าง (เช่นassert(sizeof(Foo2) == 2*sizeof(int))) จะทำลายบางสิ่ง

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

การดำเนินการที่มีผลต่อพฤติกรรมที่สังเกตได้ ได้แก่ แต่ไม่ จำกัด เพียง:

  • การกำหนดขนาดของวัตถุประเภทหนึ่ง ( sizeof(Foo))
  • รับที่อยู่ของสมาชิกข้อมูลที่ประกาศตามหลัง "ไม่ได้ใช้"
  • คัดลอกวัตถุที่มีฟังก์ชั่นเช่นmemcpy,
  • การจัดการการเป็นตัวแทนของวัตถุ (เช่นเดียวกับmemcmp)
  • คุณสมบัติของวัตถุที่ระเหยได้
  • ฯลฯ

1)

[intro.abstract]/1

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

2)เช่นเดียวกับการยืนยันว่าผ่านหรือล้มเหลวคือ


ความคิดเห็นแนะนำการปรับปรุงคำตอบที่ได้รับการเก็บไว้ในการแชท
โคดี้เกรย์

1
แม้แต่assert(sizeof(…)…)คอมไพเลอร์ก็ไม่ได้ จำกัด คอมไพเลอร์ แต่ก็ต้องจัดเตรียมsizeofโค้ดที่อนุญาตให้ใช้สิ่งต่างๆเช่นmemcpyทำงานได้ แต่นั่นไม่ได้หมายความว่าคอมไพเลอร์จะต้องใช้ไบต์จำนวนมากนั้นเว้นแต่ว่าพวกเขาอาจสัมผัสกับสิ่งmemcpyที่สามารถทำได้ ไม่เขียนซ้ำเพื่อให้ได้ค่าที่ถูกต้องอยู่ดี
Davis Herring

@ เดวิสอย่างแน่นอน.
YSC

64

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

ตกลงมันยังเขียนส่วนข้อมูลคงที่และอื่น ๆ

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


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

ในขณะที่คุณทำให้การโต้ตอบของฟังก์ชันกับโลกภายนอกซับซ้อนขึ้น / ไม่ชัดเจนกับคอมไพเลอร์ (รับ / ส่งคืนโครงสร้างข้อมูลที่ซับซ้อนมากขึ้นเช่นกstd::vector<Foo>ซ่อนนิยามของฟังก์ชันในหน่วยคอมไพล์ที่แตกต่างกันห้าม / ปิดการใช้งานอินไลน์เป็นต้น) มีแนวโน้มมากขึ้นเรื่อย ๆ ที่คอมไพลเลอร์ไม่สามารถพิสูจน์ได้ว่าสมาชิกที่ไม่ได้ใช้นั้นไม่มีผล

ไม่มีกฎที่ยากที่นี่เพราะทุกอย่างขึ้นอยู่กับการปรับให้เหมาะสมที่คอมไพเลอร์ทำ แต่ตราบใดที่คุณทำสิ่งเล็กน้อย (เช่นที่แสดงในคำตอบของ YSC) เป็นไปได้มากว่าจะไม่มีค่าใช้จ่ายในขณะที่ทำสิ่งที่ซับซ้อน (เช่นการส่งคืนstd::vector<Foo>จากฟังก์ชั่นขนาดใหญ่เกินไปสำหรับ inlining) อาจจะต้องเสียค่าใช้จ่าย


เพื่อแสดงประเด็นให้พิจารณาตัวอย่างนี้ :

struct Foo {
    int var1 = 3;
    int var2 = 4;
    int var3 = 5;
};

int test()
{
    Foo foo;
    std::array<char, sizeof(Foo)> arr;
    std::memcpy(&arr, &foo, sizeof(Foo));
    return arr[0] + arr[4];
}

เราทำสิ่งที่ไม่สำคัญที่นี่ (รับที่อยู่ตรวจสอบและเพิ่มไบต์จากการแทนค่าไบต์ ) แต่เครื่องมือเพิ่มประสิทธิภาพสามารถคิดได้ว่าผลลัพธ์จะเหมือนกันบนแพลตฟอร์มนี้เสมอ:

test(): # @test()
  mov eax, 7
  ret

สมาชิกไม่เพียง แต่ไม่Fooได้ครอบครองความทรงจำใด ๆ แต่Fooยังไม่เกิดขึ้นด้วยซ้ำ! หากมีการใช้งานอื่น ๆ ที่ไม่สามารถปรับให้เหมาะสมได้เช่นsizeof(Foo)อาจมีความสำคัญ - แต่สำหรับโค้ดส่วนนั้นเท่านั้น! หากสามารถปรับการใช้งานทั้งหมดได้เช่นนี้การมีอยู่ของเช่นvar3จะไม่มีผลต่อรหัสที่สร้างขึ้น แต่ถึงแม้ว่าจะใช้ที่อื่นtest()ก็ยังคงได้รับการปรับให้เหมาะสม!

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


6
Mic drop "ดูรายละเอียดเพิ่มเติมจากคู่มือคอมไพเลอร์ของคุณ" : D
YSC

22

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

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


1
แต่มันก็เป็นเช่นนั้น: godbolt.org/z/UJKguS + ไม่มีคอมไพเลอร์ใดที่จะเตือนสำหรับสมาชิกข้อมูลที่ไม่ได้ใช้
YSC

@YSC clang ++ เตือนเกี่ยวกับสมาชิกข้อมูลและตัวแปรที่ไม่ได้ใช้
Maxim Egorushkin

3
@YSC ฉันคิดว่ามันเป็นสถานการณ์ที่แตกต่างกันเล็กน้อยมันปรับโครงสร้างให้เหมาะสมที่สุดและพิมพ์ 5 โดยตรง
Alan Birtles

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

2
@YSC ในรหัสจริงที่มีการใช้โครงสร้างจริงแทนที่จะสร้างขึ้นเพื่อผลข้างเคียงซึ่งอาจเป็นไปได้ยากที่จะได้รับการปรับให้เหมาะสม
Alan Birtles

7

โดยทั่วไปคุณต้องสมมติว่าคุณได้รับสิ่งที่คุณขอตัวอย่างเช่นตัวแปรสมาชิก "ไม่ได้ใช้" จะอยู่ที่นั่น

เนื่องจากในตัวอย่างของคุณมีสมาชิกทั้งสองpublicคอมไพเลอร์ไม่สามารถทราบได้ว่าโค้ดบางตัว (โดยเฉพาะจากหน่วยการแปลอื่น = ไฟล์ * .cpp อื่น ๆ ซึ่งรวบรวมแยกจากกันแล้วเชื่อมโยงกัน) จะเข้าถึงสมาชิกที่ "ไม่ได้ใช้"

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

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

และตราบใดที่คุณอยู่ในขอบเขตของภาษาคุณจะไม่สามารถสังเกตได้ว่ามีการกำจัดใด ๆ เกิดขึ้น ถ้าคุณเรียกคุณจะได้รับsizeof(Foo) 2*sizeof(int)หากคุณสร้างอาร์เรย์ของFoos ระยะห่างระหว่างจุดเริ่มต้นของวัตถุสองชิ้นที่ต่อเนื่องกันFooจะเป็นsizeof(Foo)ไบต์เสมอ

ประเภทของคุณเป็นประเภทโครงร่างมาตรฐานซึ่งหมายความว่าคุณยังสามารถเข้าถึงสมาชิกโดยอิงตามค่าชดเชยที่คำนวณตามเวลาคอมไพล์ (เทียบกับoffsetofมาโคร) นอกจากนี้คุณสามารถตรวจสอบการเป็นตัวแทนไบต์โดยไบต์ของวัตถุด้วยการคัดลอกลงบนอาร์เรย์ของใช้char std::memcpyในทุกกรณีเหล่านี้สมาชิกคนที่สองสามารถสังเกตได้ว่าอยู่ที่นั่น


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
โคดี้เกรย์

2
+1: เฉพาะการเพิ่มประสิทธิภาพทั้งโปรแกรมเชิงรุกเท่านั้นที่สามารถปรับโครงร่างข้อมูลได้ (รวมถึงขนาดเวลาคอมไพล์และออฟเซ็ต) สำหรับกรณีที่อ็อบเจ็กต์โครงสร้างโลคัลไม่ได้รับการปรับให้เหมาะสมทั้งหมด gcc -fwhole-program -O3 *.cในทางทฤษฎีสามารถทำได้ แต่ในทางปฏิบัติอาจไม่เป็นเช่นนั้น (เช่นในกรณีที่โปรแกรมตั้งสมมติฐานเกี่ยวกับค่าที่แน่นอนที่sizeof()มีต่อเป้าหมายนี้และเนื่องจากเป็นการเพิ่มประสิทธิภาพที่ซับซ้อนมากที่โปรแกรมเมอร์ควรทำด้วยมือหากต้องการ)
Peter Cordes

6

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

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

สำหรับภาษาที่มีการจัดการเช่น C # / Java ที่มีคอมไพเลอร์ JIT คอมไพลเลอร์อาจสามารถลบออกได้อย่างปลอดภัยvar2เนื่องจากสามารถติดตามได้อย่างแม่นยำว่ามีการใช้งานอยู่หรือไม่และจะหลบหนีไปยังโค้ดที่ไม่มีการจัดการหรือไม่ ขนาดทางกายภาพของโครงสร้างในภาษาที่มีการจัดการอาจแตกต่างจากขนาดที่รายงานไปยังโปรแกรมเมอร์

คอมไพเลอร์ C / C ++ ประจำปี 2019 ไม่สามารถกำจัดvar2ออกจากโครงสร้างได้เว้นแต่ว่าตัวแปรโครงสร้างทั้งหมดจะถูกกำจัดออกไป สำหรับกรณีที่น่าสนใจของการกำจัดvar2ออกจากโครงสร้างคำตอบคือ: ไม่

คอมไพเลอร์ C / C ++ ในอนาคตบางตัวจะสามารถหลุดvar2ออกจากโครงสร้างได้และระบบนิเวศที่สร้างขึ้นรอบ ๆ คอมไพเลอร์จะต้องปรับให้เข้ากับข้อมูลการแยกกระบวนการที่สร้างโดยคอมไพเลอร์


1
ย่อหน้าของคุณเกี่ยวกับข้อมูลการดีบักจะสรุปว่า "เราไม่สามารถปรับให้เหมาะสมได้หากจะทำให้การดีบักยากขึ้น" ซึ่งเป็นเรื่องที่ไม่ถูกต้อง หรือฉันกำลังอ่านผิด คุณช่วยชี้แจงได้ไหม
Max Langhof

หากคอมไพลเลอร์ปล่อยข้อมูลการดีบักเกี่ยวกับโครงสร้างก็จะไม่สามารถขยาย var2 ได้ ตัวเลือก ได้แก่ (1) อย่าปล่อยข้อมูลการดีบักหากไม่สอดคล้องกับการแสดงทางกายภาพของโครงสร้าง (2) สนับสนุนการแยกสมาชิกของโครงสร้างในข้อมูลการดีบักและปล่อยข้อมูลการดีบัก
atomsymbol

บางทีอาจจะเป็นการกล่าวถึงการเปลี่ยนสเกลาร์ของมวลรวม (และจากนั้นการกำจัดร้านค้าที่ตายแล้วเป็นต้น )
Davis Herring

4

ขึ้นอยู่กับคอมไพเลอร์ของคุณและระดับการเพิ่มประสิทธิภาพ

ใน gcc หากคุณระบุ-Oจะเปิดแฟล็กการปรับให้เหมาะสมดังต่อไปนี้ :

-fauto-inc-dec 
-fbranch-count-reg 
-fcombine-stack-adjustments 
-fcompare-elim 
-fcprop-registers 
-fdce
-fdefer-pop
...

-fdceย่อมาจากตายรหัสกำจัด

คุณสามารถใช้__attribute__((used))เพื่อป้องกันไม่ให้ gcc กำจัดตัวแปรที่ไม่ได้ใช้ด้วยหน่วยเก็บแบบคงที่:

แอ็ตทริบิวต์นี้แนบกับตัวแปรที่มีการจัดเก็บแบบคงที่หมายความว่าตัวแปรจะต้องถูกปล่อยออกมาแม้ว่าจะดูเหมือนว่าตัวแปรนั้นไม่มีการอ้างอิงก็ตาม

เมื่อนำไปใช้กับสมาชิกข้อมูลแบบคงที่ของเทมเพลตคลาส C ++ แอตทริบิวต์ยังหมายความว่าสมาชิกนั้นถูกสร้างอินสแตนซ์หากคลาสนั้นถูกสร้างอินสแตนซ์


สำหรับสมาชิกข้อมูลแบบคงที่ไม่ใช่สมาชิกที่ไม่ได้ใช้ต่ออินสแตนซ์ (ซึ่งจะไม่ได้รับการปรับให้เหมาะสมเว้นแต่ว่าวัตถุทั้งหมดจะทำ) แต่ใช่ฉันเดาว่าจะนับ BTW การกำจัดตัวแปรคงที่ที่ไม่ได้ใช้ไม่ใช่การกำจัดโค้ดที่ตายแล้วเว้นแต่ GCC จะโค้งงอคำศัพท์
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.