一行中对同一类成员的多个访问是否以某种方式进行了优化



在这段代码中,我访问了exitButton。矩形连续几次,我想知道它是否经过优化,所以产生的目标代码不必每次都向exitButton请求rectangle:

struct MenuItem {
    Rectangle rectangle;
};
MenuItem exitButton;
exitButton.rectangle.top = 383;
exitButton.rectangle.height = 178;
exitButton.rectangle.left = 0;
exitButton.rectangle.width = 1024;

我需要写这样的东西来保证它是优化的吗?

Rectangle &tempRectangle = exitButton.rectangle;
tempRectangle.top = 383;
tempRectangle.height = 178;
tempRectangle.left = 0;
tempRectangle.width = 1024;

会是相同的,但使用类而不是结构?提前谢谢。

编辑

g++ - 0,无引用:

    CPU Disasm
Address   Hex dump          Command                                  Comments
004013B0  /$  55            PUSH EBP                                 ; CppTest.004013B0(guessed void)
004013B1  |.  89E5          MOV EBP,ESP
004013B3  |.  83E4 F0       AND ESP,FFFFFFF0                         ; DQWORD (16.-byte) stack alignment
004013B6  |.  83EC 10       SUB ESP,10
004013B9  |.  E8 42060000   CALL 00401A00                            ; [CppTest.00401A00
004013BE  |.  C70424 7F0100 MOV DWORD PTR SS:[LOCAL.4],17F
004013C5  |.  C74424 04 B20 MOV DWORD PTR SS:[LOCAL.3],0B2
004013CD  |.  C74424 08 000 MOV DWORD PTR SS:[LOCAL.2],0
004013D5  |.  C74424 0C 000 MOV DWORD PTR SS:[LOCAL.1],400
004013DD  |.  B8 00000000   MOV EAX,0
004013E2  |.  C9            LEAVE
004013E3  .  C3            RETN

g++ - 0,参考:

CPU Disasm
Address   Hex dump          Command                                  Comments
004013B0  /$  55            PUSH EBP                                 ; CppTest.004013B0(guessed void)
004013B1  |.  89E5          MOV EBP,ESP
004013B3  |.  83E4 F0       AND ESP,FFFFFFF0                         ; DQWORD (16.-byte) stack alignment
004013B6  |.  83EC 20       SUB ESP,20
004013B9  |.  E8 62060000   CALL 00401A20                            ; [CppTest.00401A20
004013BE  |.  8D4424 0C     LEA EAX,[LOCAL.5]
004013C2  |.  894424 1C     MOV DWORD PTR SS:[LOCAL.1],EAX
004013C6  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[LOCAL.1]
004013CA  |.  C700 7F010000 MOV DWORD PTR DS:[EAX],17F
004013D0  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[LOCAL.1]
004013D4  |.  C740 04 B2000 MOV DWORD PTR DS:[EAX+4],0B2
004013DB  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[LOCAL.1]
004013DF  |.  C740 08 00000 MOV DWORD PTR DS:[EAX+8],0
004013E6  |.  8B4424 1C     MOV EAX,DWORD PTR SS:[LOCAL.1]
004013EA  |.  C740 0C 00040 MOV DWORD PTR DS:[EAX+0C],400
004013F1  |.  B8 00000000   MOV EAX,0
004013F6  |.  C9            LEAVE
004013F7  .  C3            RETN

在这种特殊情况下,编译器实际上没有进行任何优化。事实上,在第二种情况下,编译器实际上可能不得不更加努力地工作,以产生同样高效的代码;因为它必须解析引用别名。

原因是Rectangle不是指针,而是直接嵌入到MenuItem中。在这种情况下,编译器实际上将整个结构树视为一组平坦的变量。编译器根据结构体开头的字节偏移量来考虑事情。例子:

struct Item1 {
    int i1, i2, i3;
};
struct Item2 {
    Item1  item1;
    int    t1, t2;
};

…在内部结构上等价于:

struct ItemAll {
    int i1, i2, i3;
    int t1, t2;
};

在本例中,您实际上可以在Item2ItemAll之间使用静态强制转换。在这两种情况下,如果引用ItemAll.i2Item2::Item1.i2,编译器在内部将其视为variable_base_address + sizeof(int)同样适用于类和结构。

你需要关心的是,当你使用-> operator,例如,如果你的结构是这样设计的:

struct MenuItem {
    Rectangle* rectangle;
};

在这种情况下,编译器必须执行额外的解引用步骤来访问Rectangle的内容。启用了优化后,任何现代编译器都会尽其所能优化解引用。这不是问题。如果你有很多交错的解引用,超过了cpu上可用的寄存器,就会出现问题:

struct MenuItem {
    Rectangle* rect1;
    Rectangle* rect2;
    Point*     point1;
    Point*     point2;
};
menuItem.rect1->top = 50;
menuItem.rect2->top = 50;
menuItem.point1->x  = 33;
menuItem.point2->x  = 45;
menuItem.rect1->bottom = 450;
menuItem.rect2->bottom = 450;
// etc...

在上面的情况下,编译器可能会用完适合用作基寄存器的寄存器,迫使它为以后的使用冗余地重新计算解引用(在这个特定的例子中,它不会用完寄存器,因为这些都是赋值的立即数,但如果涉及任何变量算术,则可能性会高得多)。很明显,在这种情况下重新排序对于你来说也是微不足道的这样所有的rect1赋值都是成对的,等等。不过,如果我们做的是比简单赋值更复杂的事情,那么这也可能是不可能的。

然而,在这种情况下,即使使用引用也无济于事。无论如何,编译器都必须以相同的方式溢出并重新加载指针解引用。

结论:可以随意使用Rectangle& var = menuItem.var;作为代码简化工具。它可以使您的代码更容易维护,并减少一些类型输入!但是它不会帮助编译器完成它的工作,所以如果这是你的目标,不要麻烦。

首先考虑第二个问题,类与结构体没有区别(至少在任何相同的编译器中)。两者之间的唯一区别是成员的默认可访问性(类为private,结构为public)。

关于确定关于优化的唯一方法是查看目标代码,但我期望任何合理的编译器确定您正在使用相同的引用系列来重复访问exitButton.rectangle,并自动使用寄存器来引用连续访问。

如果您关闭所有优化,情况可能不是这样,但基本上任何优化都允许,您可以期待它-这种优化已经众所周知多年了(它基本上是一个常见的子表达式消除)。

最新更新