std::vector 中的每个元素访问都是缓存未命中吗?

2024-04-04

据了解std::vector将其数据保存在堆上,因此向量本身的实例和第一个元素具有不同的地址。另一方面,std::array是原始数组的轻量级包装,其地址等于第一个元素的地址。

假设集合的大小足以容纳一个缓存行int32。在我的具有 384kB L1 缓存的机器上,它是 98304 个数字。

如果我迭代std::vector事实证明,我总是首先访问向量本身的地址,然后访问下一个元素的地址。并且这些地址可能不在同一缓存行中。因此,每个元素访问都是缓存未命中。

但如果我迭代std::array地址位于同一高速缓存行中,因此应该更快。

我用VS2013进行了全面优化测试std::array大约快 20%。

我的假设正确吗?

Update:为了不创建第二个类似的主题。在这段代码中,我有一个数组和一些局部变量:

void test(array<int, 10>& arr)
{
    int m{ 42 };

    for (int i{ 0 }; i < arr.size(); ++i)
    {
        arr[i] = i * m;
    }
}

在循环中,我访问数组和堆栈变量,它们在内存中彼此远离。这是否意味着每次迭代我都会访问不同的内存并错过缓存?


您所说的许多事情都是正确的,但我不相信您会以您认为的速度看到缓存未命中。相反,我认为您看到了编译器优化的其他影响。

你是对的,当你在 a 中查找一个元素时std::vector,有两次内存读取:首先,读取指向元素的指针的内存;其次,读取元素本身的内存。但是,如果您对std::vector,那么您执行的第一次读取很可能会在元素上出现缓存未命中,但所有后续读取要么在缓存中,要么是不可避免的。内存高速缓存针对引用局部性进行了优化,因此每当将单个地址拉入高速缓存时,大量相邻的内存地址也会被拉入高速缓存。因此,如果您迭代 a 的元素std::vector,大多数时候根本不会有任何缓存未命中。性能看起来应该与常规阵列非常相似。还值得记住的是,缓存存储多个不同的内存位置,而不仅仅是一个,因此您正在读取堆栈上的内容(std::vector内部指针)和堆中的某些内容(元素)或堆栈上的两个不同元素不会立即导致缓存未命中。

需要记住的是,缓存未命中是极其与缓存命中相比昂贵 - 通常慢 10 倍 - 因此,如果您确实看到缓存的每个元素上存在缓存未命中std::vector您不会看到只有 20% 的性能差距。您会看到更接近 2 倍或更大的性能差距。

那么,为什么您会看到性能差异呢?您尚未考虑的一个重要因素是,如果您使用std::array<int, 10>,那么编译器可以在编译时得知该数组中正好有十个元素,并且可以展开或以其他方式优化循环,您必须消除不必要的检查。事实上,编译器原则上可以用 10 个连续的代码块替换循环,这些代码块全部写入特定的数组元素,这可能比在循环中反复向后分支要快得多。另一方面,使用等效代码std::vector,编译器无法总是提前知道循环将运行多少次,因此它很可能无法生成与为数组生成的代码一样好的代码。

事实上,您在这里编写的代码非常小,任何对其计时的尝试都会产生大量噪音。很难评估其可靠程度,因为与该方法的“冷”运行相比,仅仅将其放入 for 循环这样简单的事情就会扰乱缓存行为。

总的来说,我不会将其归因于缓存未命中,因为我怀疑缓存未命中的数量是否有明显不同。相反,我认为这是对大小静态已知的数组的编译器优化,与对数组的优化相比std::vector其大小只能动态得知。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

std::vector 中的每个元素访问都是缓存未命中吗? 的相关文章

随机推荐

  • 使用 cblas 库时出现“对‘cblas_ddot’的未定义引用”

    我正在测试 cblas ddot 我使用的代码来自link https stackoverflow com questions 14470799 calling ddot function in blas library我将其修复为 inc
  • 为什么当我添加产品图像代码时不支持“产品名称”并且“列“产品名称”不允许为 NULL;SQL 语句:”[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 i am not add any null in productName but error org hibernate exception
  • 用户输入是否进入控制器或模型?

    现在我已经拆分了模型 但我的控制器和视图仍然组合在一个 12k 行文件中 我一直在寻求为此创建一个真正的 MVC 系统 拆分视图 但是在寻找要拆分的内容时 我注意到我的控制器正在执行大量可能属于模型的工作 例如 假设我有 if isset
  • IE7 一段时间后停止发出网络请求

    我们的 asp net 系统是一个更大系统的一部分 它是通过这个更大的系统从 javascript 启动的 该系统执行 window open 调用来打开一个新窗口 此外 身份验证数据等通过加密的查询字符串参数传递到我们的系统 当使用 IE
  • Windows - 使用 perl 监视目录中是否有新文件删除/创建

    寻找一种方法来监视目录中新文件的创建或删除 因此 如果我有一个文件夹 c temp 并且在其中复制 创建了 abc txt 我需要一个事件或其他内容 以便我可以拾取该文件然后处理它 另外 我想持续监控这个文件夹 我怎样才能做到这一点 我正在
  • 使用 AVAudioRecorder 看似随机的文件损坏(有时文件无法播放) - iOS

    在我目前正在开发的应用程序中 我或多或少遇到了障碍 在应用程序中 您可以进入一个视图 该视图在标准表格视图中列出所有本地保存的音频文件 从这里 您可以单击它们来播放它们 或者点击下面的录制按钮来制作新的录音 该录音随后会自动保存到应用程序沙
  • Android:Listview的弹跳到scrollview

    有什么方法可以将ListView的弹跳效果添加到常规滚动视图中吗 我所说的弹跳是指当您到达列表底部时类似橡皮筋的效果 在android中为listview添加效果反弹 Step 1 在com base view包中创建新文件BounceLi
  • 在 vaadin 8 中将文本复制到剪贴板

    我想问如何在 vaadin 8 java web 应用程序中正确地将一些文本复制到剪贴板 我找到了适用于 Chrome 和 IE 的解决方案 但不适用于 Firefox Firefox 总是提示 错误 document execComman
  • JqG​​rid 单元格中的选择框

    我试图让选择框位于特定的单元格中 我的复选框显示得很好 但选择框没有显示 list5 jqGrid datatype local width 100 height 100 colNames Universe1 Connect String1
  • 从更高的时间范围获取历史值

    我构建了一个自定义指标 并使用蜡烛顶部的点绘制了它们 当一个点与另一个点满足特定标准时 我会绘制一条连接它们的趋势线 这样可行 我想做的是从更高的时间范围增加这些线 因为我通常在 5m 上进行交易 意思是 如果每日时间范围内的这些点符合标准
  • jquery加载大数据

    我有一个返回数据的 Web 服务 数据集相当大 可能有 600 行 20 列 在 Jquery 代码中将此数据加载到 html 表中最快最有效的方法是什么 我尝试通过循环返回的数据并在字符串中创建表 DOM 来创建表 html 但循环部分非
  • 遵循 JSON-LD API 中的所有链接

    假设我想使用一个返回 JSON LD 的 API 并跟踪所有链接 我正在尝试Hydra API 演示 http www markus lanthaler com hydra api demo 但它应该适用于所有 JSON LD API 而不
  • ListView 中的 WPF ListView

    我确信我错过了一些简单 明显的东西 但我似乎无法在 ListView 中绑定 ListView 的数据
  • Java 中的 Thread.Sleep 替代方案

    有人告诉我使用Thread Sleep 有时 人们希望在同步方法的操作循环中设置一些时间间隔 这是一个糟糕的解决方案 另一方面 我有两个不同的线程 它们在程序运行期间处于活动状态 还有一个共享对象 当我在该共享对象中使用 Object wa
  • 是否有像 pygccxml 一样的 Python Clang 包装器来包装 GCC-XML?

    很长一段时间以来 我一直在使用 pygccxml 来解析和内省我的 C 源代码 它帮助我在构建过程中进行一些巧妙的代码生成 最近我读了很多关于 LLVM 堆栈的好处 特别是 LLVM Clang 解析器给 C 编译带来的好处 我现在想知道
  • 如何在 Debian 上升级 glibc?

    我听说我可以使用apt get install libc6 但我需要向 etc apt sources list 添加一些内容才能接收最新的 glibc 版本 我应该怎么办 我能够安装libc6 2 17 in Debian Wheezy通
  • 3D饼图:图例太大

    传说的问题太大了 当我改变cex的数量时 字体太小 盒子仍然很大 希望盒子和测试可以搭配 不会太小也不会太大 table lt data frame num c 90 26 28 39 98 countries c India Sri La
  • Parse.com:如何为 Fragment 内的 Parse ListView 添加搜索过滤器

    我正在尝试为选项卡片段内的 ListView 添加搜索过滤器 使用适配器从解析服务器调用数据 我的片段java文件如下 跑车 java import android os Bundle import android text Editabl
  • 如何使 Hibernate @Lock 注释适用于 Oracle DB?

    我偶然发现 Oracle DB 中锁定行的问题 锁的目的是防止多个事务从数据库读取数据 因为这些数据会影响新数据的生成 并且会在事务中发生更改 为了进行锁定 我将 Lock 注释放在 SpringData find 方法上 该方法检索参与事
  • std::vector 中的每个元素访问都是缓存未命中吗?

    据了解std vector将其数据保存在堆上 因此向量本身的实例和第一个元素具有不同的地址 另一方面 std array是原始数组的轻量级包装 其地址等于第一个元素的地址 假设集合的大小足以容纳一个缓存行int32 在我的具有 384kB