相关疑难解决方法(0)

我什么时候应该使用_mm_sfence _mm_lfence和_mm_mfence

我阅读了"英特尔架构的英特尔优化指南指南".

但是,我仍然不知道何时应该使用

_mm_sfence()
_mm_lfence()
_mm_mfence()
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释在编写多线程代码时何时应该使用它们?

c++ x86 multithreading intrinsics memory-barriers

14
推荐指数
3
解决办法
6488
查看次数

为什么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 xchg与mfence具有相同的行为吗?

我想知道的是lock xchg,mfence从一个线程访问内存位置的角度来看是否会有类似的行为,这个内存位置正在被其他线程进行变异(让我们随便说).它能保证我获得最新的价值吗?之后的内存读/写指令?

我混淆的原因是:

8.2.2"读取或写入不能通过I/O指令,锁定指令或序列化指令重新排序."

-Intel 64 Developers Manual Vol.3

这是否适用于线程?

mfence 状态:

对MFENCE指令之前发出的所有内存加载和存储到内存指令执行序列化操作.此序列化操作保证在MFENCE指令之前的任何加载或存储指令全局可见之前,在程序顺序之前的每条加载和存储指令都是全局可见的.MFENCE指令针对所有加载和存储指令,其他MFENCE指令,任何SFENCE和LFENCE指令以及任何序列化指令(例如CPUID指令)进行排序.

-Intel 64 Developers Manual Vol 3A

听起来更有力.因为它听起来mfence几乎正在刷写写缓冲区,或者至少延伸到写缓冲区和其他内核以确保我未来的加载/存储是最新的.

当基准标记时,两个指令都需要约100个循环才能完成.所以我无论如何都看不出那么大的差异.

主要是我只是困惑.我的指令基于lock互斥体使用,但后来这些包含没有内存栅栏.然后,我看到锁免费使用内存栅栏编程,但没有锁.我知道AMD64有一个非常强大的内存模型,但过时的值可以在缓存中持续存在.如果lock行为与行为不同,mfence那么互斥量如何帮助您查看最新值?

x86 assembly multithreading cpu-architecture memory-barriers

10
推荐指数
1
解决办法
1145
查看次数

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

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

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

x86 cpu-architecture memory-barriers

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

Intel指令的LOCK前缀.有什么意义?

我阅读了英特尔手册,发现指令有一个锁定前缀,可以防止处理器同时写入同一个内存位置.我很兴奋.我想它可以用作硬件互斥.所以我写了一段代码来拍摄.结果非常令人沮丧.锁不支持MOV或LEA指令.手册说LOCK仅支持ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCH8B,DEC,INC,NEG,NOT,OR,SBB,SUB,XOR,XADD和XCHG.而且,如果LOCK前缀与这些指令之一一起使用并且源操作数是存储器操作数,则可以生成未定义的操作码异常(#UD).

我想知道为什么这么多限制,如此多的限制使得LOCK看起来毫无用处.我不能用它来保证一般的写操作没有脏数据或并行引起的其他问题.

例如,我在C中编写了代码++(*p).p是指向共享内存的指针.相应的程序集如下:

movl    28(%esp), %eax
movl    (%eax), %eax
leal    1(%eax), %edx
movl    28(%esp), %eax
movl    %edx, (%eax)
Run Code Online (Sandbox Code Playgroud)

我在"movl"和"leal"之前添加了"lock",但是处理器抱怨"无效指令".:-(我想将序列化写操作的唯一方法是使用软件互斥,对吧?

c linux parallel-processing assembly intel

5
推荐指数
2
解决办法
9913
查看次数

Linux 内核 flush_write_buffers() 如何在 x86 上工作?

以下代码来自include/asm-i386/io.h,并且是从调用的dma_map_single()。我的理解是flush_write_buffers()应该在为 DMA 映射内存之前刷新 CPU 内存缓存。但是这个汇编代码是如何刷新 CPU 缓存的呢?

static inline void flush_write_buffers(void)
{
    __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory");
}
Run Code Online (Sandbox Code Playgroud)

c x86 assembly linux-kernel dma

5
推荐指数
2
解决办法
276
查看次数

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

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

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

    这是根据维基百科的说法。

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

    这是根据Alex 的回答,“负载可能会通过早期存储重新排序到不同位置”(对于 x86),并且 mfence 不会总是被添加。

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

    1. 至少一个显式MFENCE添加到任一LOADSTORE
    2. LOAD(无栅栏)和 LOCK XCHG
    3. LOCK XADD ( 0 ) 和 STORE (无栅栏)

    否则仍有可能重新订购。

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

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

    这是根据Alex的回答。所以我总是可以替换asm volatile("mfence" ::: "memory")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)
    {   /* …
Run Code Online (Sandbox Code Playgroud)

c++ x86 memory-barriers stdatomic

4
推荐指数
1
解决办法
2212
查看次数

为什么具有顺序一致性的std :: atomic存储使用XCHG?

为什么是std::atomic store:

std::atomic<int> my_atomic;
my_atomic.store(1, std::memory_order_seq_cst);
Run Code Online (Sandbox Code Playgroud)

xchg请求具有顺序一致性的商店时执行?


从技术上讲,具有读/写内存屏障的普通商店不应该足够吗?相当于:

_ReadWriteBarrier(); // Or `asm volatile("" ::: "memory");` for gcc/clang
my_atomic.store(1, std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)

我明确地谈论x86和x86_64.商店有隐含的获取围栏.

c++ x86 assembly lock-free stdatomic

4
推荐指数
1
解决办法
683
查看次数

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
查看次数

为什么我们需要读和写屏障?

为什么我们需要定义两种具有相同实现的障碍?

例如,io_uringLinux 中的这段代码:

#if defined(__x86_64) || defined(__i386__)
#define read_barrier()  __asm__ __volatile__("":::"memory")
#define write_barrier() __asm__ __volatile__("":::"memory")
#else
Run Code Online (Sandbox Code Playgroud)

c x86 gcc linux-kernel memory-barriers

2
推荐指数
1
解决办法
661
查看次数

在没有 XCHG 的情况下实现自旋锁?

C++ 自旋锁可以使用std::atomic_flag轻松实现,它可以粗略地编码(没有特殊功能),如下所示:

std::atomic_flag f = ATOMIC_FLAG_INIT;

while (f.test_and_set(std::memory_order_acquire)); // Acquire lock
// Here do some lock-protected work .....
f.clear(std::memory_order_release); // Release lock
Run Code Online (Sandbox Code Playgroud)

可以看到在线汇编,说明获取是通过XCHG指令原子实现的。

另外,正如人们在 uops.info此处的屏幕)上看到的那样,XCHG 可能会占用30相当流行的 Skylake 上的 CPU 周期。这是相当慢的。

通过这样的程序可以测量自旋锁的整体速度。

是否可以在没有 XCHG 的情况下实现自旋锁定?主要关心的是速度,而不仅仅是使用另一条指令。

最快的自旋锁是什么?是否可以将其改为 10 个周期而不是 30 个?还有5个周期?也许是一些平均运行速度很快的概率自旋锁?

它应该以严格的方式实施,这意味着在 100% 的情况下它可以正确保护代码和数据。如果它是概率性的,那么它应该运行可能的时间,但每次运行后仍能 100% 正确地保护。

对我来说,这种自旋锁的主要目的是保护多个线程内非常小的操作,这些操作运行十几个或两个周期,因此 30 个周期的延迟太大了。当然可以说我可以使用原子或其他无锁技术来实现所有操作。但这种技术并不适用于所有情况,并且还需要花费大量工作才能在许多类和方法的庞大代码库中严格实现。因此,还需要一些通用的东西,比如常规的自旋锁。

c++ performance x86-64 atomic spinlock

2
推荐指数
1
解决办法
783
查看次数