脚本跑了10分钟处理100万行——Python性能优化五大方向
本文目录
先说结论
Python性能优化有五个方向:① 向量化(NumPy/Pandas替代for循环,快100倍);② 数据结构优化(set/dict替代list查找,快100倍);③ 内置函数(map/filter/sum等C实现比Python循环快3-5倍);④ 并行化(multiprocessing利用多核);⑤ 底层加速(Cython/Numba编译成C代码)。按ROI排序:向量化效果最大,数据结构其次。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
方向一:向量化——最大的性能杠杆
import numpy as np
import pandas as pd
# 场景:100万个数值求平方和
# Python for循环(最慢)
data = list(range(1000000))
result = sum([x**2 for x in data]) # 约200ms
# NumPy向量化(快100倍)
arr = np.arange(1000000)
result = np.sum(arr**2) # 约2ms!
# 为什么快100倍?
# Python for循环:每次迭代都要做类型检查、方法查找、装箱拆箱
# NumPy:底层C代码直接操作连续内存,无Python开销
# Pandas向量化也一样快
df = pd.DataFrame({'value': range(1000000)})
result = (df['value'] ** 2).sum() # 约3ms
# 比 df['value'].apply(lambda x: x**2).sum() 快50倍
方向二:数据结构优化
# 场景:检查100万个用户ID是否在黑名单中
# 黑名单有10万个ID
blacklist_list = list(range(100000)) # list
blacklist_set = set(range(100000)) # set
user_ids = list(range(1000000))
# 用list查找(极慢)
result = [uid for uid in user_ids if uid in blacklist_list]
# 约450秒(7.5分钟!)每次in操作都是O(N)遍历
# 用set查找(极快)
result = [uid for uid in user_ids if uid in blacklist_set]
# 约0.15秒!每次in操作是O(1)哈希查找
# 差距3000倍!
# 核心原则:需要做'判断是否存在'的操作时,永远用set/dict
方向三:内置函数替代手写循环
# Python内置函数是C实现的,比Python循环快3-5倍
# 求和
total = sum(data) # 比 for循环累加 快3倍
# 过滤
evens = list(filter(lambda x: x % 2 == 0, data)) # 比for+if快2倍
# 映射
doubled = list(map(lambda x: x * 2, data)) # 比for+append快2倍
# 字符串拼接
result = ','.join(str_list) # 比for循环+= 快10倍(第13篇讲过原理)
# 排序
sorted_data = sorted(data, key=lambda x: x[1]) # Timsort算法,C实现
# 计数
from collections import Counter
freq = Counter(data) # 比手动dict计数快3倍
方向四:并行化——利用多核CPU
# GIL限制了多线程的CPU并行,用multiprocessing做多进程并行
from multiprocessing import Pool
def process_chunk(chunk):
# 处理一块数据
return [validate(row) for row in chunk]
# 把100万行分成4块,4个进程并行处理
chunks = [data[i::4] for i in range(4)]
with Pool(4) as pool:
results = pool.map(process_chunk, chunks)
# 4核CPU:理论快4倍,实际快3倍(进程间通信有开销)
# 更现代的方式:concurrent.futures
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(process_chunk, chunks))
# 注意:多进程有序列化开销(数据要pickle传给子进程)
# 如果数据量小(几千行),多进程的启动开销可能大于收益
# 建议数据量 > 10万行时才用多进程
方向五:底层加速——Cython/Numba
# Numba:用JIT编译把Python函数编译成机器码
from numba import jit
@jit(nopython=True) # nopython模式:完全编译成机器码
def compute_sum_of_squares(arr):
total = 0.0
for i in range(len(arr)):
total += arr[i] ** 2
return total
# 第一次调用:编译(约1秒)
# 后续调用:和C一样快
result = compute_sum_of_squares(np.arange(1000000, dtype=np.float64))
# 约1ms(和NumPy向量化差不多,比纯Python快200倍)
# Numba适合:
# - 无法用NumPy/Pandas向量化的复杂循环逻辑
# - 数值计算密集的场景
# Numba不适合:
# - 字符串处理、dict操作等非数值场景
# - 依赖第三方库的代码(Numba只支持NumPy子集)
实战经验:100万行数据从10分钟到12秒
在某知名出海硬件品牌的数据质量校验脚本优化过程:
| 优化步骤 | 方法 | 时间 | 提升 |
|---|---|---|---|
| 原始版本 | for循环+iterrows+list查找 | 10分钟 | 基准 |
| 步骤一 | list查找改set | 3分钟 | 3倍 |
| 步骤二 | iterrows改向量化 | 25秒 | 7倍 |
| 步骤三 | 推导式替代for+append | 18秒 | 1.4倍 |
| 步骤四 | Pandas category减少内存 | 15秒 | 1.2倍 |
| 步骤五 | 多进程并行(4核) | 12秒 | 1.25倍 |
核心发现:向量化和数据结构优化贡献了95%的提升(10分钟到25秒)。后面的推导式、内存优化、多进程只是锦上添花。这说明Python性能优化的ROI高度集中在前两个方向——先确保没有用for循环遍历大数据、没有用list做查找,再考虑其他优化。
Python性能优化的系统方法论
第一步:先定位瓶颈。用cProfile或line_profiler找到耗时最长的代码行,不要凭感觉优化。
# 用cProfile定位瓶颈
import cProfile
cProfile.run('process_data()', sort='cumulative')
# 用line_profiler逐行分析
# pip install line_profiler
# @profile # 装饰器标记要分析的函数
# kernprof -l -v script.py
第二步:按ROI排序优化。向量化(100倍)→ 数据结构(100倍)→ 内置函数(3-5倍)→ 并行化(2-4倍)→ Cython/Numba(按需)。
第三步:验证效果。每步优化后测量耗时,确认有效再进行下一步。避免过度优化——如果脚本已经从10分钟降到12秒,再优化几秒意义不大。
面试高频追问
追问一:Python比C慢多少?能不能接受?
纯Python循环比C慢50-100倍。但通过NumPy/Pandas向量化后差距缩小到1-3倍(因为NumPy底层就是C),通过Numba JIT编译后几乎和C一样快。大数据场景中Python通常只是调用入口(PySpark的Python只负责定义transformation,实际执行在JVM/C中),所以Python的速度不是瓶颈。
追问二:什么时候不该用Python?
CPU密集的低延迟场景——比如实时流处理的热路径(用Java/Scala写Flink算子)、高频交易系统(用C++)、大规模数值模拟(用Fortran/C)。Python适合的是ETL编排、数据分析、脚本自动化、原型开发这些'胶水'场景。在大数据栈里Python的定位是'调度和编排'而不是'执行引擎'。
追问三:Polars比Pandas快多少?原理是什么?
Polars在大部分操作上比Pandas快5-10倍。原理:① Rust实现(无GIL限制,真正多线程);② 延迟执行(先构建查询计划再优化执行,类似Spark);③ Apache Arrow内存格式(列式存储、SIMD加速)。对于单机处理几个GB的数据,Polars是2026年的最佳选择。
总结
Python性能优化面试讲五个方向按ROI排序:① 向量化(NumPy/Pandas替代for循环,快100倍,效果最大);② 数据结构(set/dict替代list查找,快100倍);③ 内置函数(map/filter/sum替代手写循环,快3-5倍);④ 并行化(multiprocessing利用多核,快2-4倍);⑤ 底层加速(Numba JIT编译)。能讲出100万行数据从10分钟到12秒的优化过程和每步效果,面试官会觉得你有系统性的性能优化方法论。
下一篇聊Pandas处理大文件:读了个5GB的CSV内存直接爆了——Pandas怎么处理大数据?
你们Python脚本做过什么性能优化?最大的优化效果是什么?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,只讲大数据面试干货
Python 面试通关手册
第6-10篇:高级特性与工程实践
Wood聊大数据
9年大数据老兵,只讲大数据面试干货