【问题标题】:Can there be memory contention even if there are no locks?即使没有锁,也会存在内存争用吗?
【发布时间】:2021-01-16 04:37:30
【问题描述】:

我正在编写一个多线程代码,其中一堆std::async 调用在整个程序期间生成固定数量的线程。每个线程都以只读方式处理相同的const BigData 结构。有来自const BigData 的频繁随机读取,但线程在其他方面是完全独立的。是否可以合理地期望获得完美的扩展,或者是否会因更多的内存访问而减慢?

编辑:经过一些分析,这似乎是罪魁祸首:

class Point {
  friend Point operator+(const Point& lhs, const Point& rhs) noexcept {
    return Point{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z};
  };
  friend Point operator-(const Point& lhs, const Point& rhs) noexcept {
    return Point{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z};
  };

public:
  Point() noexcept;
  Point(const Real& x, const Real& y, const Real& z) noexcept
    : x{x}, y{y}, z{z} {};

private:
  Real x{0};
  Real y{0};
  Real z{0};
};

在重构我的代码以避免对operator+operator- 的不必要调用后,我似乎获得了更好的扩展性。

【问题讨论】:

  • 有足够多的未指定参数(读取大小、缓存行为、与 NUMA 相关的任何内容等)我们无法给出明确的答案。最好的办法是进行实验,看看性能如何扩展。
  • 如果您看到速度大幅下降,我会感到惊讶,因为数据的只读性质应该允许每个 CPU 缓存它自己正在访问的任何页面的本地副本。当然,如果您的 CPU 内核共享一个缓存,并且它们一起填满该缓存,以便它们反复将彼此的页面踢出缓存以加载自己的缓存,这将导致一些速度变慢(相对于理论上的最佳性能,无论如何)。

标签: c++ multithreading contention


【解决方案1】:

是的,可能会放缓。主内存 (RAM) 带宽是有限的,如果您有多个内核快速读取大量数据,您可能会使内存总线饱和。最大内存带宽通常为每秒数十 GB(请参阅特定处理器的页面,例如 i9-9900K,显示为 41.6 GB/s)。

同样,一个物理包上的所有内核共享一个 L3 缓存,因此如果您多次读取某些数据,您的线程可能会减少缓存命中次数,因为您的线程将彼此的数据推出 L3(这是最大的缓存)。

如果您想知道某些配置会导致多少减速,您只有一个选择:测试它们。如果您提前知道可能需要什么内存,请考虑在代码中添加预取指令,尤其是在您的访问模式是非顺序的时。

【讨论】:

  • 我开始分析我的代码。一个部分部分分配内存(在堆栈上?)使用Point3D 类进行一些简单的算术运算。例如(u + v) * (w + x) 为总和创建两个临时Point3D 对象。在多线程情况下,Point3D 的构造函数占用了更大百分比的时间。这是内存总线饱和的一个可能原因吗?
  • 显示Point3D构造函数的代码。
  • 已更新。我仍在研究如何在 macOS (Big Sur) 上分析内存。 Valgrind 不起作用,但 Instruments.app 似乎可以工作。由于我的问题似乎是内存饱和,我将研究Intel's VTune Profiler
  • 您到底使用了哪些编译器标志?我特别想知道您的优化设置。
  • 我使用的是 Apple Clang 版本 12.0.0,只设置了 -O3。我可以向你发送一个回购邀请,因为它目前是私人的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-20
  • 2020-05-08
  • 2018-07-26
  • 1970-01-01
  • 2013-10-05
  • 2017-03-04
相关资源
最近更新 更多