You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python for循环下列表append快于预分配NumPy数组填充的原因问询

Python列表append与预分配NumPy数组逐元素填充速度差异原因

测试场景说明

  • 测试目标:对比Python内置列表append操作、预创建空NumPy数组逐下标赋值两种操作的运行速度
  • 初始预期:NumPy底层由C实现,运行速度应当优于Python内置列表操作
  • 测试参数:设置测试规模n=10000000,排除库加载耗时对结果的干扰
  • 测试结果:逐元素填充场景下,列表操作速度显著快于NumPy数组操作

测试代码如下:

import time 
import numpy as np 
import matplotlib.pyplot as plt 

n = 10000000

t_np_0= time.perf_counter()
f = np.empty(n)
for i in range(n):
    f[i] = i

t_np_1 = time.perf_counter()
print("Time elapsed numpy: ", t_np_1 - t_np_0) 

t_list_0= time.perf_counter()
f = []
for i in range(n):
     f.append(i)

t_list_1 = time.perf_counter()
print("Time elapsed list: ", t_list_1 - t_list_0) 

speed = (t_np_1 - t_np_0)/(t_list_1 - t_list_0)
print("np is " + str((speed - 1)*100) + "% slower than list")

结果成因拆解

  • 逐元素赋值的跨层调用+类型转换开销:测试中NumPy数组的赋值操作f[i] = i是在Python层循环中逐次执行的,每次赋值都需要在Python解释器和NumPy的C底层之间切换,完成下标合法性校验、内存偏移计算、Python原生int到NumPy C数值类型的转换,这些操作累积千万次后的总开销极高。而列表append存储的是Python对象的内存指针,不需要做额外的类型转换,直接存入引用即可,单次操作开销更低。
  • 测试场景完全没有触发NumPy的性能优势:NumPy的高性能来自于向量化操作——即整块数组的计算逻辑完全在C层执行,全程跳过Python解释器的循环调度开销。在Python层写for循环逐次调用NumPy的下标赋值,相当于把C实现的逻辑拆成千万次跨层调用,完全抵消了C实现的速度优势,反而比纯Python列表操作更慢。
  • 列表append本身已做内存预分配优化:CPython对列表的动态扩容做了超额预分配设计,每次扩容时会申请比当前需求更大的预留内存,append操作的均摊时间复杂度为O(1),不存在每次追加元素都重新拷贝整块内存的问题,预分配NumPy数组在内存层面的优势被大幅削弱。

正确的NumPy高性能用法

如果要发挥NumPy的速度优势,需要避免Python层循环,直接使用内置的向量化接口实现相同逻辑,运行速度会远快于列表append,示例如下:

t_np_vec_0 = time.perf_counter()
# 直接调用C层实现的arange接口生成序列,无Python层循环
f = np.arange(n)
t_np_vec_1 = time.perf_counter()
print("Time elapsed numpy vectorized: ", t_np_vec_1 - t_np_vec_0)

内容的提问来源于stack exchange,提问作者user18519249

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:45:38