相关疑难解决方法(0)

哪个是x86上更好的写屏障:lock + addl还是xchgl?

Linux内核lock; addl $0,0(%%esp)用作写屏障,而RE2库xchgl (%0),%0用作写屏障.有什么区别,哪个更好?

x86还需要读屏障指令吗?RE2将其读屏障功能定义为x86上的无操作,而Linux lfence根据SSE2是否可用将其定义为无操作或无操作.什么时候lfence需要?

x86 assembly memory-barriers

23
推荐指数
4
解决办法
7771
查看次数

x86上的原子性

8.1.2总线锁定

Intel 64和IA-32处理器提供LOCK#信号,该信号在某些关键存储器操作期间自动置位,以锁定系统总线或等效链路.当该输出信号被断言时,来自其他处理器或总线代理的用于控制总线的请求被阻止.软件可以指定在遵循LOCK语义的其他情况下将LOCK前缀添加到指令之前.

它来自英特尔手册,第3卷

听起来内存上的原子操作将直接在内存(RAM)上执行.我很困惑,因为当我分析装配输出时,我看到"没什么特别的".基本上,生成的汇编输出std::atomic<int> X; X.load()只会产生"额外"的影响.但是,它负责正确的内存排序,而不是原子性.如果我理解得X.store(2)恰到好处mov [somewhere], $2.就这样.它似乎没有"跳过"缓存.我知道将对齐(例如int)移动到内存是原子的.但是,我很困惑.


所以,我提出了疑问,但主要问题是:

CPU如何在内部实现原子操作?

c++ x86 multithreading atomic memory-barriers

18
推荐指数
1
解决办法
5684
查看次数

我可以直接对原子变量进行算术运算吗?

我可以直接对原子变量进行算术运算吗?

因为我发现 C 标准库提供了很多实用函数,例如atomic_fetch_add执行原子变量和非原子变量之间的加法。但是,我很好奇,由于变量是原子的,我可以直接对其进行算术运算吗?就像下面所示的代码一样:

#include <threads.h>
#include <stdio.h>
#include <stdatomic.h>
atomic_int i = 0;
int run(void* v) {
  i += 100;  // <- is this operaiton thread-safe?
  // atomic_fetch_add(&i, 100);
  printf("%d\n", i);
  return thrd_success;
}
int main(void) {
  thrd_t thread;  
  thrd_create(&thread, run, NULL);
  thrd_join(thread, NULL);
  return 0; 
} 
Run Code Online (Sandbox Code Playgroud)

c multithreading atomic language-lawyer stdatomic

12
推荐指数
2
解决办法
1260
查看次数

为什么GCC使用mov/mfence而不是xchg来实现C11的atomic_store?

C++和2012之后:Herb Sutter - 原子<>武器, Herb Sutter中的2个(约0:38:20)认为应该使用xchg,而不是mov/ 在x86 mfence上实现atomic_store.他似乎也暗示这个特定的指令序列是每个人都同意的.但是,海湾合作委员会使用后者.为什么GCC使用这个特定的实现?

c gcc atomic memory-barriers

11
推荐指数
1
解决办法
752
查看次数

如何使用LOCK ASM前缀读取值?

我知道如何使用LOCK线程安全地增加一个值:

  lock inc     [J];
Run Code Online (Sandbox Code Playgroud)

但是我如何以线程安全的方式阅读[J](或任何值)?LOCK前缀不能与mov一起使用.如果我做以下事情:

  xor eax, eax;
  lock add eax, [J];
  mov [JC], eax;
Run Code Online (Sandbox Code Playgroud)

它在第2行引发错误.

x86 assembly locking thread-safety

6
推荐指数
1
解决办法
5649
查看次数

加载和存储是否只有重新排序的指令?

我已经阅读了很多关于内存排序的文章,并且所有这些文章都只说CPU重新加载和存储.

CPU(我对x86 CPU特别感兴趣)是否仅重新排序加载和存储,并且不重新排序它具有的其余指令?

x86 cpu-architecture memory-barriers

6
推荐指数
2
解决办法
915
查看次数

x86 mfence和C ++内存屏障

我正在检查编译器如何为x86_64上的多核内存屏障发出指令。以下代码是我正在测试的代码gcc_x86_64_8.3

std::atomic<bool> flag {false};
int any_value {0};

void set()
{
  any_value = 10;
  flag.store(true, std::memory_order_release);
}

void get()
{
  while (!flag.load(std::memory_order_acquire));
  assert(any_value == 10);
}

int main()
{
  std::thread a {set};
  get();
  a.join();
}
Run Code Online (Sandbox Code Playgroud)

使用时std::memory_order_seq_cst,我可以看到该MFENCE指令用于任何优化-O1, -O2, -O3。该指令确保刷新了存储缓冲区,因此在L1D缓存中更新了它们的数据(并使用MESI协议确保其他线程可以看到效果)。

但是,当我std::memory_order_release/acquire不进行优化MFENCE使用时,也会使用指令,但是使用-O1, -O2, -O3优化会忽略该指令,并且不会看到其他刷新缓冲区的指令。

MFENCE不使用的情况下,如何确保将存储缓冲区数据提交给高速缓存以确保内存顺序语义?

以下是使用get / set函数的汇编代码-O3,例如我们在Godbolt编译器资源管理器中获得的代码

set():
        mov     DWORD PTR any_value[rip], 10
        mov     BYTE PTR flag[rip], 1
        ret


.LC0:
        .string …
Run Code Online (Sandbox Code Playgroud)

x86 gcc memory-model memory-barriers c++11

4
推荐指数
2
解决办法
470
查看次数

全局不可见的加载说明

由于存储负载转发,某些负载指令能否在全局范围内不可见?换句话说,如果加载指令从存储缓冲区中获取其值,则它永远不必从高速缓存中读取。
通常说来,当从L1D缓存读取负载时,该负载在全局范围内可见,因此,未从L1D读取的负载应使其在全局上不可见。

cpu-architecture memory-barriers cpu-cache

3
推荐指数
1
解决办法
297
查看次数