
当我们想要优化软件时,首先要做的事情之一就是查看……
在分析数据时。软件分析器是一种尝试识别数据偏差的工具。
你的软件会消耗大量时间。虽然具体方法可能有所不同,但典型的性能分析器会对你的软件进行采样(定期执行单步执行),并收集统计数据。如果你的软件经常在某个特定函数处停止运行,那么该函数很可能消耗了大量时间。因此,这或许正是你应该着重优化的地方。
Matteo Collina 最近与我分享了他关于如何利用 JavaScript 中的性能分析数据进行软件优化的研究成果。简而言之,Matteo 获取性能分析数据,并将其转换为 AI 可以理解的形式。这个思路简单却引人入胜:告诉 AI 如何获取性能分析数据,然后让它优化你的代码,例如通过反复分析代码来实现。这个想法并非原创,因为 AI 工具本身就能找到获取性能分析数据的方法。
它的效果如何?我得亲自试一试。
案例 1. 代码合并脚本
对于 simdutf 软件库, 我们使用合并脚本:它收集磁盘上的所有 C++ 文件,进行一些浅层解析,并根据一些规则将它们粘合在一起。
我首先让AI在无法访问性能分析数据的情况下优化脚本。它立即添加了一个文件缓存。脚本会反复从磁盘加载相同的文件(脚本比较复杂)。这节省了大约20%的运行时间。
具体来说,人工智能替换了这段原始代码……
def read_file ( file ): with open ( file , 'r' ) as f : for line in f : yield line . rstrip ()通过这个带缓存的版本……def read_file ( file ): with open ( file , 'r' ) as f : for line in f : yield line . rstrip ()
def read_file ( file ): if file in file_cache : for line in file_cache [ file ]: yield line else : lines = [] with open ( file , 'r' ) as f : for line in f : line = line . rstrip () lines . append ( line ) yield line file_cache [ file ] = linesAI能否更好地利用分析数据?我指示它运行Python分析器:def read_file ( file ): if file in file_cache : for line in file_cache [ file ]: yield line else : lines = [] with open ( file , 'r' ) as f : for line in f : line = line . rstrip () lines . append ( line ) yield line file_cache [ file ] = lines
python -m cProfile -s cumtime myprogram.py . 它发现了两个额外的优化:
1. 它预编译了正则表达式( re.compile )。它替换了
if re . match ( '.*generic/.*.h' , file ): # ...经过if re . match ( '.*generic/.*.h' , file ): # ...
if generic_pattern . match ( file ): # ...而在代码的其他地方,我们有……if generic_pattern . match ( file ): # ...
generic_pattern = re . compile ( r'.*generic/.*\.h' )
2. 它没有反复调用re.sub来进行正则表达式替换,而是先检查字符串中是否存在关键字,从而过滤字符串。
if 'SIMDUTF_IMPLEMENTATION' in line : # This IF is the optimization print ( uses_simdutf_implementation . sub ( context . current_implementation + "\\1" , line ), file = fid ) else : print ( line , file = fid ) # Fast path这两个优化点或许可以直接通过查看代码得出,我无法确定它们是否是由性能分析数据驱动的。但我可以肯定的是,它们确实出现在性能分析数据中。if 'SIMDUTF_IMPLEMENTATION' in line : # This IF is the optimization print ( uses_simdutf_implementation . sub ( context . current_implementation + "\\1" , line ), file = fid ) else : print ( line , file = fid ) # Fast path
遗憾的是,缓存文件访问这种最容易实现的优化措施已经带来了大部分的性能提升。人工智能无法进一步优化代码,因此性能分析数据并没有起到太大作用。
案例 2:检查链接脚本
我在设计在线课程时,经常会用到很多链接。这些链接会随着时间推移而失效。所以我写了一个简单的Python脚本,用来遍历所有链接并进行验证。
我首先让我的AI优化代码。它使用了同样的正则表达式技巧,编译了正则表达式。它创建了一个线程池,并将脚本设置为异步运行。
with concurrent . futures . ThreadPoolExecutor ( max_workers = 10 ) as executor : url_results = { url : executor . submit ( check_url , url ) for url in urls_to_check } for url , future in url_results . items (): url_cache [ url ] = future . result ()这种并行化使脚本运行速度提高了一倍以上。with concurrent . futures . ThreadPoolExecutor ( max_workers = 10 ) as executor : url_results = { url : executor . submit ( check_url , url ) for url in urls_to_check } for url , future in url_results . items (): url_cache [ url ] = future . result ()
它使用functools以一种有趣的方式缓存了 URL 检查:
from functools import lru_cache @lru_cache ( maxsize = None ) def check ( link ): # ...我之前不知道这个小技巧。不过在我的情况下,这个技巧没什么用,因为我很少会多次使用同一个链接。from functools import lru_cache @lru_cache ( maxsize = None ) def check ( link ): # ...
然后我重新开始,并让它使用性能分析器。它执行的操作基本相同,只是优化了正则表达式。
据我观察,除了多线程优化之外,其他所有优化都徒劳无功。而且,即使没有性能分析数据,它也能完成这部分工作。
目前为止的结论
我尝试的Python脚本并没有进行深度优化,因为它们的性能要求并不高。这些脚本相对简单。
由于文件缓存,合并后的性能提升了 20%,这算是“免费”获得的。链接检查器现在也支持多线程,速度会更快。这两项优化都有效且实用,能略微改善我的工作体验。
在这两种情况下,我都未能从性能分析器数据中看出任何益处。我最初希望人工智能能够循环运行性能分析器,持续优化代码,但在这些简单的案例中并没有实现。根据性能分析器数据,人工智能优化的代码段对运行时间的贡献微乎其微。
平心而论,性能分析数据通常用途有限。真正的问题往往出在架构上,而非具体的瓶颈。即使存在可识别的瓶颈,简单的性能分析也可能无法将其清晰地呈现出来。此外,性能分析器的作用会随着代码库的增长而增强,而我的测试用例却非常小。
总的来说,我认为我相对失败的主要原因是缺乏合适的应用场景。我认为,目前来说,收集用户画像数据并让人工智能进行分析或许是一个合理的第一步。
原文: https://lemire.me/blog/2026/01/25/optimizing-python-scripts-with-ai/