我的目标是使用简单的代码来衡量(不同)缓存的效果。我正在关注这篇文章,特别是第20页和第21页:https://people.freebsd.org/~lstewart/articles/cpumemory.pdf
我正在使用64位Linux。 L1d缓存为32K,L2为256K,L3为25M。
这是我的代码(我使用没有标志的g ++编译此代码):
#include <iostream>
// ***********************************
// This is for measuring CPU clocks
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif
// ***********************************
static const int ARRAY_SIZE = 100;
struct MyStruct {
struct MyStruct *n;
};
int main() {
MyStruct myS[ARRAY_SIZE];
unsigned long long cpu_checkpoint_start, cpu_checkpoint_finish;
// Initializing the array of structs, each element pointing to the next
for (int i=0; i < ARRAY_SIZE - 1; i++){
myS[i].n = &myS[i + 1];
for (int j = 0; j < NPAD; j++)
myS[i].pad[j] = (long int) i;
}
myS[ARRAY_SIZE - 1].n = NULL; // the last one
for (int j = 0; j < NPAD; j++)
myS[ARRAY_SIZE - 1].pad[j] = (long int) (ARRAY_SIZE - 1);
// Filling the cache
MyStruct *current = &myS[0];
while ((current = current->n) != NULL)
;
// Sequential access
current = &myS[0];
// For CPU usage in terms of clocks (ticks)
cpu_start = rdtsc();
while ((current = current->n) != NULL)
;
cpu_finish = rdtsc();
unsigned long long avg_cpu_clocks = (cpu_finish - cpu_start) / ARRAY_SIZE;
std::cout << "Avg CPU Clocks: " << avg_cpu_clocks << std::endl;
return 0;
}
我有两个问题:
1- I将ARRAY_SIZE从1变为1,000,000(因此我的阵列大小介于2B到2MB之间),但平均CPU时钟始终为10。
根据该PDF(第21页的图3-10),当阵列完全适合L1时,我希望得到3-5个时钟,当它超过L1的大小时,可以得到更高的数字(9个周期)。
2-如果我将ARRAY_SIZE增加到超过1,000,000,我将得到分段错误(核心转储),这是由于堆栈溢出造成的。我的问题是使用动态分配(MyStruct *myS = new MyStruct[ARRAY_SIZE]
)是否不会产生任何性能损失。
这是我的代码(我使用没有标志的g ++编译此代码)
如果你没有传递-O3
,那么while ((current = current->n) != NULL)
将编译成多个内存访问,而不是单个加载指令。通过传递-O3
,循环将被编译为:
.L3:
mov rax, QWORD PTR [rax]
test rax, rax
jne .L3
这将按照您的预期每次迭代运行4个周期。
请注意,您可以使用__rdtsc
编译器内在而不是内联汇编。见:Get CPU cycle count?。