相关疑难解决方法(0)

SSE指令:哪些CPU可以进行原子16B内存操作?

考虑在x86 CPU上进行单个内存访问(单个读取或单个写入,而不是读取或写入)SSE指令.该指令访问16字节(128位)的存储器,访问的存储器位置对齐为16字节.

文档"英特尔®64架构内存订购白皮书"指出,对于"读取或写入地址在8字节边界上对齐的四字(8字节)的指令",内存操作似乎作为单个内存访问执行,而不管记忆类型.

问题:是否存在Intel/AMD/etc x86 CPU,它们保证读取或写入与16字节边界对齐的16字节(128位)作为单个内存访问执行?是这样,它是哪种特定类型的CPU(Core2/Atom/K8/Phenom/...)?如果您对此问题提供答案(是/否),请同时指定用于确定答案的方法 - PDF文档查找,强力测试,数学证明或您用于确定答案的任何其他方法.

此问题涉及http://research.swtch.com/2010/02/off-to-races.html等问题


更新:

我在C中创建了一个可以在您的计算机上运行的简单测试程序.请在您的Phenom,Athlon,Bobcat,Core2,Atom,Sandy Bridge或您碰巧拥有的任何支持SSE2的CPU上编译并运行它.谢谢.

// Compile with:
//   gcc -o a a.c -pthread -msse2 -std=c99 -Wall -O2
//
// Make sure you have at least two physical CPU cores or hyper-threading.

#include <pthread.h>
#include <emmintrin.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

typedef int v4si __attribute__ ((vector_size (16)));
volatile v4si x;

unsigned n1[16] __attribute__((aligned(64)));
unsigned n2[16] __attribute__((aligned(64)));

void* thread1(void *arg) {
        for (int i=0; i<100*1000*1000; i++) { …
Run Code Online (Sandbox Code Playgroud)

concurrency x86 sse atomic thread-safety

30
推荐指数
3
解决办法
7100
查看次数

为什么在x86上对自然对齐的变量进行整数赋值?

我一直在读这篇关于原子操作的文章,它提到了32位整数赋值在x86上是原子的,只要该变量是自然对齐的.

为什么自然对齐确保原子性?

c c++ concurrency x86 atomic

28
推荐指数
2
解决办法
5386
查看次数

将原子类型的指针分配给非原子类型的指针

该代码的行为是否定义明确?

#include <stdatomic.h>

const int test = 42;
const int * _Atomic atomic_int_ptr;
atomic_init(&atomic_int_ptr, &test);
const int ** int_ptr_ptr = &atomic_int_ptr;
printf("int = %d\n", **int_ptr_ptr); //prints int = 42
Run Code Online (Sandbox Code Playgroud)

我将原子类型的指针分配给非原子类型的指针(类型相同)。这是我对这个示例的看法:

标准中明确规定的区别constvolatilerestrict从预选赛_Atomic资格赛6.2.5(p27)

只要允许一种类型的原子版本以及该类型的其他合格版本,本标准就明确使用短语“原子,限定或不限定类型”。在没有具体提及原子的情况下,短语“合格或不合格类型”不包括原子类型。

合格类型的兼容性也定义为6.7.3(p10)

为了使两个合格的类型兼容,两个都应具有相同的合格类型的兼容类型;指定符或限定符列表中类型限定符的顺序不会影响指定的类型。

结合以上引用的引言,我得出结论,原子类型和非原子类型是兼容的类型。因此,应用简单分配规则6.5.16.1(p1)(emp.mine):

左操作数具有原子,合格或不合格的指针类型,并且(考虑到左操作数在左值转换后将具有的类型), 两个操作数都是指向 兼容类型的合格或不合格版本的指针,并且由左侧指向的类型具有全部右边指出的类型的限定词;

因此,我得出的结论是,行为已得到很好的定义(即使将原子类型分配给非原子类型也是如此)。

所有这些的问题是,应用上述规则,我们还可以得出结论:将非原子类型简单分配给原子类型也定义得很好,这显然是不正确的,因为我们atomic_store为此拥有专用的泛型函数。

c concurrency language-lawyer c11 stdatomic

11
推荐指数
2
解决办法
387
查看次数

大于CPU原生支持的原子如何工作

使用当前的 C++ 编译器,您可以获得比 CPU 实际支持更大的原子支持。使用 x64,您可以拥有 16 字节的原子,但 std::atomic 也适用于更大的元组。看这段代码:

#include <iostream>
#include <atomic>

using namespace std;

struct S { size_t a, b, c; };

atomic<S> apss;

int main()
{
    auto ref = apss.load( memory_order_relaxed );
    apss.compare_exchange_weak( ref, { 123, 456, 789 } );
    cout << sizeof ::apss << endl;
}
Run Code Online (Sandbox Code Playgroud)

对于我的平台,上面的 cout 总是打印 32。但是,如果没有互斥体,这些事务实际上是如何工作的呢?我从检查拆卸中没有得到任何线索。

如果我使用 MSVC++ 运行以下代码:

#include <atomic>
#include <thread>
#include <array>

using namespace std;

struct S { size_t a, b, c, d, e; };

atomic<S> apss;

int …
Run Code Online (Sandbox Code Playgroud)

c++ c++11 stdatomic

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

非原子类型如何实现std :: atomic_ref?

我想知道如何才能std::atomic_ref有效地实现std::mutex非原子对象(每个对象一个),因为以下属性似乎很难实施:

相对于通过引用同一对象的任何其他atomic_ref施加的原子操作,通过atomic_ref施加到对象的原子操作是原子的。

特别是以下代码:

void set(std::vector<Big> &objs, size_t i, const Big &val) {
    std::atomic_ref RefI{objs[i]};
    RefI.store(val);
}
Run Code Online (Sandbox Code Playgroud)

似乎很难实现,因为std::atomic_ref每次都需要以某种方式进行选择std::mutex(除非这是由相同类型的所有对象共享的大主锁)。

我想念什么吗?还是每个对象都有责任实施std::atomic_ref,因此要么是原子的要么携带一个std::mutex

c++ stdatomic c++20 stdmutex

8
推荐指数
2
解决办法
102
查看次数

在实践中,无锁原子是否无地址?

Boost.Interprocess是一个很棒的库,可以简化不同进程之间共享内存的使用.它提供互斥锁,条件变量和信号量,允许在从共享内存中写入和读取时进行同步.

但是,在某些情况下,这些(相对)性能密集型同步机制不是必需的 - 原子操作足以满足我的用例,并且可能会提供更好的性能.

不幸的是,Boost.Interprocess似乎没有原子.


C++标准库提供了std::atomic类模板,它封装了操作需要是原子的对象,还具有测试原子操作是否无锁的功能.但它也不要求无锁原子也不需要地址:[atomics.lockfree]/4只是鼓励无锁操作无地址,这与cppreference一致.

我想不出为什么会以非地址方式实现无锁原子的任何理由.在我看来,以无地址的方式实现无锁原子相当容易.

因为在使用原子而不是互斥体(来自Boost.Interprocess)时我会获得显着的性能优势,所以在这里折扣标准兼容性并将std::atomic对象存储在共享内存中似乎很诱人.


这个问题分为两部分:

  1. 在实践中,CPU是否以无地址方式实现无锁原子?(我只关心用于运行现代桌面和移动操作系统的CPU(例如Windows,MacOS,Linux,Android,iOS),但不关心嵌入式系统)
  2. 为什么实现使用无锁地址的无地址原子?

c++ atomic shared-memory lock-free c++17

7
推荐指数
1
解决办法
660
查看次数

互斥体如何工作?互斥体是否可以全局保护变量?它的定义范围重要吗?

互斥锁是否全局锁定对变量的访问,或者仅锁定与锁定互斥锁相同范围内的变量的访问?

请注意,我必须更改这个问题的标题,因为很多答案似乎对我的问题感到困惑。这不是关于“互斥体对象”的范围(全局或其他)的问题,而是关于互斥体“锁定”变量的范围的问题。

我相信答案是互斥体锁定对所有变量的访问,即;所有全局和局部范围的变量。(这是互斥体阻塞线程执行而不是访问特定内存区域的结果。)

我正在尝试理解互斥体。

我试图了解互斥体将锁定内存的哪些部分,或者等效地,哪些变量。

然而,我从网上阅读的理解是,互斥体不会锁定内存,它们会锁定(或阻止)同时运行的线程,这些线程都是同一进程的成员。(那是对的吗?)

https://mortoray.com/2011/12/16/how-does-a-mutex-work-what-does-it-cost/

所以我的问题就变成了“互斥体是全局的吗?”

...或者它们可能“一般来说是全球性的,但 stackoverflow 社区可以想象一些特殊情况,但它们不是?”

最初考虑我的问题时,我对以下示例所示的内容感兴趣。

// both in global scope, this mutex will lock any global scope variable?
int global_variable;
mutex global_variable_mutex;

int main()
{
    // one thread operates here and locks global_variable_mutex
    // before reading/writing

    {
        // local variables in a loop
        // launch some threads here, and wait later
        int local_variable;
        mutex local_variable_mutex;
        // wait for launched thread to return

        // does the mutex here prevent data …
Run Code Online (Sandbox Code Playgroud)

parallel-processing multithreading mutex atomic

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

当缓存行大小通常为 64 字节时,为什么 sizeof std::mutex == 40

在 static_assert 之后,gcc 和 clang 主干都通过了。

#include<mutex>
int main(){
    static_assert(sizeof(std::mutex)==40);
}
Run Code Online (Sandbox Code Playgroud)

由于 x86 CPU 有 64 字节缓存线,我期望互斥锁 sizeof 为 64,因此可以避免错误共享。大小“仅”40 字节是否有原因?

注意:我知道大小也会影响性能,但程序中很少有大量互斥体,因此与错误共享的成本相比,大小开销似乎可以忽略不计。

注意:有一个类似的问题问为什么 std::mutex 这么大,我问为什么它这么小:)

编辑:MSVC 16.7 的大小为 80。

c++ optimization x86 false-sharing stdmutex

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

T、volatile T 和 std::atomic&lt;T&gt; 之间有什么区别?

考虑下面的示例是打算等到另一个线程存储42在一个共享变量shared没有锁,无需等待线程终止,为什么会volatile Tstd::atomic<T>会要求或建议,以保证并发正确性?

#include <atomic>
#include <cassert>
#include <cstdint>
#include <thread>

int main()
{
  int64_t shared = 0;
  std::thread thread([&shared]() {
    shared = 42;
  });
  while (shared != 42) {
  }
  assert(shared == 42);
  thread.join();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用 GCC 4.8.5 和默认选项,示例按预期工作。

c++ concurrency multithreading c++11 stdatomic

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

升级到MacPorts gcc 7.3后,is_lock_free()返回false

以前,使用Apple LLVM 9.1.0,128 is_lock_free()位结构已经恢复正常.为了得到完全的std::optional支持,我随后升级到了MacPorts gcc 7.3.在我第一次尝试编译时,我遇到了这个臭名昭着的showstopper链接器错误:

Undefined symbols for architecture x86_64:
  "___atomic_compare_exchange_16", referenced from:
Run Code Online (Sandbox Code Playgroud)

我知道我可能需要添加-latomic.使用Apple LLVM 9.1.0,我不需要它,我对此非常不满意.如果它是无锁的,您通常不需要链接到任何其他库,单独的编译器就能够处理它.否则,它可能只是基于锁的,需要来自其他库的支持.正如我所担心的那样,添加后-latomic,构建成功,但is_lock_free()返回false.

我认为gcc 7.3及其标准库实现很好.这可能只是我身上的一些配置问题.事实上,我没有做任何配置.我只是安装了MacPorts gcc并完成了.知道我可能缺少什么吗?

c++ gcc atomic lock-free gcc7

5
推荐指数
1
解决办法
272
查看次数