测量TLB的容量和访问TLB的开销
2023-11-21
TLB的全称是translation lookaside buffer,不懂这个概念的人就不要看下去了,本文只讲讲怎么测量TLB的容量和访问TLB的开销。
先来讲基本思路。假设我们有一个横跨多页的大数组arr,页面大小是4096个字节,int大小为4个字节,那么每页就有1024个数组元素。第1次访问arr[0],第1次访问arr[1024],第1次访问arr[2048] …… 于是我们就依次访问了第1页,第2页,第3页……
开一个横跨多页的数组,依次修改各个页里面的数组元素,访问的页面数量逐渐变多,直到超过了TLB的容量,访问数组元素就会变慢,这就是基本思路。
几个问题:
- 如何知道操作系统的页面大小?
- 对于有多个CPU的计算机,为了得到更准确的数据,如何做到程序只在一个CPU上运行?
Talk is cheap, show you my code:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sched.h>
#define MAX_NUM_PAGES 256
#define TRIALS 10000000
void trials(int jump, int num_pages, int arr[])
{
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < TRIALS; i++)
{
for (int i = 0; i < num_pages * jump; i += jump)
{
arr[i] += 1;
}
}
gettimeofday(&end, NULL);
uint64_t trial_time = (end.tv_sec - start.tv_sec) * 1000000
+ end.tv_usec - start.tv_usec;
double one_trial_time = trial_time
/ (double)(num_pages * TRIALS);
printf("pages:%d, average time:%f\n",
num_pages, one_trial_time);
}
int main(int argc, char *argv[])
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset))
{
fprintf(stderr, "Error setting cpu affinity\n");
exit(EXIT_FAILURE);
}
int page_size = getpagesize();
int jump = page_size / sizeof(int);
int *arr = (int *)calloc(MAX_NUM_PAGES * jump, sizeof(int));
for (int pages = 1; pages < MAX_NUM_PAGES; pages++)
{
trials(jump, pages, arr);
}
}
不开任何优化,使用GCC编译运行,然后查看结果。在我的计算机上,访问页面数量在[1,10]这个区间时,访问一页所需的时间大约为3ns左右;当页面数量落在[11, 26]时,所需时间为6ns左右;继续增加页面数量,所需时间涨到10ns上下了。这说明系统存在二级TLB。我很懒,就不画折线图了。
如果你有什么想法,或者发现了本文的问题,欢迎给我发邮件。