这是关于在链中解除引用变量的变量。请考虑以下代码:
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()更快?如果数组索引不是常量,用户要求会发生什么?如果有人能够描述这段代码,我将不胜感激。
答案 0 :(得分:4)
这个assembly version of your code可以帮助您理解为什么您的代码速度较慢。但当然它可能会因目标架构和优化标志而有所不同(使用O2
或O3
标志进行拼接会为foo1
和foo2
生成相同的代码
在foo2
中,ChannelInfo
的地址存储在寄存器中,并且相对于存储在寄存器中的值计算地址。或者在堆栈中最坏的情况下(局部变量),在这种情况下,它可能与foo1
一样慢。
在foo1
中,printf的变量地址是相对于存储在内存堆(或缓存中)中的变量gAppInfo
计算的。
根据@Ludin的要求,我添加了these numbers for reference:
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;
}