C结构解引用链效率

时间:2015-07-07 09:41:52

标签: c

这是关于在链中解除引用变量的变量。请考虑以下代码:

struct ChannelInfo
{
    int iData1;
    int iData2;
    int iData3;
    int iData4;
}

struct AppInfo
{
    struct ChannelInfo gChanInfo[100];

} gAppInfo;


void main()
{

    gAppInfo.gChannelInfo[50].iData1 = 1;
    gAppInfo.gChannelInfo[50].iData2 = 2;
    gAppInfo.gChannelInfo[50].iData3 = 3;
    gAppInfo.gChannelInfo[50].iData4 = 4;
    foo1();
    foo2();
}

void foo1()
{
    printf("Data1 = %d, Data2 = %d, Data3 = %d, Data4 = %d", gAppInfo.gChannelInfo[50].iData1, gAppInfo.gChannelInfo[50].iData2, gAppInfo.gChannelInfo[50].iData3, gAppInfo.gChannelInfo[50].iData4);
}

void foo2()
{
    struct ChannelInfo* pCurrrentChan = &gAppInfo.gChanInfo[50];
    printf("Data1 = %d, Data2 = %d, Data3 = %d, Data4 = %d", pCurrrentChan->iData1, pCurrrentChan->iData2, pCurrrentChan->iData3, pCurrrentChan->iData4);
}

foo2()是否比foo1()更快?如果数组索引不是常量,用户要求会发生什么?如果有人能够描述这段代码,我将不胜感激。

3 个答案:

答案 0 :(得分:4)

这个assembly version of your code可以帮助您理解为什么您的代码速度较慢。但当然它可能会因目标架构和优化标志而有所不同(使用O2O3标志进行拼接会为foo1foo2生成相同的代码

foo2中,ChannelInfo的地址存储在寄存器中,并且相对于存储在寄存器中的值计算地址。或者在堆栈中最坏的情况下(局部变量),在这种情况下,它可能与foo1一样慢。

foo1中,printf的变量地址是相对于存储在内存堆(或缓存中)中的变量gAppInfo计算的。

根据@Ludin的要求,我添加了these numbers for reference

  • 执行指令:1 ns
  • 从主存储器中取出:~100 ns

assembly version with -O2 flags(-Os和-O3标志生成相同的代码)

答案 1 :(得分:1)

是的,foo2()肯定比foo1()更快,因为foo2引用了一个指向该内存块的指针,每次访问它时只需指向那里并从mmory中获取值。

答案 2 :(得分:1)

思考这样的事情并不是有意义的,而且它是预先成熟的优化,因为代码将得到优化,以便这两个函数都是等价的。

如果由于某种原因你不会优化代码,foo2()会稍微慢一点,因为它会产生更多的指令。

请注意,对printf的调用比该函数中的其余代码慢大约100倍,所以如果你真的关心性能,你应该专注于避免使用stdio.h而不是做这些类型的优化

在答案的底部,我已经包含了一些Windows的基准测试代码。因为printf调用与其他代码相比是如此之慢,并且我们对printf本身的基准测试并不感兴趣,所以我删除了printf调用并用volatile变量替换它们。这意味着无论优化级别如何,都需要编译器执行读取。

  

gcc test.c -otest.exe -std = c11 -pedantic-errors -Wall -Wextra -O0

输出:

foo1 5.669101us
foo2 7.178366us
  

gcc test.c -otest.exe -std = c11 -pedantic-errors -Wall -Wextra -O2

输出:

foo1 2.509606us
foo2 2.506889us

正如我们所看到的,非优化代码的执行时间差异大致相当于生成的汇编指令数(参见@dvhh的答案)。

不科学:

10 /(10 + 16)指令= 0.384

5.67 /(5.67 + 7.18)微秒= 0.441

基准代码:

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>


struct ChannelInfo
{
  int iData1;
  int iData2;
  int iData3;
  int iData4;
};

struct AppInfo
{
  struct ChannelInfo gChannelInfo[100];

} gAppInfo;


void foo1 (void);
void foo2 (void);

static double get_time_diff_us (const LARGE_INTEGER* freq, 
                                const LARGE_INTEGER* before, 
                                const LARGE_INTEGER* after)
{
  return ((after->QuadPart - before->QuadPart)*1000.0) / (double)freq->QuadPart;
}


int main (void)
{
  /*** Initialize benchmarking functions ***/
  LARGE_INTEGER freq;
  if(QueryPerformanceFrequency(&freq)==FALSE)
  {
    printf("QueryPerformanceFrequency not supported");
    return 0;
  }

  LARGE_INTEGER time_before;
  LARGE_INTEGER time_after;

  gAppInfo.gChannelInfo[50].iData1 = 1;
  gAppInfo.gChannelInfo[50].iData2 = 2;
  gAppInfo.gChannelInfo[50].iData3 = 3;
  gAppInfo.gChannelInfo[50].iData4 = 4;


  const size_t ITERATIONS = 1000000;

  QueryPerformanceCounter(&time_before);
  for(size_t i=0; i<ITERATIONS; i++)
  {
    foo1();
  }
  QueryPerformanceCounter(&time_after);
  printf("foo1 %fus\n", get_time_diff_us(&freq, &time_before, &time_after));

  QueryPerformanceCounter(&time_before);
  for(size_t i=0; i<ITERATIONS; i++)
  {
    foo2();
  }
  QueryPerformanceCounter(&time_after);
  printf("foo2 %fus\n", get_time_diff_us(&freq, &time_before, &time_after));

}


void foo1 (void)
{
  volatile int d1, d2, d3, d4;

  d1 = gAppInfo.gChannelInfo[50].iData1;
  d2 = gAppInfo.gChannelInfo[50].iData2;
  d3 = gAppInfo.gChannelInfo[50].iData3;
  d4 = gAppInfo.gChannelInfo[50].iData4;
}

void foo2 (void)
{
  struct ChannelInfo* pCurrrentChan = &gAppInfo.gChannelInfo[50];
  volatile int d1, d2, d3, d4;

  d1 = pCurrrentChan->iData1;
  d2 = pCurrrentChan->iData2;
  d3 = pCurrrentChan->iData3;
  d4 = pCurrrentChan->iData4;
}