CSDN|如何写出让 CPU 跑得更快的代码?( 四 )


那访问 array[0][0] 元素时 , CPU 具体会一次从内存中加载多少元素到 CPU Cache 呢?这个问题 , 在前面我们也提到过 , 这跟 CPU Cache Line 有关 , 它表示 CPU Cache 一次性能加载数据的大小 , 可以在 Linux 里通过 coherency_line_size 配置查看 它的大小 , 通常是 64 个字节 。CSDN|如何写出让 CPU 跑得更快的代码?
本文插图
也就是说 , 当 CPU 访问内存数据时 , 如果数据不在 CPU Cache 中 , 则会一次性会连续加载 64 字节大小的数据到 CPU Cache , 那么当访问 array[0][0] 时 , 由于该元素不足 64 字节 , 于是就会往后顺序读取 array[0][0]~array[0][15] 到 CPU Cache 中 。 顺序访问的 array[i][j] 因为利用了这一特点 , 所以就会比跳跃式访问的 array[j][i] 要快 。
因此 , 遇到这种遍历数组的情况时 , 按照内存布局顺序访问 , 将可以有效的利用 CPU Cache 带来的好处 , 这样我们代码的性能就会得到很大的提升 ,
如何提升指令缓存的命中率? 提升数据的缓存命中率的方式 , 是按照内存布局顺序访问 , 那针对指令的缓存该如何提升呢?
我们以一个例子来看看 , 有一个元素为 0 到 100 之间随机数字组成的一维数组:
CSDN|如何写出让 CPU 跑得更快的代码?
本文插图
接下来 , 对这个数组做两个操作:CSDN|如何写出让 CPU 跑得更快的代码?
本文插图

  • 第一个操作 , 循环遍历数组 , 把小于 50 的数组元素置为 0;
  • 第二个操作 , 将数组排序;
那么问题来了 , 你觉得先遍历再排序速度快 , 还是先排序再遍历速度快呢? 在回答这个问题之前 , 我们先了解 CPU 的分支预测器 。 对于 if 条件语句 , 意味着此时至少可以选择跳转到两段不同的指令执行 , 也就是 if 还是 else 中的指令 。 那么 , 如果分支预测可以预测到接下来要执行 if 里的指令 , 还是 else 指令的话 , 就可以「提前」把这些指令放在指令缓存中 , 这样 CPU 可以直接从 Cache 读取到指令 , 于是执行速度就会很快 。当数组中的元素是随机的 , 分支预测就无法有效工作 , 而当数组元素都是顺序的 , 分支预测器会动态地根据历史命中数据对未来进行预测 , 这样命中率就会很高 。因此 , 先排序再遍历速度会更快 , 这是因为排序之后 , 数字是从小到大的 , 那么前几次循环命中 if < 50 的次数会比较多 , 于是分支预测就会缓存 if 里的 array[i] = 0 指令到 Cache 中 , 后续 CPU 执行该指令就只需要从 Cache 读取就好了 。如果你肯定代码中的 if 中的表达式判断为 true 的概率比较高 , 我们可以使用显示分支预测工具 , 比如在 C/C++ 语言中编译器提供了 likely 和 unlikely 这两种宏 , 如果 if 条件为 ture 的概率大 , 则可以用 likely 宏把 if 里的表达式包裹起来 , 反之用 unlikely 宏 。CSDN|如何写出让 CPU 跑得更快的代码?
本文插图
实际上 , CPU 自身的动态分支预测已经是比较准的了 , 所以只有当非常确信 CPU 预测的不准 , 且能够知道实际的概率情况时 , 才建议使用这两种宏 。如果提升多核 CPU 的缓存命中率? 在单核 CPU , 虽然只能执行一个进程 , 但是操作系统给每个进程分配了一个时间片 , 时间片用完了 , 就调度下一个进程 , 于是各个进程就按时间片交替地占用 CPU , 从宏观上看起来各个进程同时在执行 。而现代 CPU 都是多核心的 , 进程可能在不同 CPU 核心来回切换执行 , 这对 CPU Cache 不是有利的 , 虽然 L3 Cache 是多核心之间共享的 , 但是 L1 和 L2 Cache 都是每个核心独有的 , 如果一个进程在不同核心来回切换 , 各个核心的缓存命中率就会受到影响 , 相反如果进程都在同一个核心上执行 , 那么其数据的 L1 和 L2 Cache 的缓存命中率可以得到有效提高 , 缓存命中率高就意味着 CPU 可以减少访问 内存的频率 。当有多个同时执行「计算密集型」的线程 , 为了防止因为切换到不同的核心 , 而导致缓存命中率下降的问题 , 我们可以把线程绑定在某一个 CPU 核心上 , 这样性能可以得到非常可观的提升 。在 Linux 上提供了 sched_setaffinity 方法 , 来实现将线程绑定到某个 CPU 核心这一功能 。总结由于随着计算机技术的发展 , CPU 与 内存的访问速度相差越来越多 , 如今差距已经高达好几百倍了 , 所以 CPU 内部嵌入了 CPU Cache 组件 , 作为内存与 CPU 之间的缓存层 , CPU Cache 由于离 CPU 核心很近 , 所以访问速度也是非常快的 , 但由于所需材料成本比较高 , 它不像内存动辄几个 GB 大小 , 而是仅有几十 KB 到 MB 大小 。


推荐阅读