修改:我使用基准测试的详细信息更新了我的问题
出于基准测试目的,我试图在运行于两个Intel Xeon 56xx(“Westmere”)处理器之上的Linux 3.13系统中设置1GB页面。为此我修改了我的启动参数以添加对1GB页面的支持(10页)。这些引导参数仅包含1GB页面而不包含2MB页面。正在运行hugeadm --pool-list
会导致:
Size Minimum Current Maximum Default
1073741824 10 10 10 *
我的内核启动参数被考虑在内。在我的基准测试中,我正在分配1GiB内存,我想用1GiB大页面支持:
#define PROTECTION (PROT_READ | PROT_WRITE)
#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)
uint64_t size = 1UL*1024*1024*1024;
memory = mmap(0, size, PROTECTION, FLAGS, 0, 0);
if (memory == MAP_FAILED) {
perror("mmap");
exit(1);
}
sleep(200)
在工作台休眠时看/proc/meminfo
(上面sleep
调用),我们可以看到已经分配了一个巨大的页面:
AnonHugePages: 4096 kB
HugePages_Total: 10
HugePages_Free: 9
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 1048576 kB
注意:我在运行工作台之前禁用了THP(通过/sys
文件系统),因此我认为AnonHugePages
报告的/proc/meminfo
字段代表了THP在停止之前分配的大页面。
此时我们可以认为一切都很好,但不幸的是我的工作台让我认为使用了许多2MiB页面,而不是一个1GiB页面。以下是解释:
这个工作台通过指针的追逐随机访问分配的内存:第一步填充内存以启用指针追逐(每个单元格指向另一个单元格),第二步,工作台使用
导航内存。pointer = *pointer;
使用perf_event_open
系统调用,我只计算数据TLB读取未命中的第二步。当内存分配大小为64MiB时,我计算一个非常小的数字,即我的6400000内存访问的0.01%,数据TLB读取未命中。所有访问都保存在TLB中。换句话说,可以在TLB中保留64MiB的内存。一旦分配的内存大小大于64 MiB,我就会看到数据tlb读取未命中。对于内存大小等于128 MiB,我在TLB中错过了6400000次内存访问的50%。 64MiB似乎是适合TLB和64MiB = 32个条目的大小(如下所示)* 2MiB页面。我的结论是我没有使用1GiB页面而是使用2MiB页面。
你能看到对这种行为的任何解释吗?
此外,cpuid
工具会报告以下有关我系统上的tlb的信息:
cache and TLB information (2):
0x5a: data TLB: 2M/4M pages, 4-way, 32 entries
0x03: data TLB: 4K pages, 4-way, 64 entries
0x55: instruction TLB: 2M/4M pages, fully, 7 entries
0xb0: instruction TLB: 4K, 4-way, 128 entries
0xca: L2 TLB: 4K, 4-way, 512 entries
L1 TLB/cache information: 2M/4M pages & L1 TLB (0x80000005/eax):
L1 TLB/cache information: 4K pages & L1 TLB (0x80000005/ebx):
L2 TLB/cache information: 2M/4M pages & L2 TLB (0x80000006/eax):
L2 TLB/cache information: 4K pages & L2 TLB (0x80000006/ebx):
如您所见,没有关于1GiB页面的信息。 TLB中可以缓存多少个这样的页面?
答案 0 :(得分:20)
在这种情况下,您(特别是您的处理器)无法从1GB页面中受益,但您的代码是正确的,无需对可以的系统进行修改。
我按照这些步骤尝试重现您的问题。
My System: Intel Core i7-4700MQ, 32GB RAM 1600MHz, Chipset H87
svn co https://github.com/ManuelSelva/c4fun.git
cd c4fun.git/trunk
make
。发现了一些依赖项是必需的。安装它们。构建失败,但mem_load
确实构建并链接,所以没有进一步追求其余的。重新启动系统,将GRUB时间附加到以下引导参数:
hugepagesz=1G hugepages=10 default_hugepagesz=1G
预留10个1GB页面。
cd c4fun.git/trunk/mem_load
在随机访问模式模式下使用memload
进行多次测试并将其固定到核心3,这不是0(自举处理器)。
./mem_load -a rand -c 3 -m 1073741824 -i 1048576
这导致TLB错过了大约零。
./mem_load -a rand -c 3 -m 10737418240 -i 1048576
这导致大约60%的TLB未命中。我做了预感
./mem_load -a rand -c 3 -m 4294967296 -i 1048576
这导致大约没有TLB未命中。我做了预感
./mem_load -a rand -c 3 -m 5368709120 -i 1048576
这导致大约20%的TLB未命中。
此时我下载了cpuid
实用程序。它给了我这个cpuid -1 | grep -i tlb
:
cache and TLB information (2):
0x63: data TLB: 1G pages, 4-way, 4 entries
0x03: data TLB: 4K pages, 4-way, 64 entries
0x76: instruction TLB: 2M/4M pages, fully, 8 entries
0xb5: instruction TLB: 4K, 8-way, 64 entries
0xc1: L2 TLB: 4K/2M pages, 8-way, 1024 entries
L1 TLB/cache information: 2M/4M pages & L1 TLB (0x80000005/eax):
L1 TLB/cache information: 4K pages & L1 TLB (0x80000005/ebx):
L2 TLB/cache information: 2M/4M pages & L2 TLB (0x80000006/eax):
L2 TLB/cache information: 4K pages & L2 TLB (0x80000006/ebx):
如您所见,我的TLB有4个条目用于1GB页面。这很好地解释了我的结果:对于1GB和4GB的竞技场,TLB的4个插槽完全足以满足所有访问。对于5GB竞技场和随机接入模式模式,5个页面中的4个只能通过TLB进行映射,因此将指针追逐到剩余的一个将导致未命中。将指针追逐到未映射页面的概率是1/5,因此我们预计未命中率为1/5 = 20%,我们得到了。对于10GB,4/10页面被映射,6/10不是这样,未命中率将是6/10 = 60%,我们得到了。
因此,您的代码至少在我的系统上无需修改即可运行。您的代码似乎没有问题。
然后我对CPU-World进行了一些研究,虽然并非所有的CPU都列在TLB几何数据中,但有些是。我看到的唯一一个与你的cpuid打印输出完全相符(可能还有更多)的是Xeon Westmere-EP X5650; CPU-World没有明确说明数据TLB0有1GB页面的条目,但确实说处理器有“1 GB大页面支持”。
然后我做了更多的研究,最后把它钉了下来。 RealWorldTech的一位作者在讨论Sandy Bridge的内存子系统时做了一个(不可否认,我必须找到一个这方面的资源)。它显示为as follows:生成地址后,uops将访问DTLB以从虚拟地址转换为物理地址,与缓存访问的开始并行。 DTLB大多保持不变,但对1GB页面的支持有所改善。 此前,Westmere增加了对1GB页面的支持,但由于TLB没有任何1GB页面条目,因此将1GB页面分成2MB页面。 Sandy Bridge在DTLB中为1GB页面添加了4个专用条目。
(强调补充)
无论模糊概念“CPU支持1GB页面”表示,英特尔认为它并不意味着“TLB支持1GB页面条目”。我担心您无法在英特尔Westmere处理器上使用1GB页面来减少TLB未命中数。
那,或者英特尔通过区分大页面(在TLB中)与大页面来欺骗我们。