c++ - 哪些数据会被缓存?

标签 c++ c caching cpu-architecture cpu-cache

cache的描述书上总是很笼统。我是一名建筑学专业的学生。我想了解 cache 的行为更详细。

在c/c++语言代码中,哪些数据会从内存加载到缓存中?频繁使用的时候会加载到缓存吗?例如,当我用C语言编写for循环时,我经常使用变量i, j, and k 。这些也会被加载到缓存中吗? C语言局部变量一般放在栈区,全局变量会放在数据区吗?这些在使用的时候会先加载到缓存中吗?数据是否必须经过缓存才能到达寄存器,然后到达CPU?

指针变量p存储数据的地址。如果我使用指针*p访问变量。会p首先加载到缓存中,然后*p会被加载到缓存中吗?

最佳答案

通常,C++ 程序使用的所有内存(代码和数据)都位于可缓存内存中。

对任何 C++ 对象的任何访问(读或写)1 都会导致包含该对象的缓存行变热,以防万一,假设是正常的 CPU 缓存:设置关联、回写/写入-分配1,即使它以前不热。

最简单的设计是,每一级缓存都通过下一层外层取数据,因此在加载未命中后,各级缓存中的数据都是热的。但是您可以拥有不进行读取分配的外部缓存,并充当 victim caches 。或者是内部缓存独占的外部级别,以避免浪费空间缓存相同的数据两次( https://en.wikipedia.org/wiki/Cache_inclusion_policy )。 但无论发生什么,在读取或写入之后,至少最内部(最接近该 CPU 核心)级别的缓存将具有热数据,因此立即再次访问它(或同一对象中的相邻项)缓存行)会很快。如果下一次访问发生在一系列其他访问之后,不同的设计选择会影响线路仍然处于热状态的可能性。如果很热,您可能会在哪一级缓存中找到它。但最重要的是,任何内存编译器生成的代码接触的内容最终都会进入缓存。CPU 缓存透明地缓存物理内存。

许多缓存线可以同时热点,不会互相混淆。即缓存有很多组。一些访问模式是悲观的,例如多个指针彼此偏移 4k,这将使所有访问在 L1d 缓存中使用相同的别名集,并且有时在 CPU 的内存消歧逻辑中会产生额外的 4k 别名惩罚。 (假设页面大小为 4k,类似于 x86)。例如L1 memory bandwidth: 50% drop in efficiency using addresses which differ by 4096+64 bytes - 内存性能影响可能变得非常复杂。了解一些理论就足以理解一般情况下好的内容,但具体的细节可能非常复杂。 (有时甚至对于带宽博士这样的真正专家来说,例如this case)。

脚注 1:宽松地说,对象是一个命名变量或由指针指向的动态分配的内存。

脚注 2:具有写分配策略的回写式缓存对于现代 CPU 来说几乎是通用的,也是一种伪 LRU 替换策略;请参阅wikipedia 。一些设备的访问模式受益于仅在读取时分配而不是在写入时分配的缓存,但 CPU 受益于写入分配。现代 CPU 几乎总是具有多级缓存层次结构,每个级别都与某种级别的关联性设置关联。某些嵌入式 CPU 可能只有 1 级,甚至没有缓存,但如果您专门为这样的系统编写代码,您就会知道。

现代大型 L3 缓存有时会使用替换策略。


当然,优化可能意味着某些局部变量(尤其是循环计数器或数组指针)可以优化到寄存器中,并且根本不存在于内存中。寄存器根本不是CPU缓存或内存的一部分,它们是一个单独的存储空间。人们经常将其描述为“编译器将值缓存在寄存器中”,但不要将其与 CPU 缓存混淆。 (相关: https://software.rajivprab.com/2018/04/29/myths-programmers-believe-about-cpu-caches/When to use volatile with multi threading? )

如果您想了解编译器正在使 CPU 做什么,请查看编译器的 asm 输出。 How to remove "noise" from GCC/clang assembly output? 。 asm 源中的每个内存访问都是计算机体系结构术语中的访问,因此您可以应用您对给定访问模式的缓存状态的了解来弄清楚集关联缓存会发生什么。

相关内容:

关于c++ - 哪些数据会被缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65402150/

相关文章:

ios - 在 Collection View 中缓存图像

c++ - 在客户端矩形中隐藏光标但不在标题栏上

c++ - 检测并移除网格的隐藏表面

c - 将两个已排序的链表合并为一个链表(递归)

c - 嵌套函数被禁用

php - 记住 MYSQL 查询结果以便在 PHP 页面中使用

java - 如何获取每个子弹的起点? Libgdx 简单游戏

c++ - C++ 中的模板参数

c - 如何在 C 中将字符串添加到 PUNICODE_STRING 的末尾

java - 如何将泛型类型存储在缓存中以避免在检索时进行未经检查的强制转换?