如何解决指针数组中的数据依赖性?

2023-12-20

如果我们有一个整数指针数组,它们都指向同一个 int,并对其进行循环++操作,它会比那些指向两个不同 int 的指针慢 100%。这是一个具体的例子

int* data[2];
int a, b;
a = b = 0;
for (auto i = 0ul; i < 2; ++i) {
    // Case 3: 2.5 sec
    data[i] = &a;

    // Case 2: 1.25 sec
    // if (i & 1)
    //     data[i] = &a;
    // else
    //     data[i] = &b;
}

for (auto i = 0ul; i < 1000000000; ++i) {
    // Case 1: 0.5sec
    // asm volatile("" : "+g"(i)); // deoptimize
    // ++*data[0];

    ++*data[i & 1];
}

总之,观察结果是:(描述了循环体)

情况1(快速): +*指针[0]

案例2(中): +*pointer[i] 的一半指针指向一个 int,另一半指向另一个 int。

情况3(慢): +*pointer[i] 所有指针都指向同一个 int

这是我目前的想法。情况 1 很快,因为现代 CPU 知道我们正在读/写相同的内存位置,从而缓冲操作,而在情况 2 和情况 3 中,我们需要在每次迭代中写出结果。情况 3 比情况 2 慢的原因是,当我们通过指针 a 写入内存位置,然后尝试通过指针 b 读取它时,我们必须等待写入完成。这会停止超标量执行。

我理解正确吗?有什么方法可以在不改变指针数组的情况下使情况3更快吗? (也许添加一些 CPU 提示?)

问题是从真实问题中提取出来的https://github.com/ClickHouse/ClickHouse/pull/7550 https://github.com/ClickHouse/ClickHouse/pull/7550


您已经发现了导致直方图出现瓶颈的影响之一。该问题的解决方法是保留多个计数器数组并循环遍历它们,因此同一索引的重复运行会分布在内存中的 2 或 4 个不同计数器上。

(然后循环计数器数组,将它们汇总为最终一组计数。这部分可以从 SIMD 中受益。)


情况 1 很快,因为现代 CPU 知道我们正在读/写相同的内存位置,从而缓冲操作

不,这不是CPU,而是编译时优化。

++*pointer[0]速度很快,因为编译器可以将存储/重新加载提升到循环之外,实际上只是增加一个寄存器。 (如果您不使用结果,它甚至可能会优化掉。)

假设没有数据争用 UB 让编译器假设没有其他东西正在修改pointer[0]所以每次都会增加同一个对象。假设规则让它保持*pointer[0]在寄存器中而不是实际执行内存目标增量。

所以这意味着增量有 1 个周期的延迟,当然它可以将多个增量合并为一个并执行*pointer[0] += n如果它完全展开并优化循环。


当我们通过指针 a 写入内存位置,然后尝试通过指针 b 读取它时,我们必须等待写入完成。这会停止超标量执行。

是的,通过该内存位置的数据依赖性就是问题所在。在编译时不知道指针都指向同一个位置的情况下,编译器将使 asm 实际上增加指向的内存位置。

不过,“等待写入完成”并不严格准确。 CPU 有一个存储缓冲区,用于将存储执行与高速缓存未命中解耦,以及将无序推测执行与实际提交到 L1d 并对其他内核可见的存储解耦。重新加载最近存储的数据不必等待其提交到缓存;存储转发一旦 CPU 检测到它,从存储缓冲区到重新加载就会发生。

在现代 Intel CPU 上,存储转发延迟约为 5 个周期,因此内存目标添加具有 6 个周期延迟。 (1 表示添加,5 表示存储/重新加载(如果位于关键路径上)。)

是的,乱序执行可以让这两个 6 周期延迟依赖链并行运行。循环开销也被 OoO exec 隐藏在延迟之下。

Related:

  • x86 处理器中的存储到加载转发和内存消歧 http://blog.stuffedcow.net/2014/01/x86-memory-disambiguation/在 stuffedcow.net 上
  • 存储转发地址与数据:英特尔优化指南中 STD 和 STA 之间有什么区别? https://stackoverflow.com/questions/47701898/store-forwarding-address-vs-data-what-the-difference-between-std-and-sta-in-the
  • 在未对齐的内存访问情况下,存储到加载转发如何发生? https://stackoverflow.com/questions/42210733/how-does-store-to-load-forwarding-happens-in-case-of-unaligned-memory-access
  • IvyBridge 上指针追逐循环中附近的依赖存储对性能产生奇怪的影响。添加额外的负载会加快速度吗? https://stackoverflow.com/questions/54084992/weird-performance-effects-from-nearby-dependent-stores-in-a-pointer-chasing-loop
  • 为什么一个进程共享同一个HT核心时,另一个进程的执行时间会更短 https://stackoverflow.com/questions/58106459/why-is-execution-time-of-a-process-shorter-when-another-process-shares-the-same(在 Sandybridge 系列上,存储转发延迟可以是reduced如果您不尝试立即重新加载。)

有什么方法可以在不改变指针数组的情况下使情况3更快吗?

是的,如果这种情况是预料之中的,也许branch on it:

    int *current_pointer = pointer[0];
    int repeats = 1;
    ...

    loop {
        if (pointer[i] == current_pointer) {
            repeats++;
        } else {
            *current_pointer += repeats;
            current_pointer = pointer[i];
            repeats = 1;
        }
    }

我们优化通过计算重复同一指针的游程长度.

这被案例2彻底打败了如果长跑的话会表现不佳not common.

短运行可以被乱序执行隐藏;只有当 dep 链变得足够长以填充 ROB(重新排序缓冲区)时,我们才会真正停止。

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

如何解决指针数组中的数据依赖性? 的相关文章

  • Poco c++Net:Http 从响应中获取标头

    我使用 POCO C Net 库进行 http 我想尝试制定持久缓存策略 首先 我认为我需要从缓存标头中获取过期时间 并与缓存值进行交叉检查 如果我错了 请告诉我 那么我如何从中提取缓存头httpResponse 我已经看到你可以用 Jav
  • 解析 JWT 令牌以仅获取有效负载内容,无需 C# 或 Blazor 中的外部库

    我正在使用 Blazor 编写可以访问 JWT 的客户端应用程序 我想知道一种简单的方法来读取令牌有效负载内容而不添加额外的依赖项 因为我不需要其他信息 也不需要验证令牌 我认为解析有效负载内容应该足够简单 只需将其写入方法即可 JwtTo
  • 在开关中使用“goto”?

    我看到了一个建议的编码标准 内容如下Never use goto unless in a switch statement fall through 我不跟 这个 例外 案例到底是什么样的 这证明了goto 此构造在 C 中是非法的 swi
  • 一元 +/- 运算符如何可能导致“-a”或“+a”中的整数提升,“a”是算术数据类型常量/变量?

    这句看似微不足道的台词摘自我的迈克 巴纳汉和布雷迪的 C 书 第 2 8 8 2 节 http publications gbdirect co uk c book chapter2 expressions and arithmetic h
  • 使用 LINQ 更新 IEnumerable 对象的简单方法

    假设我有一个这样的业务对象 class Employee public string name public int id public string desgination public int grade List
  • C# 编译器不会优化不必要的强制转换

    前几天 在写答案的时候这个问题 https stackoverflow com questions 2208315 why is any slower than contains在这里 关于溢出 我对 C 编译器感到有点惊讶 它没有按照我的
  • MFC:如何设置CEdit框的焦点?

    我正在开发我的第一个简单的 MFC 项目 但我正在努力解决一个问题 想要设置所有的焦点CEdit其中一个对话框中的框 我的想法是 当打开对话框时 焦点位于第一个编辑框上 然后使用 选项卡 在它们之间交换 我看到了方法SetFocus 但我无
  • 如何在三个 IEnumerable 上使用 Zip [重复]

    这个问题在这里已经有答案了 可能的重复 使用 Linq 从 3 个集合创建项目 https stackoverflow com questions 5284315 create items from 3 collections using
  • 析构函数中的异步操作

    尝试在类析构函数中运行异步操作失败 这是代码 public class Executor public static void Main var c1 new Class1 c1 DoSomething public class Class
  • 从BackgroundWorker线程更新图像UI属性

    在我正在编写的 WPF 应用程序中 我有一个 TransformedBitmap 属性 该属性绑定到 UI 上的 Image 对象 每当我更改此属性时 图像就会更新 因此显示在屏幕上的图像也会更新 为了防止在检索下一张图像时 UI 冻结或变
  • 使用 GCC 生成可读的程序集?

    我想知道如何使用GCC http en wikipedia org wiki GNU Compiler Collection在我的 C 源文件中转储机器代码的助记符版本 这样我就可以看到我的代码被编译成什么 你可以使用 Java 来做到这一
  • Linux mremap 不释放旧映射?

    我需要一种方法将页面从一个虚拟地址范围复制到另一个虚拟地址范围 而无需实际复制数据 范围很大 延迟很重要 mremap 可以做到这一点 但问题是它也会删除旧的映射 由于我需要在多线程环境中执行此操作 因此我需要旧映射能够同时使用 因此稍后当
  • 从浏览器访问本地文件?

    您好 我想从浏览器访问系统的本地文件 由于涉及大量安全检查 是否可以通过某种方式实现这一目标 或使用 ActiveX 或 Java Applet 的任何其他工作环境 请帮帮我 要通过浏览器访问本地文件 您可以使用签名的 Java Apple
  • 选择查询不适用于使用Parameters.AddWithValue 的参数

    C 中的以下查询不起作用 但我看不出问题所在 string Getquery select from user tbl where emp id emp id and birthdate birthdate cmdR Parameters
  • 使用 jQuery 从 ASP.Net JSON 服务获取数据

    我正在尝试调用 Google 地图地理编码 API 从纬度 经度对中获取格式化的地址 然后将其记录到控制台 我正在尝试获取为给定位置返回的第一个 formatted address 项目 我很简单无法从 JSON 中提取该项目 我不知道为什
  • 如何调试 .NET 运行时中的内部错误?

    我正在尝试调试一些处理大文件的工作 代码本身works 但 NET 运行时本身会报告零星错误 对于上下文 这里的处理是一个 1 5GB 文件 仅加载到内存中一次 在循环中处理和释放 故意尝试重现此否则不可预测的错误 我的测试片段基本上是 t
  • 通过 Tab 键浏览 XML 文档字段

    In VB NET you can move through the fields in the XML member documentation with the Tab key 这在 C 中不起作用 还有其他方法吗 除了用鼠标将光标放在
  • 如何得知客户端从服务器的下载速度?

    根据客户的下载速度 我想以低质量或高质量显示视频 任何 Javascript 或 C 解决方案都是可以接受的 Thanks 没有任何办法可以确定 您只能测量向客户端发送数据的速度 如果没有来自客户端的任何类型的输入来表明其获取信息的速度 您
  • 如何将 SQL“LIKE”与 LINQ to Entities 结合使用?

    我有一个文本框 允许用户指定搜索字符串 包括通配符 例如 Joh Johnson mit ack on 在使用 LINQ to Entities 之前 我有一个存储过程 该存储过程将该字符串作为参数并执行以下操作 SELECT FROM T
  • 使用未分配的局部变量

    我遇到了一个错误 尽管声明了变量 failturetext 和 userName 错误仍然出现 谁能帮帮我吗 Use of Unassigned local variable FailureText Use of Unassigned lo

随机推荐