订阅更新

脚本跑了10分钟处理100万行——Python性能优化五大方向

Python手册 第 5 题 约 9 分钟读完

本文目录
  1. 先说结论
  2. 方向一:向量化——最大的性能杠杆
  3. 方向二:数据结构优化
  4. 方向三:内置函数替代手写循环
  5. 方向四:并行化——利用多核CPU
  6. 方向五:底层加速——Cython/Numba
  7. 实战经验:100万行数据从10分钟到12秒
  8. Python性能优化的系统方法论
  9. 面试高频追问
  10. 总结

先说结论

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查找改set3分钟3倍
步骤二iterrows改向量化25秒7倍
步骤三推导式替代for+append18秒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年大数据老兵,只讲大数据面试干货

刷题进度保存在当前浏览器