如何防止声明为局部变量的对象在堆栈中分配?
Posted
技术标签:
【中文标题】如何防止声明为局部变量的对象在堆栈中分配?【英文标题】:How to prevent objects declared as local variables from being allocated in the stack? 【发布时间】:2016-11-12 17:21:55 【问题描述】:我在使用 ARM/Keil 编译器为小型 ARM 处理器编译 C++ 时遇到以下性能问题。
在执行某些处理的函数中,我的代码具有以下结构:
MyClass temp = global_variable_input;
Operation 1 on temp;
Operation 2 on temp;
...
Operation N on temp;
global_variable_output = temp;
MyClass 用于对数学对象进行建模,唯一成员是 32 位整数(即对象的完整大小为 4 个字节)。
所有操作都涉及使用重载运算符或 MyClass 的方法,并因此更改 'temp' 的值。有些操作是微不足道的内联的(方法在类中声明为内联),而有些则比较复杂,需要生成对方法的调用。
查看编译器为我的例程生成的汇编代码,我注意到编译器在堆栈中为“temp”分配空间,并且每个操作(也是内联操作!)都将操作的结果存储在放入堆栈,然后继续使用上次操作中存储在寄存器中的值。对于非内联对象,编译器传递一个指向寄存器 r1 中对象 (this) 的指针,以及一个指向在堆栈中创建的另一个对象的指针,以将结果存储在寄存器 r0 中。
代码实现了一种信号处理算法,您可以将其想象为对 temp 的一系列算术运算,因此在每次操作之后都有这个附加的“存储”指令以及相应的内存访问(可能只是一个操作码)在实现中引入了巨大的性能损失。
理想情况下,我希望编译器仅使用一个寄存器来完成操作,而不是保留每次操作后都需要更新的“temp”的堆叠版本。
另一个希望是将对象的当前值传递给方法,只需使用寄存器(就像 ARM C 调用约定为普通 C 函数指定的那样)并以相同的方式获取结果,而不是使用指针到内存位置。
我要求太多了吗?我怎样才能让我的 ARM/Keil 编译器以这种方式工作?
PS:这个函数很简单,所以编译器不需要在堆栈中分配我的变量,因为它用完了寄存器。我怀疑这样做的原因是它觉得需要一个指针来传递给非内联方法,然后认为有必要使堆栈中的值始终保持最新。
非常感谢!
【问题讨论】:
MyClass& temp = global_variable_input;
怎么样? `
您尝试过不同的优化级别吗?
感谢您的建议!我会尝试参考。但我可以想象编译器会发现在每次操作后更新值更合理? (因为它现在是对全局变量的引用,其他人可能正在使用它)
是的,我尝试了不同的优化级别,但即使使用 -O3,编译器也会以这种方式运行。只有 O3 + 内联 ALL 方法的组合似乎导致了改进。
您在编译时和链接时都尝试过g++ -O3 -flto
吗?您是否尝试过最近的 GCC 和/或Clang 编译器?
【参考方案1】:
使用类似的引用
MyClass& temp = global_variable_input;
将避免在堆栈上分配MyClass
的完整副本(本地存储)
尽管任何一个
Operation 1 on temp;
Operation 2 on temp;
// ...
也会影响原来的global_variable_input
。
【讨论】:
不幸的是,我的性能问题仍然存在。【参考方案2】:您可以将您的类更改为结构。它将存储在堆栈上而不是堆上。
【讨论】:
以上是关于如何防止声明为局部变量的对象在堆栈中分配?的主要内容,如果未能解决你的问题,请参考以下文章