CPU处理数据的空间局部性和时间局部性: c++矩阵乘法实现示例

在程序开发过程中,空间局部性和时间局部性是两个重要的概念,它们用于描述cpu访问数据的模式,这些模式对性能优化非常关键,尤其是对于cpu处理数据的缓存机制。

下面解释它们在 C++ 代码中的应用:

时间局部性(Temporal Locality)

时间局部性指的是如果一个数据项被访问过,那么在不久的将来它很可能会再次被访问。这种特性在程序中通过以下方式体现:

1. 循环:在循环中多次访问相同的变量。

for (int i = 0; i < 1000; ++i) {
    // 'i' 和 'sum' 的时间局部性
    sum += i;
}

2. 缓存数据:将经常访问的数据缓存起来。

int x = array[5];
// 多次使用 'x'
x += 10;
y = x * 2;

空间局部性(Spatial Locality)

空间局部性指的是如果一个数据项被访问过,那么其附近的数据项很可能也会被访问。这种特性在程序中通过以下方式体现:

1. 顺序访问数组:按顺序遍历数组元素。

for (int i = 0; i < 1000; ++i) {
    // 按顺序访问数组元素
    sum += array[i];
}

2. 访问结构体中的多个成员

struct Point {
    int x, y;
};

Point p;
// 访问结构体中相邻的成员变量
p.x = 10;
p.y = 20;

基本用例

考虑下面的例子,它展示了如何利用时间局部性和空间局部性:

#include <iostream>
#include <vector>

int main() {
    const int size = 1000;
    std::vector<int> array(size, 1);
    int sum = 0;

    // 利用空间局部性:按顺序访问数组元素
    for (int i = 0; i < size; ++i) {
        sum += array[i];
    }

    // 利用时间局部性:多次访问 'sum' 变量
    for (int i = 0; i < size; ++i) {
        sum += i;
    }

    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

在这个例子中:

  • 在第一个循环中,我们按顺序访问 array 的元素,利用了空间局部性,因为数组元素在内存中是连续存储的。
  • 在第二个循环中,我们多次访问 sum 变量,利用了时间局部性,因为在每次循环迭代中都需要对 sum 进行更新。

通过优化代码以利用时间局部性和空间局部性,可以显著提高程序的性能,因为这样可以更有效地利用 CPU 缓存,从而减少内存访问的开销,提升性能。

矩阵乘法应用示例

1. 3x3矩阵乘法

优化前:

template <typename T>
void Matrix4<T>::append3x3(const Matrix4<T>& lhs)
{
    auto lfs = lhs.getLocalFS();
    auto sfs = m_localFS;

    auto m111 = sfs[0];
    auto m121 = sfs[4];
    auto m131 = sfs[8];
    auto m112 = sfs[1];
    auto m122 = sfs[5];
    auto m132 = sfs[9];
    auto m113 = sfs[2];
    auto m123 = sfs[6];
    auto m133 = sfs[10];
    auto m211 = lfs[0];
    auto m221 = lfs[4];
    auto m231 = lfs[8];
    auto m212 = lfs[1];
    auto m222 = lfs[5];
    auto m232 = lfs[9];
    auto m213 = lfs[2];
    auto m223 = lfs[6];
    auto m233 = lfs[10];
    sfs[0]    = m111 * m211 + m112 * m221 + m113 * m231;
    sfs[1]    = m111 * m212 + m112 * m222 + m113 * m232;
    sfs[2]    = m111 * m213 + m112 * m223 + m113 * m233;
    sfs[4]    = m121 * m211 + m122 * m221 + m123 * m231;
    sfs[5]    = m121 * m212 + m122 * m222 + m123 * m232;
    sfs[6]    = m121 * m213 + m122 * m223 + m123 * m233;
    sfs[8]    = m131 * m211 + m132 * m221 + m133 * m231;
    sfs[9]    = m131 * m212 + m132 * m222 + m133 * m232;
    sfs[10]   = m131 * m213 + m132 * m223 + m133 * m233;
}

优化后:

template <typename T>
void Matrix4<T>::append3x3Fast(const Matrix4<T>& lhs)
{
    auto lfs = lhs.m_localFS;
    auto sfs = m_localFS;

    T av0[3];
    constexpr size_t av0Size = sizeof(av0);

    memcpy(&av0, sfs + 0, av0Size);
    sfs[0] = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + (av0[2] * lfs[8]);
    sfs[1] = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + (av0[2] * lfs[9]);
    sfs[2] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + (av0[2] * lfs[10]);

    memcpy(&av0, sfs + 4, av0Size);
    sfs[4] = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + (av0[2] * lfs[8]);
    sfs[5] = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + (av0[2] * lfs[9]);
    sfs[6] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + (av0[2] * lfs[10]);

    memcpy(&av0, sfs + 8, av0Size);
    sfs[8]  = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + (av0[2] * lfs[8]);
    sfs[9]  = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + (av0[2] * lfs[9]);
    sfs[10] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + (av0[2] * lfs[10]);
}

2. 4x4矩阵乘法

优化前:

template <typename T>
void Matrix4<T>::append(const Matrix4<T>& lhs)
{
    auto lfs = lhs.m_localFS;
    auto sfs = m_localFS;

    auto m111 = sfs[0];
    auto m112 = sfs[1];
    auto m113 = sfs[2];
    auto m114 = sfs[3];

    auto m121 = sfs[4];
    auto m122 = sfs[5];
    auto m123 = sfs[6];
    auto m124 = sfs[7];

    auto m131 = sfs[8];
    auto m132 = sfs[9];
    auto m133 = sfs[10];
    auto m134 = sfs[11];

    auto m141 = sfs[12];
    auto m142 = sfs[13];
    auto m143 = sfs[14];
    auto m144 = sfs[15];


    auto m211 = lfs[0];
    auto m212 = lfs[1];
    auto m213 = lfs[2];
    auto m214 = lfs[3];

    auto m221 = lfs[4];
    auto m222 = lfs[5];
    auto m223 = lfs[6];
    auto m224 = lfs[7];

    auto m231 = lfs[8];
    auto m232 = lfs[9];
    auto m233 = lfs[10];
    auto m234 = lfs[11];

    auto m241 = lfs[12];
    auto m242 = lfs[13];
    auto m243 = lfs[14];
    auto m244 = lfs[15];

    sfs[0]  = ((m111 * m211) + (m112 * m221)) + ((m113 * m231) + (m114 * m241));
    sfs[1]  = ((m111 * m212) + (m112 * m222)) + ((m113 * m232) + (m114 * m242));
    sfs[2]  = ((m111 * m213) + (m112 * m223)) + ((m113 * m233) + (m114 * m243));
    sfs[3]  = ((m111 * m214) + (m112 * m224)) + ((m113 * m234) + (m114 * m244));
    sfs[4]  = ((m121 * m211) + (m122 * m221)) + ((m123 * m231) + (m124 * m241));
    sfs[5]  = ((m121 * m212) + (m122 * m222)) + ((m123 * m232) + (m124 * m242));
    sfs[6]  = ((m121 * m213) + (m122 * m223)) + ((m123 * m233) + (m124 * m243));
    sfs[7]  = ((m121 * m214) + (m122 * m224)) + ((m123 * m234) + (m124 * m244));
    sfs[8]  = ((m131 * m211) + (m132 * m221)) + ((m133 * m231) + (m134 * m241));
    sfs[9]  = ((m131 * m212) + (m132 * m222)) + ((m133 * m232) + (m134 * m242));
    sfs[10] = ((m131 * m213) + (m132 * m223)) + ((m133 * m233) + (m134 * m243));
    sfs[11] = ((m131 * m214) + (m132 * m224)) + ((m133 * m234) + (m134 * m244));
    sfs[12] = ((m141 * m211) + (m142 * m221)) + ((m143 * m231) + (m144 * m241));
    sfs[13] = ((m141 * m212) + (m142 * m222)) + ((m143 * m232) + (m144 * m242));
    sfs[14] = ((m141 * m213) + (m142 * m223)) + ((m143 * m233) + (m144 * m243));
    sfs[15] = ((m141 * m214) + (m142 * m224)) + ((m143 * m234) + (m144 * m244));
}

优化后:

template <typename T>
void Matrix4<T>::appendFast(const Matrix4<T>& lhs)
{
    auto lfs = lhs.m_localFS;
    auto sfs = m_localFS;

    T av0[4];

    memcpy(&av0, sfs + 0, sizeof(Vec3<T>));
    sfs[0] = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + ((av0[2] * lfs[8])  + (av0[3] * lfs[12]));
    sfs[1] = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + ((av0[2] * lfs[9])  + (av0[3] * lfs[13]));
    sfs[2] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + ((av0[2] * lfs[10]) + (av0[3] * lfs[14]));
    sfs[3] = ((av0[0] * lfs[3]) + (av0[1] * lfs[7])) + ((av0[2] * lfs[11]) + (av0[3] * lfs[15]));

    memcpy(&av0, sfs + 4, sizeof(Vec3<T>));
    sfs[4] = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + ((av0[2] * lfs[8])  + (av0[3] * lfs[12]));
    sfs[5] = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + ((av0[2] * lfs[9])  + (av0[3] * lfs[13]));
    sfs[6] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + ((av0[2] * lfs[10]) + (av0[3] * lfs[14]));
    sfs[7] = ((av0[0] * lfs[3]) + (av0[1] * lfs[7])) + ((av0[2] * lfs[11]) + (av0[3] * lfs[15]));

    memcpy(&av0, sfs + 8, sizeof(Vec3<T>));
    sfs[8]  = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + ((av0[2] * lfs[8])  + (av0[3] * lfs[12]));
    sfs[9]  = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + ((av0[2] * lfs[9])  + (av0[3] * lfs[13]));
    sfs[10] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + ((av0[2] * lfs[10]) + (av0[3] * lfs[14]));
    sfs[11] = ((av0[0] * lfs[3]) + (av0[1] * lfs[7])) + ((av0[2] * lfs[11]) + (av0[3] * lfs[15]));

    memcpy(&av0, sfs + 12, sizeof(Vec3<T>));
    sfs[12] = ((av0[0] * lfs[0]) + (av0[1] * lfs[4])) + ((av0[2] * lfs[8])  + (av0[3] * lfs[12]));
    sfs[13] = ((av0[0] * lfs[1]) + (av0[1] * lfs[5])) + ((av0[2] * lfs[9])  + (av0[3] * lfs[13]));
    sfs[14] = ((av0[0] * lfs[2]) + (av0[1] * lfs[6])) + ((av0[2] * lfs[10]) + (av0[3] * lfs[14]));
    sfs[15] = ((av0[0] * lfs[3]) + (av0[1] * lfs[7])) + ((av0[2] * lfs[11]) + (av0[3] * lfs[15]));
}

优化后,release版单线程情况下,性能提升接近30%(通过百万次计算耗时统计)。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值