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

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

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

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

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

นอกจากนี้ ฉันค้นพบว่าหากฉันไม่ทำการเรียก std::ofstream เช่น 'open', 'ข้อยกเว้น' หรือ 'ล้าง' ที่ใดก็ได้ในเนื้อความของเมธอดของคลาสตัวอย่าง ฉันจะได้รับการปรับให้เหมาะสมตามที่คาดหวังไว้ด้วย (อย่างไรก็ตาม การออกแบบของฉันต้องการทำการเรียกดังกล่าวบน std::ofstream ดังนั้นสำหรับฉันมันเป็นจุดที่สงสัย ) โค้ดด้านล่างใช้ MACRO DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR เพื่ออนุญาตให้ลองใช้เคสนี้

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

รหัสอินพุต C ++:

#include <fstream> // ofstream

#define DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR 0

class Test_Ofstream
{
public:
    Test_Ofstream( const char a_filename[],
                   bool a_b_enabled )
    #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        : m_ofstream( a_filename ),
          m_b_enabled( a_b_enabled )
    {
    }
    #else
        : m_ofstream(),
          m_b_enabled( a_b_enabled )
    {
        m_ofstream.open( a_filename );
    }
    #endif

    void write_test()
    {
        if( m_b_enabled )
        {
            m_ofstream << "Some text.\n";
        }
    }

private:
    std::ofstream m_ofstream;
    bool m_b_enabled;
};

int main( int argc, char* argv[] )
{
    {
        Test_Ofstream test_ofstream( "test.txt", true );
        asm( "# BEGIN class enabled-test" );
        test_ofstream.write_test();
        asm( "# END class enabled-test" );
    }

    {
        Test_Ofstream test_ofstream( "test.txt", false );
        asm( "# BEGIN class disabled-test" );
        test_ofstream.write_test();
        asm( "# END class disabled-test" );
    }

    {
        bool b_enabled = true;
        #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        std::ofstream test_ofstream( "test.txt" );
        #else
        std::ofstream test_ofstream;
        test_ofstream.open( "test.txt" );
        #endif
        asm( "# BEGIN locals enabled-test" );
        if( b_enabled )
        {
            test_ofstream << "Some text.\n";
        }
        asm( "# END locals enabled-test" );
    }

    {
        bool b_enabled = false;
        #if DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR
        std::ofstream test_ofstream( "test.txt" );
        #else
        std::ofstream test_ofstream;
        test_ofstream.open( "test.txt" );
        #endif
        asm( "# BEGIN locals disabled-test" );
        if( b_enabled )
        {
            test_ofstream << "Some text.\n";
        }
        asm( "# END locals disabled-test" );
    }

    return 0;
}

รหัสแอสเซมบลีเอาต์พุต:

##### Cut here. #####
#APP
# 53 "test_ofstream_optimization.cpp" 1
        # BEGIN class disabled-test
# 0 "" 2
#NO_APP
        cmpb        $0, 596(%esp)
        je  .L22
        movl        $.LC1, 4(%esp)
        movl        %ebx, (%esp)
.LEHB9:
        call        _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
.LEHE9:
.L22:
#APP
# 55 "test_ofstream_optimization.cpp" 1
        # END class disabled-test
# 0 "" 2
#NO_APP
##### Cut here. #####
#APP
# 116 "test_ofstream_optimization.cpp" 1
        # BEGIN locals disabled-test
# 0 "" 2
# 121 "test_ofstream_optimization.cpp" 1
        # END locals disabled-test
# 0 "" 2
#NO_APP
##### Cut here. #####

ฉันรู้ว่านี่อาจเชื่อมโยงกับคอมไพเลอร์ที่ฉันใช้ซึ่งก็คือ: g++-4.6 (Debian 4.6.1-4) 4.6.1; ธงคอมไพเลอร์: -Wall -S -O2 อย่างไรก็ตาม ดูเหมือนว่าเป็นการเพิ่มประสิทธิภาพที่เรียบง่าย ฉันพบว่ามันยากที่จะเชื่อว่าคอมไพเลอร์อาจเกิดปัญหาได้

ความช่วยเหลือ ข้อมูลเชิงลึก หรือคำแนะนำใด ๆ ที่ได้รับการชื่นชมอย่างมาก


person Charles L Wilcox    schedule 17.07.2011    source แหล่งที่มา
comment
คุณสามารถให้ตัวเลือกการเพิ่มประสิทธิภาพที่คุณใช้อยู่ได้หรือไม่? ฉันไม่สามารถทำซ้ำสิ่งที่คุณค้นพบเกี่ยวกับการทดสอบผู้พิการในท้องถิ่นได้ เช่น มีการสร้างโค้ดบางส่วน   -  person Luc Danton    schedule 17.07.2011
comment
หากคุณยังไม่ได้ดำเนินการ เราขอแนะนำให้คุณแสดงสิ่งนี้ในรายชื่ออีเมล gcc-help   -  person Luc Danton    schedule 17.07.2011
comment
ลัค ฉันเพิ่มแฟล็กคอมไพเลอร์ในโพสต์ของฉันแล้ว ไม่มีอะไรพิเศษ แค่ -Wall -S -O2   -  person Charles L Wilcox    schedule 17.07.2011
comment
ลัค ฉันไม่คิดว่านี่เป็นสิ่งเฉพาะของ GCC ในทันที การเพิ่มแฟล็ก -Winline จะแสดงว่า GCC ไม่ อยู่ในแนวคอนสตรัคเตอร์ด้วยเคส #define DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR 0 น่าแปลกที่ ทำ ใน Constructor ด้วย #define DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR 1   -  person Charles L Wilcox    schedule 17.07.2011


คำตอบ (2)


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

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

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

person Puppy    schedule 17.07.2011
comment
FWIW การใช้สแน็ปช็อตของ GCC 4.7 และการตรวจสอบโค้ดที่แยกส่วนโดยไม่มีแอสเซมบลีในซอร์สจะให้ผลลัพธ์เหมือนกับที่คุณทำ: เวอร์ชันคลาสยังคงแตกสาขา - person Luc Danton; 17.07.2011
comment
@DeadMG คุณพูดถูกเกี่ยวกับการอินไลน์ ฉันเพิ่งค้นพบแฟล็ก -Winline ของ GCC และมันบอกว่าการเรียกทั้งสองไปยังคอนสตรัคเตอร์นั้นไม่ได้อินไลน์ :-( ฉันเดาว่าสัญชาตญาณของฉันสำหรับสิ่งที่ ควร สามารถอินลินได้นั้นปิดอยู่มาก เท่าที่นิพจน์ 'asm' ที่มีอิทธิพลต่อเครื่องมือเพิ่มประสิทธิภาพ ... ฉันไม่มีประสบการณ์ที่นี่ ฉันจะถือว่าอย่างไรก็ตาม เนื่องจากแอสเซมบลีที่เพิ่มเข้ามาไม่ได้อ้างอิงถึงตัวแปรใดๆ จึงควรมีผลกระทบเพียงเล็กน้อยหรือไม่มีเลย - person Charles L Wilcox; 17.07.2011

อย่างที่คุณพูดสิ่งนี้จะขึ้นอยู่กับคอมไพเลอร์ แต่ฉันเดาว่า:

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

เมื่อ DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR เป็นจริง ตัวสร้าง Test_Ofstream:

  • เรียกตัวสร้าง ofstream(const char*)
  • เริ่มต้นสมาชิก m_b_enabled

เนื่องจากไม่มีการดำเนินการใด ๆ ระหว่างการเริ่มต้น test_ofstream.m_b_enabled และการทดสอบ การเพิ่มประสิทธิภาพนี้จึงยุ่งยากกว่าเล็กน้อย แต่ดูเหมือนว่า g++ ยังคงจัดการได้

เมื่อ DISABLE_OPEN_OFSTREAM_AFTER_CONSTRUCTOR เป็นเท็จ ตัวสร้าง Test_Ofstream:

  • เรียกตัวสร้างเริ่มต้น ofstream
  • เริ่มต้นสมาชิก m_b_enabled
  • โทร m_ofstream.open(const char*)

เครื่องมือเพิ่มประสิทธิภาพไม่ได้รับอนุญาตให้ถือว่า ofstream::open จะไม่เปลี่ยนแปลง test_ofstream.m_b_enabled เรารู้ว่าไม่ควร แต่ตามทฤษฎีแล้วฟังก์ชันไลบรารีที่ไม่ใช่แบบอินไลน์สามารถค้นหาวัตถุที่สมบูรณ์ test_ofstream ซึ่งมีอาร์กิวเมนต์ 'this' ของมัน และแก้ไขด้วยวิธีนั้น

person aschepler    schedule 17.07.2011
comment
แอสเชพเลอร์ ฉันพบหลักฐานว่า GCC ไม่ได้อินไลน์ตัวสร้างอย่างที่ฉันคาดไว้อย่างไร้เดียงสา ดังนั้นคอมไพเลอร์จะไม่มาถึงจุดนี้ที่คุณคาดเดาด้วยซ้ำ ฉันไม่แน่ใจว่ากลไกที่คุณกำลังสำรวจที่นี่ควรจะเป็นไปได้หรือไม่ - person Charles L Wilcox; 17.07.2011