即使没有锁也会有内存争用吗?
Can there be memory contention even if there are no locks?
我正在编写一个多线程代码,其中一堆 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-
的不必要调用之后,我似乎得到了更好的缩放。
是的,速度可能会放缓。主内存 (RAM) 带宽是有限的,如果您有多个内核快速读取大量数据,您可能会使内存总线饱和。最大内存带宽通常为每秒数十 GB(请参阅特定处理器的页面,例如 i9-9900K 显示 41.6 GB/s)。
同样,一个物理包上的所有内核共享一个 L3 缓存,因此如果您多次读取某些数据,您的缓存命中率可能会降低,因为您的线程会将彼此的数据推出 L3(这是最大的缓存)。
如果您想知道某些配置的减速有多少,您只有一个选择:测试它们。如果您提前知道您可能需要什么内存,请考虑在您的代码中添加预取指令,特别是如果您的访问模式是非顺序的。
我正在编写一个多线程代码,其中一堆 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-
的不必要调用之后,我似乎得到了更好的缩放。
是的,速度可能会放缓。主内存 (RAM) 带宽是有限的,如果您有多个内核快速读取大量数据,您可能会使内存总线饱和。最大内存带宽通常为每秒数十 GB(请参阅特定处理器的页面,例如 i9-9900K 显示 41.6 GB/s)。
同样,一个物理包上的所有内核共享一个 L3 缓存,因此如果您多次读取某些数据,您的缓存命中率可能会降低,因为您的线程会将彼此的数据推出 L3(这是最大的缓存)。
如果您想知道某些配置的减速有多少,您只有一个选择:测试它们。如果您提前知道您可能需要什么内存,请考虑在您的代码中添加预取指令,特别是如果您的访问模式是非顺序的。