4.4.3 高速缓存(cache)优化规则
我们都知道,计算机内存的访问效率大大低于处理器,而且在程序的运行中,被访问的数据和指令相对集中,为此处理器准备了片上高速缓存(cache)来存放需要经常访问的数据和代码。这些数据的内容和所在的虚拟地址(Virtual Address, VA)以表格方式一一对应起来,在处理器访问内存数据时,先去cache中看看这个VA有没有记录,如果有,则命中(cache hit),无需访问内存单元;如果没有找到(cache miss),则转换VA访问数据,并保存到cache中。通常,cache不仅会读取指令需要的数据,还会把这个地址附近的数据都读进来。为了节省cache的宝贵空间,VA值的二进制低位不会被保存,也就是说保存的数据是以2n字节为单位的,VA的值具体会被保存多少位是由cache设计的数据组织方式确定的。
由于现代操作系统的内存管理是分段加分页的管理模式,而页级管理是虚拟内存的基础,为了避免频繁访问三级页表转换地址,处理器准备了页表缓冲(Translation lookaside buffer, TLB)来存放长期命中的页表数据。需要访问虚拟内存时,处理器会先去TLB查询是否命中,如果命中则直接查询TLB表中对应的物理地址。对于虚拟内存的管理,长期没有命中的分页会被交换到磁盘上,下次访问时会触发缺页中断,中断处理程序会把磁盘数据读回RAM。
基于以上设计,cache优化有以下几点:
①数据对齐
cache不会保存VA的二进制低位,对于Intel的32位处理器来说,如果访问的地址是4的倍数,则可以直接查询并提取之;如果不是4的倍数,则需要访问多次。因此,VC++编译器在设置变量地址时会按照4字节边界对齐。
②数据集中
将访问次数多的数据或代码尽量安排在一起,一方面是cache在抓取命中数据时会抓取周围的其他数据;另一方面是虚拟内存分页的问题,如果数据分散,保留到多个分页中,就会导致过多的虚拟地址转换,甚至会导致缺页中断频繁发生,这些都会影响效率。
③减少体积
命中率高的代码段应减少体积,尽量放入cache中,以提高效率。
如果读者对与32位处理器相关的知识意犹未尽,可以阅读《80x86汇编语言程序设计教程》(作者:杨季文)一书,该书对保护模式下的开发讲解得相当不错。