Numpy按行读取时Fortran内存布局为何比C布局执行速度更快
Numpy C顺序与Fortran顺序性能测试异常原因解析
测试结果异常的核心原因
你观测到的3秒左右的性能差异完全是测试方法设计失误导致的随机误差,并不代表F顺序行遍历速度比C顺序快:
- 你用了三层纯Python循环做元素访问,Python本身的循环调度开销、numpy索引返回Python对象的封装开销占了总耗时的99%以上,内存访问连续性带来的性能差异被完全淹没,根本无法被观测到。
- 你可以尝试多轮重复测试,会发现两者耗时差异完全随机,时而C快时而F快,差值波动在几秒量级,没有统计意义。
reshape的行为与flags标记的含义
首先明确两个核心事实:
- reshape不会主动修改内存中元素的实际存储顺序,只要新形状和原数组元素总数匹配,reshape默认会返回原内存的视图,仅修改数组的shape和stride(步长)属性。
C_CONTIGUOUS和F_CONTIGUOUS标记不是直接标记内存存储顺序,而是根据数组的shape和stride计算得到的属性:- C连续要求:数组最后一个维度的步长等于单个元素的字节大小,行方向相邻元素在内存中相邻。
- F连续要求:数组第一个维度的步长等于单个元素的字节大小,列方向相邻元素在内存中相邻。
你代码中的逻辑验证:
np.arange(1000000)生成的原始数组是C连续的,内存中元素按0、1、2...999999的顺序线性存储。- 当
order='C'做reshape时,新数组的stride为(1000*8, 8)(假设int64类型),满足C连续的要求,因此C_CONTIGUOUS=True。 - 当
order='F'做reshape时,numpy会调整新数组的stride为(8, 1000*8),满足F连续的要求,因此F_CONTIGUOUS=True,此时内存中元素的存储顺序没有发生任何变化,只是数组的索引映射逻辑变了。
正确的性能测试方法
要观测内存连续性带来的性能差异,需要排除Python层面的开销,使用numpy底层实现的向量化操作即可:
import numpy as np import time x_c = np.arange(1000000).reshape((1000, 1000), order='C') x_f = np.arange(1000000).reshape((1000, 1000), order='F') # 测试按行求和(行遍历) start = time.time() np.sum(x_c, axis=1) print(f"C顺序行求和耗时: {time.time() - start}") start = time.time() np.sum(x_f, axis=1) print(f"F顺序行求和耗时: {time.time() - start}")
运行后可以看到C顺序行遍历的速度是F顺序的数倍,完全符合我们的认知。
内容的提问来源于stack exchange,提问作者Whistleroosh
相关产品推荐
相关产品推荐

