在程序开发过程中,空间局部性和时间局部性是两个重要的概念,它们用于描述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%(通过百万次计算耗时统计)。