strides决定cpu访问内存的步长,非连续数组因随机跳址导致缓存命中率低、性能下降;可通过arr.flags['c_contiguous']检测,用np.ascontiguousarray()修复。

strides 决定了 CPU 怎么跳着读内存
NumPy 数组本身不存“多维结构”,只是一块连续内存 + shape + strides + dtype。其中 strides 是个元组,每个值表示:沿对应轴移动 1 步,内存地址要跳多少字节。
比如 arr = np.arange(12).reshape(3, 4).astype(np.int64),arr.strides 是 (32, 8):
-
strides[1] == 8:同一行里,从第 0 列到第 1 列,地址 +8 字节(一个int64大小)→ 连续读,缓存友好 -
strides[0] == 32:从第 0 行第 0 列跳到第 1 行第 0 列,地址 +32 字节(4 列 × 8 字节)→ 仍是线性步进,没问题
但一旦 strides 变得“稀疏”或“错位”,CPU 就得反复随机跳地址,缓存命中率暴跌,速度就掉下来。
非连续切片让 strides 变成“跨页式跳跃”
像 arr[::2, ::2] 这种带步长的切片,不会复制数据,而是生成新 strides(比如变成 (64, 16)),导致每次取下一个元素都要跳更远、且不规律。
常见后果:
-
np.sum()、np.mean()等聚合操作变慢 3–5 倍,尤其在大数组上 - 某些底层库(如 OpenCV、BLAS)拒绝接受非连续数组,直接报错
ValueError: array is not C-contiguous - 即使你没显式调用外部库,NumPy 自己在触发 ufunc 时也可能悄悄复制一份连续副本,反而多花时间
如何快速判断和修复 stride 导致的性能问题
别猜,用 .flags 和 .strides 直接看:
- 检查是否连续:
arr.flags['C_CONTIGUOUS']返回False?那就是隐患 - 对比原始和切片后的
strides:如果某维 stride 增大为原来的 N 倍,访问该维时就会跳 N 倍远 - 修复手段很直接:
np.ascontiguousarray(arr[::2, ::2])强制重排成连续内存;或者用arr[::2, ::2].copy() - 注意:
.copy()和np.ascontiguousarray()效果常一样,但后者语义更明确——只解决连续性,不改变 dtype 或 shape
转置(.T)为什么有时快有时慢
arr.T 本质只是交换 shape 和翻转 strides,零拷贝。所以它本身极快。但后续操作是否快,取决于你用哪一维做主循环:
- 如果你对
arr.T按行遍历(即逻辑上的“列”),而它实际是 F-order 视图,strides就会迫使你跨大步读 → 慢 - 如果你改用
np.asfortranarray(arr)显式创建列优先数组,再按列处理,反而更快 - 真正关键不是“转没转”,而是“你怎么访问”——
strides匹配你的访问模式,才快
最易被忽略的一点:连续性不是永久属性。一次 reshape、一次 transpose、一次带步长切片,都可能让原本飞快的数组突然变慢,而且毫无警告。别依赖“它之前很快”,每次关键计算前,用 .flags['C_CONTIGUOUS'] 看一眼。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











