- Published on
列主序 vs 行主序:内存布局详解
用 2×2 矩阵清晰地解释列主序和行主序的区别。
矩阵定义
我们先定义一个 2×2 矩阵:
矩阵元素:
a00 a01
a10 a11
其中第一个下标是行索引,第二个下标是列索引。
1. 行主序(Row-Major Order)
C/C++、Python等语言的默认存储方式
内存布局
内存地址: 0 1 2 3
存储元素: a00 a01 a10 a11
访问公式
index = i * cols + j
// 其中:
// i = 行索引 (0,1)
// j = 列索引 (0,1)
// cols = 矩阵的列数 (2)
计算示例
a00: i=0, j=0 → 0*2 + 0 = 0
a01: i=0, j=1 → 0*2 + 1 = 1
a10: i=1, j=0 → 1*2 + 0 = 2
a11: i=1, j=1 → 1*2 + 1 = 3
可视化存储
内存: | a00 | a01 | a10 | a11 |
逻辑: [行0] 行0结束 [行1] 行1结束
特征:同一行的元素在内存中相邻存储。
2. 列主序(Column-Major Order)
Fortran、MATLAB、BLAS/LAPACK库的默认存储方式
内存布局
内存地址: 0 1 2 3
存储元素: a00 a10 a01 a11
访问公式
index = j * rows + i
// 其中:
// i = 行索引 (0,1)
// j = 列索引 (0,1)
// rows = 矩阵的行数 (2)
这正是你提供的宏定义:
#define IDX2C(i,j,ld) (((j)*(ld))+(i)) // ld = leading dimension = rows
计算示例
a00: i=0, j=0 → 0*2 + 0 = 0
a10: i=1, j=0 → 0*2 + 1 = 1
a01: i=0, j=1 → 1*2 + 0 = 2
a11: i=1, j=1 → 1*2 + 1 = 3
可视化存储
内存: | a00 | a10 | a01 | a11 |
逻辑: [列0] 列0结束 [列1] 列1结束
特征:同一列的元素在内存中相邻存储。
3. 对比表格
| 特性 | 行主序 (Row-Major) | 列主序 (Column-Major) |
|---|---|---|
| 默认语言 | C, C++, Python | Fortran, MATLAB |
| 内存布局 | 逐行存储 | 逐列存储 |
| 公式 | i*cols + j | j*rows + i |
| 连续元素 | 同一行 | 同一列 |
| 遍历优化 | 内循环用列索引 | 内循环用行索引 |
4. 遍历性能影响
行主序优化遍历:
// 行主序:内循环应该是列索引
for (int i = 0; i < 2; i++) { // 行
for (int j = 0; j < 2; j++) { // 列(内循环)
// 连续访问内存:a00→a01→a10→a11
value = matrix[i*2 + j]; // 行主序公式
}
}
列主序优化遍历:
// 列主序:内循环应该是行索引
for (int j = 0; j < 2; j++) { // 列
for (int i = 0; i < 2; i++) { // 行(内循环)
// 连续访问内存:a00→a10→a01→a11
value = matrix[j*2 + i]; // 列主序公式
}
}
5. 实际数值示例
假设矩阵:
1.0 2.0
3.0 4.0
行主序内存:
地址: 0 1 2 3
数值: 1.0 2.0 3.0 4.0
a00 a01 a10 a11
列主序内存:
地址: 0 1 2 3
数值: 1.0 3.0 2.0 4.0
a00 a10 a01 a11
关键记忆点
- 行主序:先存完一整行,再存下一行
- 列主序:先存完一整列,再存下一列
- 你的宏
IDX2C(i,j,ld)是列主序计算公式 - 性能优化的关键是让内循环访问连续内存
THE END