标签: false-sharing

虚假共享和堆栈变量

我有一些小但经常使用的函数对象.每个线程都有自己的副本.一切都是静态分配的.副本不共享任何全局或静态数据.我是否需要保护此对象免遭错误共享?

谢谢.编辑:这是一个使用Boost.Threads的玩具程序.字段数据可能会发生错误共享吗?

#include <boost/thread/thread.hpp>

struct Work {
    void operator()() {
        ++data;
    }

    int data;
};

int main() {
    boost::thread_group threads;
    for (int i = 0; i < 10; ++i)
        threads.create_thread(Work());
    threads.join_all();
}
Run Code Online (Sandbox Code Playgroud)

c++ false-sharing

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

当线程只写入同一个缓存块时,是否也会发生错误共享?

如果我们有两个内核在同一个缓存块中读取和写入不同的内存位置,则两个内核都被迫一次又一次地重新加载该缓存块,尽管这在逻辑上是不必要的。这就是我们所说的虚假分享。

但是,如果内核从不从该缓存块读取,而只写入怎么办?想象一下,两个内核只是在同一个缓存块中设置了一些位,但它们不必从块中读取,因为它们设置的位信息仅在程序的后期阶段才需要。

是否仅当内核在同一个块上读写时才会发生错误共享,或者如果两者都只写入它也会发生?

parallel-processing multithreading multiprocessing false-sharing

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

尽管错误共享,速度仍会提高

我一直在对OpenMP进行一些测试,并且由于错误共享数组"sum"而使得该程序不应该扩展.我遇到的问题是它确实可以扩展.更糟":

  • 1个线程:4秒(icpc),4秒(g ++)
  • 2线程:2秒(icpc),2秒(g ++)
  • 4线程:0.5秒(icpc),1秒(g ++)

我真的没有通过英特尔编译器获得从2个线程到4个线程的加速.但最重要的是:为什么扩展如此好,即使它应该表现出错误的共享?

#include <iostream>
#include <chrono>

#include <array>

#include <omp.h>

int main(int argc, const char *argv[])
{
    const auto nb_threads = std::size_t{4};
    omp_set_num_threads(nb_threads);

    const auto num_steps = std::size_t{1000000000};
    const auto step = double{1.0 / num_steps};
    auto sum = std::array<double, nb_threads>{0.0};
    std::size_t actual_nb_threads;

    auto start_time = std::chrono::high_resolution_clock::now();
    #pragma omp parallel
    {
        const auto id = std::size_t{omp_get_thread_num()};
        if (id == 0) {
            // This is needed because OMP might give us less threads
            // than the …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading openmp false-sharing

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

使用 alignas 防止错误共享被破坏

我不习惯在互联网上发布任何问题,所以如果我做错了什么,请告诉我。

简而言之

  1. 如何在 CPU 缓存行大小为 64 字节的 64 位架构上正确防止错误共享?

  2. C++ 'alignas' 关键字和简单字节数组(例如:char[64])的使用如何影响多线程效率?

语境

在研究Single Consumer Single Producer Queue的非常有效的实现时,我在对我的代码进行基准测试时遇到了 GCC 编译器的不合逻辑行为。

全文

我希望有人有必要的知识来解释正在发生的事情。

我目前在 arch linux 上使用 GCC 10.2.0 及其 C++ 20 实现。我的笔记本电脑是带有 i7-7500U 处理器的联想 T470S。

让我从数据结构开始:

class SPSCQueue
{
public:
    ...

private:
    alignas(64) std::atomic<size_t> _tail { 0 }; // Tail accessed by both producer and consumer
    Buffer _buffer {}; // Buffer cache for the producer, equivalent to _buffer2
    std::size_t _headCache { 0 }; // Head cache for the …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading x86-64 memory-alignment false-sharing

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

当缓存行大小通常为 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
查看次数

什么是并行编程.net 4.0中的"虚假共享"

任何人都可以在平行编程.net 4.0中与我分享"虚假共享"的知识吗?如果你能用一个例子来解释,那就太棒了.提前致谢 .我想要我的代码的最大性能.

.net false-sharing

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

False sharing of guarded member variables?

Consider:

class Vector
{
  double x, y, z;
  // …
};

class Object
{
  Vector Vec1, Vec2;
  std::mutex Mtx1, Mtx2;

  void ModifyVec1() { std::lock_guard Lock(Mtx1); /* … */ }
  void ModifyVec2() { std::lock_guard Lock(Mtx2); /* … */ }
};
Run Code Online (Sandbox Code Playgroud)

If either the mutexes or the guarded variables are stored contiguously and they share a cache line when cached, can this cause a sort of “cross-locking”?

If so, is it a good practice to declare the mutexes right after (or before) …

c++ mutex c++11 false-sharing c++17

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

一次加载整个缓存行,以避免争用它的多个元素

假设从一个竞争激烈的高速缓存行中需要三段数据,是否有一种方法可以“原子地”加载所有这三件事,从而避免到任何其他内核的多次往返?

实际上,对于所有3个成员的快照,我实际上都不需要原子性的正确性保证,只是在正常情况下,所有3个项目都是在同一时钟周期中读取的。我想避免高速缓存行到达的情况,但是在读取所有3个对象之前会出现无效请求。这将导致第三次访问需要发送另一个请求以共享线路,从而使争用更加严重。

例如,

class alignas(std::hardware_destructive_interference_size) Something {
    std::atomic<uint64_t> one;
    std::uint64_t two;
    std::uint64_t three;
};

void bar(std::uint64_t, std::uint64_t, std::uint64_t);

void f1(Something& something) {
    auto one = something.one.load(std::memory_order_relaxed);
    auto two = something.two;
    if (one == 0) {
        bar(one, two, something.three);
    } else {
        bar(one, two, 0);
    }

}

void f2(Something& something) {
    while (true) {
        baz(something.a.exchange(...));
    }
}
Run Code Online (Sandbox Code Playgroud)

我能否以某种方式确保onetwo并且three所有组件都可以在没有大量RFO的情况下(f1f2不是同时运行)一起加载到一起?

用于此问题的目标体系结构/平台是Intel x86 Broadwell,但是如果有某种技术或编译器内在函数可以允许某些可移植的工作尽力而为,那也很好。

c++ x86 multithreading micro-optimization false-sharing

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

什么是真正的分享?

在阅读问题时,我遇到了“虚假共享”和“真实共享”这两个术语。我读了什么是虚假共享,但在真实共享上我什么也找不到。尽管在提到的问题中该术语被描述为“建设性干扰”,但我仍然不明白它的含义。

c++ false-sharing

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

虚假共享且不稳定

大家好,我最近发现了 Java 8 中引入的一个注释,称为Contished。从这个邮件列表中,我了解了什么是错误共享以及注释如何允许对象或字段分配整个缓存行。

经过一番研究,我发现如果两个核心存储相同的缓存行,并且其中一个核心对其进行修改,那么第二个核心必须从主内存中重新读取整行。https://en.wikipedia.org/wiki/MESI_protocol。但我仍然不清楚为什么硬件会强制 CPU 重新读取它。我的意思是,这就是为什么我们在 Java 中有一个 volatile 关键字,对吗?如果变量被声明为易失性,那么线程将从缓存中跳过该变量,并始终从主内存中读取/写入它。如果硬件强制 cpu 在每次写入后重新读取缓存行,那么在多线程应用程序中如何可能出现数据不一致?
提前致谢

java caching false-sharing

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