- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我遇到了一个问题,MS C 编译器重新排序某些语句,这在多线程上下文中是高优化级别的关键。我想知道如何在特定位置强制排序,同时仍然使用高级优化。 (在低优化级别,此编译器不会重新排序语句)
以下代码:
ChunkT* plog2sizeChunk=...
SET_BUSY(plog2sizeChunk->pPoolAndBusyFlag); // set "busy" bit on this chunk of storage
x = plog2sizeChunk->pNext;
产生这个:
0040130F 8B 5A 08 mov ebx,dword ptr [edx+8]
00401312 83 22 FE and dword ptr [edx],0FFFFFFFEh
其中对 pPoolAndBusyFlag 的写入由编译器重新排序以发生在 pNext 提取之后。
SET_BUSY 本质上是
plog2sizeChunk->pPoolAndBusyFlag&=0xFFFFFFFeh;
我认为编译器正确地决定重新排序这些访问是可以的,因为它们是针对同一结构的两个独立成员,并且这种重新排序对单线程执行的结果没有影响:
typedef struct chunk_tag{
unsigned pPoolAndBusyFlag; // Contains pointer to owning pool and a busy flag
natural log2size; // holds log2size of the chunk if Busy==false
struct chunk_tag* pNext; // holds pointer to next block of same size
struct chunk_tag* pPrev; // holds pointer to previous block of same size
} ChunkT, *pChunkT;
为了我的目的,必须先设置 pPoolAndBusyFlag,然后才能在多线程/多核上下文中对该结构的其他访问有效。我不认为这个特定访问对我来说是有问题的,但编译器可以重新排序这一事实意味着我的代码的其他部分可能有相同类型的重新排序,但它可能对那些地方持批评态度。 (假设这两个语句是对两个语句的更新成员而不是一个写/一个读)。我希望能够强制执行操作的顺序。
理想情况下,我会这样写:
plog2sizeChunk->pPoolAndBusyFlag&=0xFFFFFFFeh;
#pragma no-reordering // no such directive appears to exist
pNext = plog2sizeChunk->pNext;
我已经通过实验验证了我可以用这种丑陋的方式获得这种效果:
plog2sizeChunk->pPoolAndBusyFlag&=0xFFFFFFFeh;
asm { xor eax, eax } // compiler won't optimize past asm block
pNext = plog2sizeChunk->pNext;
给予
0040130F 83 22 FE and dword ptr [edx],0FFFFFFFEh
00401312 33 C0 xor eax,eax
00401314 8B 5A 08 mov ebx,dword ptr [edx+8]
我注意到 x86 硬件可能会重新排序这些特定指令,因为它们不引用相同的内存位置,并且读取可能会通过写入;要真正修复这个 示例,我需要某种类型的内存屏障。回到我之前的评论,如果它们都是写入,x86 不会对它们重新排序,写入顺序将被其他线程看到。所以在那种情况下,我认为我不需要内存屏障,只需要强制排序。
我还没有看到编译器重新排序两次写入(还),但我(还)没有仔细寻找;我刚刚被这个绊倒了。当然,仅仅因为您在此编译中看不到它而进行优化并不意味着它不会出现在下一个编译中。
那么,我如何强制编译器对它们进行排序?
我知道我可以将结构中的内存槽声明为可变的。它们仍然是独立 存储位置,所以我看不出这如何阻止优化。也许我误解了 volatile 的含义?
编辑(10 月 20 日):感谢所有响应者。我当前的实现使用 volatile(用作初始解决方案)、_ReadWriteBarrier(标记编译器不应发生重新排序的代码)和一些 MemoryBarriers(发生读写的地方),这似乎已经解决了问题.
编辑:(11 月 2 日):为了简洁起见,我最终为 ReadBarrier、WriteBarrier 和 ReadWriteBarrier 定义了一组宏。有用于前后锁定、前后解锁和一般用途的设置。其中一些是空的,一些包含 _ReadWriteBarrier 和 MemoryBarrier,适用于 x86 和基于 XCHG 的典型自旋锁 [XCHG 包含一个隐式 MemoryBarrier,因此避免了对锁前/后设置的需求)。然后我将这些放在代码中的适当位置,记录必要的(非)重新排序要求。
最佳答案
据我所知,pNext = plog2sizeChunk->pNext
发布该 block ,以便其他线程可以看到它,您必须确保它们看到正确的忙碌标志。
这意味着在发布它之前您需要一个单向内存屏障(在另一个线程中读取它之前也需要一个,尽管如果您的代码在 x86 上运行,您可以免费获得这些屏障)以确保线程实际上看到了变化。在写入之前还需要一个,以避免在它之后重新排序写入。仅仅插入程序集或使用符合标准的 volatile(MSVC volatile 提供额外的保证,尽管这在这里有所不同)是不够的 - 是的,这会阻止编译器移动读写,但 CPU不受其约束,可以在内部进行相同的重新排序。
MSVC 和 gcc 都有内部函数/宏来创建内存屏障 (see eg here)。 MSVC 还为足以解决您的问题的 volatile 提供更强的保证。最后,C++11 原子也可以工作,但我不确定 C 本身是否有任何可移植的方式来保证内存屏障。
关于c - 强制执行 C 语句的顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19292075/
我有一个“有趣”的问题,即以两种不同的方式运行 wine 会导致: $> wine --version /Applications/Wine.app/Contents/Resources/bin/wi
我制作了这个网络抓取工具来获取网页中的表格。我使用 puppeteer (不知道 crontab 有问题)、Python 进行清理并处理数据库的输出 但令我惊讶的是,当我执行它时 */50 * * *
JavaScript 是否被调用或执行取决于什么?准确地说,我有两个函数,它们都以相同的方式调用: [self.mapView stringByEvaluatingJavaScriptFromStri
我目前正在使用 python 做一个机器学习项目(这里是初学者,从头开始学习一切)。 只是想知道 statsmodels 的 OLS 和 scikit 的 PooledOlS 使用我拥有的相同面板数据
在使用集成对象模型 (IOM) 后,我可以执行 SAS 代码并将 SAS 数据集读入 .Net/C# 数据集 here . 只是好奇,使用 .Net 作为 SAS 服务器的客户端与使用 Enterpr
有一些直接的 jQuery 在单击时隐藏打开的 div 未显示,但仍将高度添加到导航中以使其看起来好像要掉下来了。 这个脚本工作正常: $(document).ready(funct
这个问题已经有答案了: How do I compare strings in Java? (23 个回答) 已关闭 4 年前。 这里是 Java 新手,我正在使用 NetBeans 尝试一些简单的代
如果我将它切换到 Python 2.x,它执行 10。这是为什么? 训练逻辑回归模型 import keras.backend as
我有两个脚本,它们包含在 HTML 正文中。在第一个脚本中,我初始化一个 JS 对象,该对象在第二个脚本标记中引用。 ... obj.a = 1000; obj.
每当我运行该方法时,我都会收到一个带有数字的错误 以下是我的代码。 public String getAccount() { String s = "Listing the accounts";
我已经用 do~while(true) 创建了我的菜单;但是每次用户输入一个数字时,它不会运行程序,而是再次显示菜单!你怎么看? //我的主要方法 public static void main(St
执行命令后,如何让IPython通知我?我可以使用铃声/警报还是通过弹出窗口获取它?我正在OS X 10.8.5的iTerm上运行Anaconda。 最佳答案 使用最新版本的iTerm,您可以在she
您好,我刚刚使用菜单栏为 Swing 编写了代码。但是问题出现在运行中。我输入: javac Menu.java java Menu 它没有给出任何错误,但 GUI 没有显示。这是我的源代码以供引用:
我觉得这里缺少明显的东西,但是我看不到它写在任何地方。 我使用Authenticode证书对可执行文件进行签名,但是当我开始学习有关它的更多信息时,我对原样的值(value)提出了质疑。 签名的exe
我正在设计一个应用程序,它使用 DataTables 中的预定义库来创建数据表。我想对数据表执行删除操作,为此应在按钮单击事件上执行 java 脚本。 $(document).ready(functi
我是 Haskell 新手,如果有人愿意帮助我,我会很高兴!我试图让这个程序与 do while 循环一起工作。 第二个 getLine 命令的结果被放入变量 goGlenn 中,如果 goGlenn
我有一个用 swing 实现迷你游戏的程序,在主类中我有一个循环,用于监听游戏 map 中的 boolean 值。使用 while 实现的循环不会执行一条指令,如果它是唯一的一条指令,我不知道为什么。
我正在尝试开发一个连接到 Oracle 数据库并执行函数的 Java 应用程序。如果我在 Eclipse 中运行该应用程序,它可以工作,但是当我尝试在 Windows 命令提示符中运行 .jar 时,
我正在阅读有关 Java 中的 Future 和 javascript 中的 Promises 的内容。下面是我作为示例编写的代码。我的问题是分配给 future 的任务什么时候开始执行? 当如下行创
我有一个常见的情况,您有两个变量(xSpeed 和 ySpeed),当它们低于 minSpeed 时,我想将它们独立设置为零,并在它们都为零时退出。 最有效的方法是什么?目前我有两种方法(方法2更干净
我是一名优秀的程序员,十分优秀!