如何加速代码瓶颈段?线程还是多进程?附NetworkX代码场景
瓶颈函数优化 & 线程/多进程选择指南
咱们先从瓶颈优化入手,再聊线程和多进程的取舍:
一、瓶颈函数的具体优化思路
针对你代码里的瓶颈段,这些方法能直接提升效率:
- 缓存重复计算结果:路径计算(比如NetworkX的最短路径)如果遇到重复的
(source, dest)节点对,完全可以把结果存在self.my_dict里。比如把键设为(source, dest),值存计算好的路径,下次触发事件时先查字典,存在就直接用,不用重复计算——这对重复请求多的场景提升特别明显。 - 预计算高频节点对:如果某些节点组合经常被请求,可以在类初始化(
__init__)或者系统空闲时提前算好路径,存在字典里,事件触发时直接取结果,省掉实时计算的时间。 - 优化路径计算算法:NetworkX里不同算法效率差很多:如果是无权重图,默认的BFS算法(
nx.shortest_path)已经很快;如果是有权重图,试试更高效的优化版Dijkstra算法,或者如果只需要路径长度而非具体路径,用nx.shortest_path_length会比返回完整路径快不少。 - 干掉不必要的函数嵌套:每次触发
slow_function都重新定义bottleneck_function会有额外开销,直接把嵌套里的逻辑移到slow_function里,或者单独定义成类的方法,比如self._calculate_path(source, dest, self.fast_obj),减少函数定义的成本。 - 预初始化常用对象:如果
reasonably_fast('myfoo')创建耗时,而且每次参数都是'myfoo',直接把它初始化在__init__里:self.fast_obj = reasonably_fast('myfoo'),触发事件时直接用这个预创建的对象,不用重复实例化。
二、线程还是多进程?怎么选?
这个得看你的瓶颈到底是CPU密集还是IO密集,结合Python的GIL特性来判断:
- 如果是CPU密集型(比如纯路径计算,无IO等待):选多进程。因为Python的GIL会让线程在CPU密集任务下无法真正并行(同一时间只有一个线程执行Python字节码),多进程可以绕过GIL,充分利用多核CPU。但要注意:如果你的
self.graph很大,每个进程会复制一份内存,开销不小——这时候可以考虑把图放在一个单独的进程里,其他进程通过IPC(进程间通信)请求计算结果,或者用共享内存来存储图。 - 如果是IO密集型(比如
reasonably_fast涉及磁盘/网络IO,或者路径计算需要等外部资源):选线程。IO等待时GIL会自动释放,线程可以切换执行,而且线程的创建、切换开销比进程小得多,适合高频触发的事件。 - 额外注意点:
- 如果事件触发特别频繁,用线程池(
concurrent.futures.ThreadPoolExecutor)比手动创建线程高效;如果是偶尔的大计算任务,多进程池(ProcessPoolExecutor)更划算。 - 如果只是读取
self.graph(路径计算一般是读操作),线程不需要加锁;但如果涉及修改图,线程要注意同步,而多进程因为有独立内存空间,修改不会互相影响,但同步数据会有开销。
- 如果事件触发特别频繁,用线程池(
最后提个关键建议:先做性能 profiling!用cProfile跑一下代码,精准定位到底是哪部分最耗时——是对象创建?还是路径计算?还是其他逻辑?针对性优化比盲目用多线程/多进程效果好得多。
内容的提问来源于stack exchange,提问作者Niloy Saha
相关产品推荐
相关产品推荐

