2

在这段代码中,我连续多次访问 exitButton.rectangle,我想知道它是否经过优化,因此生成的目标代码不必每次都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++ -o0,无参考:

    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++ -o0,参考:

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
4

2 回答 2

3

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

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

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

...在内部结构上等效于:

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

在此示例中,您实际上可以在Item2和之间使用静态转换。ItemAll在任何一种情况下,如果您引用其中一个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;用作代码简化工具。它可以使您的代码更易于维护并减少您的一些输入!但它不会帮助编译器完成它的工作,所以如果这是你的目标,请不要打扰。

于 2012-12-30T02:37:46.927 回答
3

首先考虑第二个问题,类与结构没有区别(至少对于任何理智的编译器)。两者之间的唯一区别是成员的默认可访问性(类的私有,结构的公共)。

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

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

于 2012-12-30T01:44:34.610 回答