C++中对矩阵进行缓存友好的C++操作
cache friendly C++ operation on matrix in C++?
我的应用程序对大矩阵执行一些操作。我最近遇到了缓存的概念以及它通过这个答案可以产生的性能影响。我想知道对我的情况缓存友好的最佳算法是什么。
Algorithm 1:
for(int i = 0; i < size; i++)
{
for(int j = i + 1; j < size; j++)
{
c[i][j] -= K * c[j][j];//K is a constant double variable
}//c is a 2 dimensional array of double variables
}
Algorithm 2:
double *A = new double[size];
for(int n = 0; n < size; n++)
A[n] = c[n][n];
for(int i = 0; i < size; i++)
{
for(int j = i + 1; j < size; j++)
{
c[i][j] -= K * A[j];
}
}
我的数组大小超过 1000x1000。在我的笔记本电脑上进行基准测试显示,对于 5000x5000 的大小,算法 2 优于 1。请注意,我已经对我的应用程序进行了多线程处理,以便一组行由线程操作。
For example: For array of size 1000x1000.
thread1 -> row 0 to row 249
thread2 -> row 250 to row 499
thread3 -> row 500 to row 749
thread4 -> row 750 to row 999
如果您的基准测试显示第二种情况有显著改善,那么它很可能是更好的选择。但是,当然,要知道"平均CPU",我们必须知道,对于可以称为平均的大量CPU,没有其他方法。这实际上取决于平均CPU的定义。我们是在谈论"任何x86(AMD + Intel)CPU"还是"我们可以在从手表到x86系列中最新的超快速创作中找到的任何随机CPU"?
"复制c[n][n]
中的数据"方法很有帮助,因为它获得了自己的地址,并且当代码在更大的矩阵上行走时不会被抛出(L1)缓存[并且乘法所需的所有数据都是"紧密在一起的"。如果你走c[j][j]
,每j
步骤每次迭代都会跳sizeof(double) * (size * j + 1)
字节,所以如果大小大于 4,则需要的下一项不会在同一缓存行中,因此需要另一个内存读取来获取该数据。
换句话说,对于任何具有体面大小的缓存(大于size * sizeof(double)
)的任何内容,这是一个明确的好处。即使缓存较小,也很可能有一些好处,但缓存副本被c[i][j]
的某些部分丢弃的可能性更高。
总之,第二种算法很可能对几乎所有选项都更好。
算法 2 受益于所谓的"空间局部性",将对角线移动到单维数组中使其以连续地址的形式驻留在内存中,从而:
-
享受每个缓存行获取多个有用元素的好处(大概是 64 字节,具体取决于您的 CPU),更好地利用缓存和内存 BW(而 c[n][n] 也会获取大量无用的数据,因为它在同一行中)。
-
享受硬件流预取程序(假设您的 CPU 中存在)的好处,它沿着页面在代码之前积极运行,并将数据提前带到较低的缓存级别,从而改善内存延迟。
应该指出的是,将数据移动到 A 并不一定能提高可缓存性,因为 A 仍然会与不断来自 c 的大量数据竞争并破坏缓存。但是,由于它被反复使用,因此一个好的 LRU 算法很有可能会让它留在缓存中。您可以通过对数组 c 使用流内存操作来提供帮助。应该注意的是,这些是非常不稳定的性能工具,如果使用不当,在某些情况下可能会导致性能降低。
另一个潜在的好处可能是在到达每个新数组行之前稍微混合软件预取。
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 重载操作程序时出错>>用于类中的字符串 memebr
- 对字符串进行位操作
- 我可以在 C++ 中的函数体之外进行操作吗?
- MPI突然停止了对多个核心的操作
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 对字符数组中的元素执行逐位操作
- 如何在directx/c++中进行平移/缩放操作
- 逐位操作的隐式类型转换
- 为什么一个向量上的多线程操作很慢
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 位移操作和位掩码未检测到重复字符
- 如何进行特定的位操作?
- 当我们进行一些操作时,应该使用什么'std::string'或'std::stringstream'?
- 字符串操作 - 字符计数
- 此代码中的操作流程是什么?C/C++.
- 复制和交换习惯用法与移动操作之间的交互
- 像union_这样的 Boost.Geometry 操作如何处理浮点类型的基本不精确性?
- 为什么 std::lerp 不适用于任何已实现所需操作的类型?
- 无法合并生成操作.. 先决条件不同