为什么这个“std::atomic_thread_fence”起作用

2024-05-01

首先我想谈一下我对此的一些理解,如有错误请指正。

  1. a MFENCE在x86中可以保证全屏障
  2. 顺序一致性可防止 STORE-STORE、STORE-LOAD、LOAD-STORE 和 LOAD-LOAD 重新排序

    这是根据维基百科 https://en.wikipedia.org/wiki/Memory_ordering.

  3. std::memory_order_seq_cst不保证防止 STORE-LOAD 重新排序。

    这是根据亚历克斯的回答 https://stackoverflow.com/questions/39053600/does-standard-c11-guarantee-that-memory-order-seq-cst-prevents-storeload-reord?rq=1、“负载可能会与早期存储一起重新排序到不同位置”(对于 x86)并且 mfence 不会始终被添加。

    无论是std::memory_order_seq_cst表示顺序一致性?根据第2/3点,我认为这似乎不正确。std::memory_order_seq_cst仅当以下情况时才表示顺序一致性

    1. 至少一个明确的MFENCE添加到任一LOAD or STORE
    2. LOAD(无栅栏)和 LOCK XCHG
    3. LOCK XADD ( 0 ) 和 STORE (无栅栏)

    否则仍有可能重新订购。

    根据@LWimsey 的评论,我在这里犯了一个错误,如果两者都LOAD and STORE are memory_order_seq_cst,没有重新排序。 Alex 可能指出使用非原子或非 SC 的情况。

  4. std::atomic_thread_fence(memory_order_seq_cst)总是产生一个完整的屏障

    这是根据亚历克斯的回答 https://stackoverflow.com/questions/25478029/does-atomic-thread-fencememory-order-seq-cst-have-the-semantics-of-a-full-memo。所以我可以随时更换asm volatile("mfence" ::: "memory") with std::atomic_thread_fence(memory_order_seq_cst)

    这对我来说很奇怪,因为memory_order_seq_cst原子函数和栅栏函数之间的用法似乎有很大不同。

现在我在MSVC 2015的标准库的头文件中找到这段代码,它实现了std::atomic_thread_fence

inline void _Atomic_thread_fence(memory_order _Order)
    {   /* force memory visibility and inhibit compiler reordering */
 #if defined(_M_ARM) || defined(_M_ARM64)
    if (_Order != memory_order_relaxed)
        {
        _Memory_barrier();
        }

 #else
    _Compiler_barrier();
    if (_Order == memory_order_seq_cst)
        {   /* force visibility */
        static _Uint4_t _Guard;
        _Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst);
        _Compiler_barrier();
        }
 #endif
    }

所以我的主要问题是如何_Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst);打造全面屏障MFENCE,或者实际上做了什么来启用类似的等效机制MFENCE,因为一个_Compiler_barrier()显然这里对于完整的内存屏障来说是不够的,或者这个语句的工作原理有点类似于第 3 点?


所以我的主要问题是如何_Atomic_exchange_4(&_Guard, 0, memory_order_seq_cst);创建一个完整的屏障 MFENCE

This compiles to an xchg instruction with a memory destination. This is a full memory barrier (draining the store buffer) exactly1 like mfence.

在此之前和之后存在编译器障碍,也可以防止围绕它的编译时重新排序。因此,阻止了任一方向上的所有重新排序(原子和非原子 C++ 对象上的操作),使其足够强大,可以执行 ISO C++ 的所有操作atomic_thread_fence(mo_seq_cst)承诺。


对于弱于 seq_cst 的指令,只需要编译器屏障。 x86 的硬件内存排序模型是程序顺序 + 具有存储转发的存储缓冲区。这足够强大了acq_rel编译器不会发出任何特殊的 asm 指令,只是阻止编译时重新排序。https://preshing.com/20120930/weak-vs-strong-memory-models/ https://preshing.com/20120930/weak-vs-strong-memory-models/


脚注1:完全足以满足 std::atomic 的目的。来自 WC 内存的弱有序 MOVNTDQA 加载可能不会严格排序lockMFENCE 的编辑说明。

  • x86 上哪个写屏障更好:lock+addl 或 xchgl? https://stackoverflow.com/questions/4232660/which-is-a-better-write-barrier-on-x86-lockaddl-or-xchgl/52910647#52910647
  • lock xchg 与 mfence 具有相同的行为吗? https://stackoverflow.com/questions/40409297/does-lock-xchg-have-the-same-behavior-as-mfence- 与 std::atomic 目的相同,但对于使用 WC 内存区域的设备驱动程序可能存在一些细微的差异。以及性能差异。尤其在 Skylake 上mfence阻止 OoO exec 类似lfence https://stackoverflow.com/questions/50494658/are-loads-and-stores-the-only-instructions-that-gets-reordered
  • 为什么 LOCK 在 x86 上是完全屏障? https://stackoverflow.com/questions/60332591/why-is-lock-a-full-barrier-on-x86

x86 上的原子读-修改-写 (RMW) 操作只能通过lock前缀,或xchg有记忆 https://www.felixcloutier.com/x86/xchg即使机器代码中没有锁定前缀,情况也是如此。带锁前缀的指令(或带有 mem 的 xchg)始终是完整的内存屏障。

使用类似的指令lock add dword [esp], 0作为替代品mfence是一项众所周知的技术。 (并且在某些 CPU 上性能更好。)这个 MSVC 代码是相同的想法,但它不是对堆栈指针指向的任何内容执行无操作,而是执行xchg在虚拟变量上。实际上它在哪里并不重要,但是仅由当前核心访问并且在缓存中已经很热的缓存线是性能的最佳选择。

Using a static所有核心都将争夺访问权限的共享变量是最糟糕的选择;这段代码太糟糕了!无需与其他核心相同的高速缓存行交互来控制该核心对其自己的 L1d 高速缓存的操作顺序。这完全是疯了。 MSVC 显然仍然在其实现中使用这个可怕的代码std::atomic_thread_fence(),即使对于 x86-64,其中mfence保证可用。 (Godbolt 与 MSVC 19.14 https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAM1QDsCBlZAQwBtMQBGAFlICsupVs1qhkAUgBMAISnTSAZ0ztkBPHUqZa6AMKpWAVwC2tLgFZSW9ABk8tTADljAI0zEQADlIAHVAsLqtHqGJuY%2BfgF0tvZORq7uXkoqanQMBMzEBMHGppwWSZiqgWkZBNGOLm6eiumZ2aF5NaXlsfGeAJSKqAbEyBwA5FIAzHbIhlgA1OJDOsxERngSQ9jiAAwAgmvrAG6oeOgTzhnEeG4Q7VMA7LIbE3cTCgToICBzqAvIAPoECMSYzOhPlQtH0II9niAjJgjCQAJ6fEhYYifJQAR0%2ByEe7WmN02lwAIlstrt9g8iH8UcgwU8Xm8PtMdHYCNNsFNJAA2bYZC7ia5be4TLnEKZDfETTiSIY4okE/qdVggfpmfqkUz9VYq1CKnRyOQPbq9TBsoacFUERUa9qdBD/JGUToAaxAQ24ADoAJwednu7hmTjey4SrwK/rcFVqjWkLX9FUKECrUjm9Vy0hwWBINBGbx4dhkCgQTPZ3MoVifewAd0%2B7N4VBzBDccYgzgtKucdgysMVptImah9AA8rRWJ3k6QsEYRMB2C2x3g/kVtpg46PMAAPQoGetdlVM5Qz1h4ZzEDt6LDbxMnIzbzo0ehMNgcHj8QTCUQoXUyISHuOQTqobwpLQy4ALT9pIsbKIUgGaNo9S5JY2gtJU7icOE/iAXBgi%2BOhgRIXEVSoQURSpLUWT6DkghEYBJSZHhbSEaRmEMc0dgVPhKGdAoBp9Fw8qKsqqoztGq7VhMRgKNsyCCpw7qujwEwQAA6gAkg42AXBAuCECQxqoRMehZjmbi6RcOoyHIZotp0EAZu8RZuOQlCFkZ7iLMgMmobWrD1sQjbNqOba0B2569loBCDsOM7jpO06jvg85qIuy6RmuG5bv03a7iGkYHkeJ4YAM3YEJe16kLejAsNOT4CEMQiTu%2B5mfrlP7nFGAGBCB/ZDBBySBDBujkQ0CE2KxrQEWhkRBIN8HYZNdHjVRxSMdNlGQcRtA0WUo3IZRy0hPBjwsTEO2cJx3GPnxSrhkJiqrl6wGiW54qyasckKVp5K6aQ%2Bl2S5xpDKZH7SJZyZWqQTpDJIrpmKs7JmJIlzcHkPBDOyQiKmGgmjtGsbxomVmpjAiAoL9uaOQWpNVMA7q1V5Pl%2BTOgXBRlKqhQOQ4jpG0WiLFXNzlBeBJTOqXIJuhU7vQe6jrlx7ELCp7ixeeBXizN50BVD5cLwAiSHVb4SI18jNfAf7tXQnXdYoa3QRAVhMcN80oRNGErahs2AY7q29SRpT24tPu0dt7G7b7rtNIHx3B6dXQ9Dx0chgJEaard92icAyBSbJQzvfgn3DHpBn2cKwySIDhsg5a1q2lUrUQ1DMNwwjSN%2BtwqPo6G13Y4quMJkmlft%2BBWORjj%2BOg50i6%2BX13BAA)

如果你正在做 seq_cststore,你的选择是mov+mfence(海湾合作委员会这样做)或做商店and障碍与单一xchg(clang 和 MSVC 这样做,所以代码生成很好,没有共享虚拟变量)。


这个问题的大部分早期部分(陈述“事实”)似乎都是错误的,并且包含一些误解或被误导的事情,甚至没有错。

std::memory_order_seq_cst不保证防止 STORE-LOAD 重新排序。

C++ 使用完全不同的模型来保证顺序,其中获取加载从发布存储中看到的值与其“同步”,并且 C++ 源代码中的后续操作保证可以看到发布存储之前代码中的所有存储。

它还保证总订单为allseq_cst 操作甚至可以跨不同的对象。 (较弱的顺序允许线程在其自己的存储变得全局可见之前重新加载,即存储转发。这就是为什么只有 seq_cst 必须耗尽存储缓冲区。它们还允许 IRIW 重新排序。其他线程是否总是以相同的顺序看到对不同线程中不同位置的两个原子写入? https://stackoverflow.com/questions/27807118/will-two-atomic-writes-to-different-locations-in-different-threads-always-be-see/50679223#50679223)

StoreLoad 重新排序等概念基于以下模型:

  • 所有核心间通信都是通过将存储提交到缓存一致的共享内存
  • 重新排序发生在一个核心内部对缓存的访问之间。例如通过存储缓冲区延迟存储可见性,直到稍后加载(如 x86 允许)。 (除非核心可以通过商店转发尽早看到自己的商店。)

就该模型而言,seq_cst 确实需要在 seq_cst 存储和稍后的 seq_cst 加载之间的某个时刻耗尽存储缓冲区。实现这一目标的有效方法是设置全面的屏障afterseq_cst 存储。 (而不是在每次 seq_cst 加载之前。便宜的加载比便宜的存储更重要。)

在像 AArch64 这样的 ISA 上,有加载获取和存储释放指令,它们实际上具有顺序释放语义,这与 x86 加载/存储“仅”常规释放不同。 (因此 AArch64 seq_cst 不需要单独的屏障;微体系结构可以延迟耗尽存储缓冲区,除非/直到执行加载获取,同时仍然有存储释放未提交到 L1d 缓存。)其他 ISA 通常需要完整的屏障在 seq_cst 存储之后耗尽存储缓冲区的指令。

当然,即使是 AArch64 也需要完整的屏障指令seq_cst fence,不同于seq_cst加载或存储手术.


std::atomic_thread_fence(memory_order_seq_cst)总是产生一个完整的屏障

实际上是的。

所以我可以随时更换asm volatile("mfence" ::: "memory") with std::atomic_thread_fence(memory_order_seq_cst)

实际上是的,但理论上,实现可能允许对非原子操作进行一些重新排序std::atomic_thread_fence并且仍然符合标准。Always这是一个非常强烈的词。

ISO C++ 仅在以下情况下保证任何内容std::atomic涉及加载或存储操作。 GNU C++ 可以让你推出自己的原子操作asm("" ::: "memory")编译器障碍 (acq_rel) 和asm("mfence" ::: "memory")全面壁垒。将其转换为 ISO C++ signal_fence 和 thread_fence 将留下一个具有数据争用 UB 的“可移植”ISO C++ 程序,因此无法保证任何内容。

(尽管请注意,滚动你自己的原子应该使用至少volatile https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading/58535118#58535118,而不仅仅是障碍,以确保编译器不会发明多个加载,即使您避免了将加载提升到循环之外的明显问题。谁害怕一个糟糕的优化编译器? https://lwn.net/Articles/793253/).


永远记住,实现所做的事情必须是at least与 ISO C++ 所保证的一样强大。这往往最终会变得更强大。

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

为什么这个“std::atomic_thread_fence”起作用 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • 是否有比 lex/flex 更好(更现代)的工具来生成 C++ 分词器?

    我最近将源文件解析添加到现有工具中 该工具从复杂的命令行参数生成输出文件 命令行参数变得如此复杂 以至于我们开始允许它们作为一个文件提供 该文件被解析为一个非常大的命令行 但语法仍然很尴尬 因此我添加了使用更合理的语法解析源文件的功能 我使
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 将日期参数传递给对 MVC 操作的 ajax 调用的安全方法

    我有一个 MVC 操作 它的参数之一是DateTime如果我通过 17 07 2012 它会抛出一个异常 指出参数为空但不能有空值 但如果我通过01 07 2012它被解析为Jan 07 2012 我将日期传递给 ajax 调用DD MM
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反

随机推荐

  • C# 中类实例的内存使用情况[重复]

    这个问题在这里已经有答案了 可能的重复 C NET 对象使用多少内存 https stackoverflow com questions 426396 how much memory does a c net object use 就像标题
  • 将文件读入 Perl 中的变量 [重复]

    这个问题在这里已经有答案了 可能的重复 在 Perl 中将文件转换为字符串的最佳方法是什么 https stackoverflow com questions 206661 what is the best way to slurp a f
  • jquery是如何实现$(document).ready()的?

    jquery是如何实现的 document ready 当然我可以阅读代码 但我正在寻找这个概念 概念 jQuery ready http api jquery com ready 虽然 JavaScript 提供了在渲染页面时执行代码的
  • C# '+=' 运算符

    我有一些包含运算符 的代码 具体来说 代码如下 foreach KeyValuePair
  • 在 Google Analytics 中跟踪添加到主屏幕 Web 应用程序

    我有一个移动网络应用程序 其 添加到主屏幕 功能运行良好 我正在尝试确定如何使用 Google Analytics 来最好地跟踪已将应用程序安装到主屏幕的用户的应用程序使用情况 似乎没有默认方法可以在分析中查看应用程序是否处于独立模式 Go
  • css 中的嵌套边框布局?

    我这个工作正常 http jsfiddle net uwcEw http jsfiddle net uwcEw CSS 上的传统边框布局 我想要做的是在边框布局的 中间 内有另一个 5 区域边框布局 重新应用上面相同的模式我得到这个 htt
  • 使用 Angular 将井号作为 URL 查询字符串中的值传递

    我有一个输入标签 在输入时它将根据输入值过滤表中的数据列表 该值通过请求 URL 中的查询字符串传递 通常我会得到返回的数据并且表会被适当更新 但是 当搜索井号 时 我收到 500 内部服务器错误 我的问题是 在查询字符串中传递井号时 An
  • Bigtable数据库设计理论

    我非常精通关系数据库设计的理论和实践 我知道什么有效 什么无效 什么是高性能的 什么是可维护的 几乎 当您开始拥有真实数据时 总是有需要调整的地方 看来我找不到关于分布式可扩展数据库的大量知识 例如 Google 的 Bigtable 用于
  • MATLAB 图像锐化 - 使用(1-高斯低通滤波器)的高斯高通滤波器

    我试图通过设计高斯高通滤波器来锐化图像 我想利用高通滤波器相当于单位矩阵减去低通滤波器的事实来做到这一点 所以我执行了以下操作 image imread Question3 Data Cats jpg read image H 1 fspe
  • 在 sendmultiple 上使用 dropzone.js 发送 formData

    dropzone js 文档 wiki 没有说明如何发送表单字段 我刚刚读到表单数据对象 https developer mozilla org en US docs Web API FormData FormData它说明了如何使用表单字
  • 不同元素类型的名称属性的 jQuery 选择器

    我有一个行表 在其中一列中 某些行具有包含静态文本的跨度 某些行具有可供选择的值的选择 该一列中的所有元素都具有相同的名称属性 在我的表单提交中 我遍历行并希望获取所有列的值 我希望有一个 jQuery 选择器语句来从该元素获取值 span
  • Django ModelForm 不保存数据

    我已经尝试过以下帖子中的解决方案 从 ModelForm 保存数据 https stackoverflow com questions 13046488 saving data from modelform 没起作用 ModelForm数据
  • 识别 IR 中的阵列类型

    我一直在尝试使用以下代码来识别 IR 中的数组访问 for BasicBlock iterator ii BB gt begin ii2 ii BB gt end ii Instruction I ii if GetElementPtrIn
  • iPhone 中的应用程序堆大小是多少?

    HI iPhone 中的应用程序堆大小是多少 我的意思是应用程序可以使用 iPhone 中的内存大小 对于 ipod 来说是一样的还是对于 iPhone 或 iPod 来说是不同的 我们可以使用 NSdata 在我们的应用程序中下载的 iP
  • NGINX 与 Tomcat 配置

    我是 Nginx 新手 我需要你的帮助 根据很多论坛我了解到我们所有的静态页面都存储在Nginx中 当有请求到来时 我必须将该请求传递给 tomcat 获取数据 并在 tomcat 生成响应后生成响应 目前 我刚刚做到了 我将请求直接传递给
  • 编码标准维基

    在我的工作地点 我负责创建编码标准文档 一般来说 我们在某种程度上遵循 FxCop 和 StyleCop 工具报告的内容 但我们真正需要的是解释何时使用约定的文档 为什么使用约定 甚至可能是一个简单的示例 将来也可以将其扩展用于其他目的 我
  • pipx 如何知道要使用哪个 Python 版本?

    我是一个坚强的pyenv and poetry开始使用的用户pipx并希望了解更多有关其工作原理的信息 具体来说 我想了解它如何确定安装时使用哪个Python版本 我注意到它似乎在搜索PATH现有的应用程序参考 当您pipx install
  • ASP.NET Core [要求] 不可为 null 的类型

    Here https stackoverflow com questions 6662976 required attribute for an integer value 提出了如何验证不可为空的必需类型的问题 在我的情况下 提供的使字段
  • fbtorch:cmake 找不到 torch 目录

    我正在尝试构建并安装 fbtorch 但是当我使用 cmake 时出现以下错误 CMake Error at CMakeLists txt 9 FIND PACKAGE By not providing FindTorch cmake in
  • 为什么这个“std::atomic_thread_fence”起作用

    首先我想谈一下我对此的一些理解 如有错误请指正 a MFENCE在x86中可以保证全屏障 顺序一致性可防止 STORE STORE STORE LOAD LOAD STORE 和 LOAD LOAD 重新排序 这是根据维基百科 https