深入代码优化 (三) 数据结构布局优化

存储器简介

1980 年之前,cpu 访问内存和访问寄存器的速度是差不多的,但自 1980 年以来,cpu 和内存的性能差距一直在扩大,1980 年 -- 2000 年,cpu 的性能提升了 600 倍,而内存的访问时间只提升了 6 倍。在 cpu 从内存中读取数据的这段时间内,cpu 都是处于无事可做的等待状态。所以缓存 cache 的出现,在很大程度上能够弥补 cpu 和内存速度之间的巨大鸿沟。在高性能系统中,缓存命中率是决定性能好坏的重要因素。

存储器包含了磁盘,硬盘,内存,缓存,寄存器等能存储数据的设备。存储器的整体思想是,对于每个 k,位于 k 层的更快更小的存储设备,作为位于 k+1 层的更大更慢的存储设备的缓存。实际上,L1, L2, L3 分别缓存下一级存储单元的数据,而寄存器可能会缓存着 L1 cache 中的数据。要去访问内存中的数据时,首先会在寄存器中查找,寄存器中找不到再到 L1 cache 中查找,L1 cache 中找不到再到 L2 cache 中查找,L2 cache 中找不到再到 L3 cache 中查找,L3 中也找不到,最后才会到内存中去读取。

存储器中的数据总是以块大小为传送单元在第 k 层和第 k + 1 层之间来回拷贝的。L1 和寄存器之间的传送块大小为 1 个字,1 个字在 32 位系统上是 4 字节,在 64 位系统上是 8 字节。而 L2 和 L1 之间,以及 L3 和 L2 之间,传送的通常是 8 个字的块,通常称为一个 cache line,64 位系统上是 64 字节。而内存与 L3 之间的传送大小通常是几百或几千字节的块。层次结构中较低的存储器 (离cpu较远) 的访问时间较长,为了补偿这些较长的访问时间,倾向于使用更大的块。

CPU 中有两个硬件用来将数据从内存预取到 L2 和 L3 中的。一个是空间预取器 (Spatial Prefetcher),这个部件每次预取一对 cache line,到 L2 的高速缓存行当中。另一个是 Streamer,用来监视来自 L1 高速缓存的读取请求,以获得地址的升序或者降序序列。当检测到请求的地址是当前地址的前向或者后向流时,会将数据预取到最后一级缓存 L3,通常数据也会被带到 L2,除非 L2 缓存的需求过重。Streamer 可以检测和维护最多 32 个数据访问流,对于每个 4K 页,可以维护一个前向地址流和一个后向地址流。

访问越高层次的部件,所需要的时间就越少。下图为 Haswell process 中 cpu 访问各级缓存所需要的时间。访问 L1 需要大约 4 个时钟周期,L2 需要 11 个时钟周期,L3 需要约 34 个时钟周期。如果 cpu 直接从寄存器中读取数据,则只需要 1 个时钟周期。

下图是 Intel Xeon Processor 5500 这款处理器发生了 L2 cache miss 之后从 L3 或者内存中获取数据所需要的时间。假设 cpu 主频为 2Ghz,那么 1 个时钟周期约为 0.5ns,50 ~ 90 ns 约为 100 ~ 180 cycles。

对齐数据结构,优化内存布局

当需要访问的数据不存在于寄存器中,我们就需要从 L1 cache 中去寻找,并加载到寄存器中,cpu 访问

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值