【问题标题】:C pointers vs direct member access for structs结构的 C 指针与直接成员访问
【发布时间】:2010-11-22 16:19:57
【问题描述】:

假设我有一个类似下面的结构......

typedef struct {
  int WheelCount;
  double MaxSpeed;
} Vehicle;

...而且我有一个这种类型的全局变量(我很清楚全局变量的陷阱,这是针对嵌入式系统的,我没有设计它,但不幸的是必要的邪恶。)直接或通过指针访问结构的成员是否更快?即

double LocalSpeed = MyGlobal.MaxSpeed;

double LocalSpeed = pMyGlobal->MaxSpeed;

我的任务之一是简化和修复最近继承的嵌入式系统。

【问题讨论】:

  • 指针为您提供了哪些直接访问没有的功能?而且指针比较慢。
  • -1 用于在没有先进行基准测试的情况下询问微优化。

标签: c embedded pointers global micro-optimization


【解决方案1】:

一般来说,我会说第一个选项:

double LocalSpeed = MyGlobal.MaxSpeed;

这减少了一个取消引用(您没有找到指针,然后取消引用它以到达它的位置)。它也更简单,更易于阅读和维护,因为除了结构之外,您不需要创建指针变量。

话虽如此,我认为您看到的任何性能差异都不会很明显,即使在嵌入式系统上也是如此。两者都将是非常非常快的访问时间。

【讨论】:

  • 我和里德在一起。直接引用更安全,不需要 NULL 检查,并且节省了取消引用操作。无论如何都必须访问变量的内存内容,所以为什么要进行额外的查找。如果您已经可以直接访问变量,则指针不会为您带来任何好处。
  • 至于指针解引用,这就是我的想法,我已经很久没有处理 C 或 C++ 了。谢谢大家的建议。
  • 我对它投了反对票,因为我认为如果不指定答案的意义所在的架构,就无法回答这个问题。例如,请参阅 Ivan 的回答。
  • 除非架构和编译器完全脑死,否则直接访问将在最坏的情况下相同。 MyGlobal 很可能会变成一个静态地址。在最坏的情况下,编译器将使用指向全局的指针。
  • @plinth:只有当这是一个 const 指针时,这才是正确的(并且可能),否则,指针可能会被重新分配。您将在我见过的几乎所有编译器中保持取消引用。我同意,因为这种差异充其量是没有意义的,但从技术上讲,它应该是一个更快的操作。
【解决方案2】:

第一个应该更快,因为它不需要指针解引用。那么对于基于 x86 的系统也是如此,对于其他系统则不确定。

在 x86 上,第一个会转化为类似的东西

mov eax, [address of MyGlobal.MaxSpeed]

第二个是这样的

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)] 

【讨论】:

    【解决方案3】:

    在您的嵌入式平台上,架构很可能以这样一种方式进行了优化,以至于它本质上是一种清洗,即使不是这样,如果在一个非常紧密的循环中执行,您也只会注意到性能影响。

    您的系统可能有更明显的性能领域。

    【讨论】:

      【解决方案4】:
      struct dataStruct
      {
          double first;
          double second;
      } data;
      
      int main()
      {
          dataStruct* pData = &data;
      
          data.first = 9.0;
          pData->second = 10.0;
      }
      

      这是使用VS2008发布模式的汇编输出:

          data.first = 9.0;
      008D1000  fld         qword ptr [__real@4022000000000000 (8D20F0h)] 
      
          pData->second = 10.0;
      008D1006  xor         eax,eax 
      008D1008  fstp        qword ptr [data (8D3378h)] 
      008D100E  fld         qword ptr [__real@4024000000000000 (8D20E8h)] 
      008D1014  fstp        qword ptr [data+8 (8D3380h)] 
      

      【讨论】:

      • 现在针对他的嵌入式平台。
      • 优化设置是什么?
      • 这是错误的。您的第一个成员将始终通过结构的定义获胜(结构的指针也是第一个成员的指针)。你应该比较第二和第二。
      【解决方案5】:

      拆机、拆机、拆机……

      根据您没有向我们展示的代码行,如果您的指针有些静态,一个好的编译器可能会知道这一点并预先计算两者的地址。如果您没有优化,那么整个讨论都是无声的。它还取决于您使用的处理器,两者都可以使用一条指令执行,具体取决于处理器。所以我按照基本的优化步骤:

      1) 拆机检查 2) 执行时间

      如上所述,尽管底线可能是两条指令的情况,而不是一条花费一个时钟周期的情况,您可能永远不会看到。与试图调整一行代码以期提高性能相比,编译器和优化器选择的质量将产生更大的性能差异。切换编译器可以在任一方向为您提供 10-20% 的收益,有时甚至更多。就像更改优化标志一样,打开所有代码并不能生成最快的代码,有时 -O1 的性能优于 -O3。

      了解这两行代码产生了什么以及如何最大限度地提高高级语言的性能来自于针对不同处理器进行编译和使用各种编译器进行反汇编。更重要的是,相关行周围的代码在编译器如何优化该段方面发挥着重要作用。

      在这个问题上使用别人的例子:

      typedef struct
      {
          unsigned int first;
          unsigned int second;
      } dataStruct;
      
      dataStruct data;
      
      int main()
      {
          dataStruct *pData = &data;
      
          data.first = 9;
          pData->second = 10;
      
          return(0);
      }
      

      使用 gcc(不是那么出色的编译器)你会得到:

      mov r2, #10
      mov r1, #9
      stmia   r3, {r1, r2}
      

      所以两行 C 代码合并到一个存储中,这里的问题是用作测试的示例。两个单独的函数会更好一些,但是它需要更多的代码,并且指针需要指向其他一些内存,所以优化器没有意识到它是一个静态全局地址,要测试这个你需要传递地址因此编译器(以及 gcc)无法确定它是静态地址。

      或者没有优化,相同的代码,相同的编译器,指针和直接没有区别。

      mov r3, #9
      str r3, [r2, #0]
      
      mov r3, #10
      str r3, [r2, #4]
      

      根据编译器和处理器的不同,这是您期望看到的,可能没有区别。对于这个处理器,即使测试代码从函数中隐藏了指针的静态地址,它仍然可以归结为两条指令。如果存储在结构元素中的值已经加载到寄存器中,那么它将是一条指令,无论是指针还是直接。

      所以你的问题的答案不是绝对的......这取决于。拆机测试。

      【讨论】:

      • 使用双精度而不是整数只是意味着更多的寄存器存储本身仍然是指针或直接访问的单条指令,因此对于此编译会话的此处理器的此编译器来说仍然没有区别。
      • 这是错误的。您的第一个成员将始终通过结构的定义获胜(结构的指针也是第一个成员的指针)。你应该比较第二和第二。
      【解决方案6】:

      我想,如果这有什么不同,那将取决于架构。

      【讨论】:

        【解决方案7】:

        一般来说,直接访问结构会更快,因为它不需要额外的指针取消引用。指针解引用意味着它必须获取指针(变量中的东西),加载它指向的任何内容,然后对其进行操作。

        【讨论】:

          【解决方案8】:

          在 C 中,应该没有区别,或者对性能影响不大。

          C 学生被教导:

          pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed
          

          您应该能够比较它们的反汇编,以说服自己它们本质上是相同的,即使您不是汇编代码程序员。

          如果您正在寻找性能优化,我会寻找其他地方。通过这种微优化,您将无法节省足够多的 CPU 周期。

          出于文体原因,我更喜欢 Structure-Dot 表示法,尤其是在处理单例全局变量时。我觉得它读起来更干净。

          【讨论】:

          • 问题不在于 -> vs *,而在于首先使用指针。
          • 问题是:“直接访问结构的成员还是通过指针访问更快?”我相信我已经解决了这个问题。
          【解决方案9】:

          直接成员访问更快(对于指针,您通常会获得更多的指针取消引用操作)。虽然我很难想象它会出现问题、性能或其他方面的情况。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-01
            • 2023-03-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多