问题描述
我正在编写一个多线程代码,其中一堆 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(这是最大的缓存)。
如果您想知道某些配置的减速程度,您只有一种选择:测试它们。如果您提前知道可能需要哪些内存,尤其是在您的访问模式是非顺序的情况下,请考虑向您的代码添加预取指令。