【问题标题】:Embedded C++, any tips to avoid a local thats only used to return a value on the stack?嵌入式 C++,有什么技巧可以避免仅用于在堆栈上返回值的局部变量?
【发布时间】:2011-01-23 14:11:42
【问题描述】:

我有一个局部变量,仅用于检查另一个函数的结果并在满足某些条件时将其传递。大多数时候,这个标准永远不会得到满足。有什么办法可以避免这种“额外”的地方?

我的二进制文件只有大约 1MB 的存储空间,而且我有数千个遵循这种模式的函数调用。我知道这是一件小事,但如果有更好的模式我很想知道!

SomeDataType myclass::myFunction()
{
   SomeDataType result;  // do I really need this local???

   // i need to check the result and pass it on if it meets a certain condition
   result = doSomething();
   if ( ! result ) {
      return result;
   }

   // do other things here
   ...

   // normal result of processing
   return SomeDataType(whatever);
}

【问题讨论】:

  • 澄清:我不太担心 RAM 的使用,因为我担心闪存消耗与我的可执行文件的大小有关。我确定它可以放入寄存器中,但是额外的本地会增加我的 obj 文件的大小。
  • 我的第一个问题是,你真的需要单独的默认构造然后赋值吗? SomeDataType result = doSomething(); 有什么问题?通过消除 SomeDataType::SomeDataType() 调用,这可能会节省更多闪存。
  • 你可能是对的,我不需要默认构造。
  • “额外的本地会增加我的 obj 文件的大小” - 我不确定我是否遵循。额外的本地将如何增加 obj 文件的大小?它是驻留在 RAM(或寄存器)中的数据。

标签: c++ c embedded stack local


【解决方案1】:

我建议你不要担心。在这个级别进行优化并不是一个很好的利用时间,因为任何体面的编译器都会最大限度地减少所需的本地存储量。堆栈是可重用的,数千次调用无关紧要,因为内存只在使用它的上下文中需要,除此之外不会相关。

如果您确实需要关注堆栈上的几个字节临时存储,您可能应该在汇编中工作。我怀疑情况是否如此,所以不要出汗,把时间花在其他更有趣和更重要的问题上!

【讨论】:

    【解决方案2】:

    在调用 doSomething() 时,您可以返回 SomeDataType(whatever),而不是返回 null。

    【讨论】:

      【解决方案3】:

      我建议您检查编译器选项并确保将它们设置为“优化大小”,​​因为这是您所担心的。如果你的编译器没有这样的设置,那么也许是时候检查一个不同的了。

      【讨论】:

        【解决方案4】:

        我会反汇编代码并弄清楚您的编译器在生成代码时在做什么。现代编译器做了一些棘手的事情,试图猜测如何节省代码空间并不明显——有时我看到从源代码到程序集的对应关系很少。源代码更注重清晰度和维护 - 生成程序集有不同的指标。

        【讨论】:

          【解决方案5】:

          如果您有一个在数千个函数中重复的模式,您可以考虑将其替换为一个函数和一个查找表。

          (如果不了解您的情况的更多细节,我真的无法更具体。)

          【讨论】:

            【解决方案6】:

            如果你没有在函数的其他地方使用结果变量,你可以试试这个:

            if (!doSomething())
            {
                return;
            }
            

            上面的例子允许编译器在必要时创建临时变量,而不是你告诉编译器创建一个。

            如果你是结构化编程的忠实拥护者,你可以试试这个:

            do
            {
                if (!doSomething())
                {
                    break;
                }
            // ...
            } while (false);
            return;
            

            这个例子只允许一个函数中有一个返回点。在处理质量和可追溯性指南时,这可能是一件好事

            【讨论】:

              【解决方案7】:

              如果把函数改为

              void myclass::myFunction(SomeDataType* pResult)
              {
                // i need to check the result and pass it on if it meets a certain condition
                 *pResult = doSomething();
                 if ( ! *pResult ) {
                    return;
                 }
              
                 // do other things here
                 ...
              
                 // normal result of processing
                *pResult = SomeDataType(whatever);
              }
              

              【讨论】:

              • 嗯。也许是正确的方向,但我不确定。我刚刚尝试了一个快速的编译器测试,在函数调用中添加额外的参数似乎消耗了与本地完全相同的空间量,但我不确定这是否正确,因为我可能没有达到最小 exe 大小一个小测试。
              • 不要忘记从函数返回对象时发生的操作,它实际上包括创建临时对象,返回值分配给 abject,而不是将函数的结果分配给变量 assgn 运算符执行,如果你没有分配,而是使用类似 myFunction().doSomething() 的东西,则不会分配,但大多数情况下的典型用法是 var = myFunction()
              • RVO:好主意,但有很多注意事项要确定地依赖它。我刚刚在 Dobbs 博士上找到了关于该主题的好文章:drdobbs.com/cpp/184403855Aaargh。我真的想避免两个版本的可执行文件只是为了找到哪个更好,但我想我真的应该知道更好,只是检查输出是吗? Ce'est la vie。
              【解决方案8】:

              取决于你的编译器是否有返回值优化,以及 Somedatatype 是大还是小。如果对象很大,安全的一面是对这种模式使用智能指针。如果它们是 POD,它们可能会被优化并返回到寄存器中。

              【讨论】:

                【解决方案9】:

                SomeDataType 有多复杂?它有很多成员吗?它在构造函数中做了很多工作吗?如果是这样,那么我会避免这种情况。如果没有,您可能会发现编译器会为此生成好的代码。例如,编译器可能会很好地处理整数类型。

                对于这样的问题,答案几乎总是:查阅编译器的汇编输出。

                【讨论】:

                • 在我的例子中,SomeDataType 是一个非常简单的具体(非虚拟)类,它有一个整数成员变量和大约十几个成员函数。构造函数只执行赋值。我相当肯定地知道,在堆栈上创建它作为返回值比尽可能地创建本地值更便宜。但是,如果存在一个通用解决方案,我真的很想要一个通用的解决方案,因为我不能保证 SomeDataType 总是这样。
                • 好吧,我不认为有一个很好的通用解决方案。事实上,当我为具有严格内存限制的平台编写代码时,像这样的问题让我的 C++ 非常“像 C”。
                • 我同意,你应该看看你的编译器生成的程序集。如果这个变量可以放入一个寄存器,那么任何好的嵌入式编译器都应该为你优化它。
                【解决方案10】:

                正如 asveikau 所指出的,如果 SomeDataType 将“适合寄存器”(例如,它是一个普通的旧 int),那么您的本地将(取决于平台)不会浪费任何内存。我同意 asveikau 的观点,您应该参考汇编输出来评估是否是这种情况。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2014-10-30
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-01-09
                  • 1970-01-01
                  • 2013-09-14
                  • 1970-01-01
                  • 2015-01-03
                  相关资源
                  最近更新 更多