我正在开发一个性能是一个基本问题的应用程序。特别是,我愿意组织一个树状结构,需要在与内存页面大小相同的块中快速遍历,这样可以减少到达叶子所需的缓存未命中数。
我是内存优化领域的新手。据我所知,访问主内存的过程或多或少如下:
但是,如果我将数据组织成与内存页面大小相同的块,我认为还需要正确对齐这些数据,以便每当一个新的块时我的数据需要加载只需要从主存储器中取出一页内存,而不是包含数据块的前半部分和后半部分的两个页面。原则上,正确对齐的数据块是不是只有一个访问内存而不是两个?难道不应该或多或少地将内存性能提高一倍吗?
我尝试了以下内容:
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
using namespace std;
#define BLOCKS 262144
#define TESTS 131072
unsigned long int utime()
{
struct timeval tp;
gettimeofday(&tp, NULL);
return tp.tv_sec * 1000000 + tp.tv_usec;
}
unsigned long int pagesize = sysconf(_SC_PAGE_SIZE);
unsigned long int block_slots = pagesize / sizeof(unsigned int);
unsigned int t = 0;
unsigned int p = 0;
unsigned long int test(unsigned int * data)
{
unsigned long int start = utime();
for(unsigned int n=0; n<TESTS; n++)
{
for(unsigned int i=0; i<block_slots; i++)
t += data[p * block_slots + i];
p = t % BLOCKS;
}
unsigned long int end = utime();
return end - start;
}
int main()
{
srand((unsigned int) time(NULL));
char * buffer = new char[(BLOCKS + 3) * pagesize];
for(unsigned int i=0; i<(BLOCKS + 3) * pagesize; i++)
buffer[i] = rand();
for(unsigned int i=0; i<pagesize; i++)
cout<<test((unsigned int *) (buffer + i))<<endl;
cout<<"("<<t<<")"<<endl;
delete [] buffer;
}
此代码实例化或多或少1 GB的空字节,用随机数填充它们。然后调用功能测试,在存储器页面中进行所有可能的移位(从0移位到4096移位)。测试函数将提供的指针解释为一组数据块,并对这些块执行一些简单的操作(求和)。访问块的顺序或多或少是随机的(它由部分和确定),因此每次访问新块时,几乎可以肯定不会已经在缓存中。
然后定时功能测试。在所有的换档配置中,我应该观察一些时序,而在一个特定的换档配置中(可能是零位移?)我应该观察到效率方面的一些重大改进。然而,这根本不会发生:所有换档时间彼此完全兼容。
为什么会发生这种情况,这是什么意思?我可以忘记内存对齐吗?我还可以忘记让我的数据块与内存页面一样大吗? (我打算在它们较小的情况下使用一些填充物)。或者,缓存管理过程中的某些内容对我来说还不清楚?