Python多线程导出任务性能异常:GIL持有主体与释放方法咨询
Python多线程导出任务性能异常:GIL持有主体与释放方法咨询
大家好,最近我在测试用Python多线程导出带样式的DataFrame到图片时,碰到了一个反直觉的性能问题,想请教下各位大佬关于GIL相关的疑问。
先贴出我的测试代码:
from concurrent.futures import ThreadPoolExecutor import time import io import pandas as pd from image_exporter import export import numpy as np df1 = pd.DataFrame(np.random.rand (6, 4)) df2 = pd.DataFrame(np.random.rand (6, 4)) df3 = pd.DataFrame(np.random.rand (6, 4)) df4 = pd.DataFrame(np.random.rand (6, 4)) def get_exported(df): s = time.time() buffer = io.BytesIO() export(df.style.background_gradient(), buffer) buffer.seek(0) print('get_exported', time.time() - s) def test_parallel(): tasks = [df1, df2, df3, df4] with ThreadPoolExecutor() as executor: executor.map(get_exported, tasks) def test_seq(): tasks = [df1, df2, df3, df4] for t in tasks: get_exported(t) s = time.time() test_parallel() print('Total time parallel', time.time() - s) s = time.time() test_seq() print('Total time sequential', time.time() - s)
其中image_exporter.py里的export方法实现如下:
import matplotlib.pyplot as plt from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure import imgkit def export(style_obj, output_buffer): html_content = style_obj.render() fig = Figure(figsize=(10, 6), dpi=100) canvas = FigureCanvasAgg(fig) ax = fig.add_subplot(111) ax.axis('off') # 将HTML转为图片并渲染到画布 img_data = imgkit.from_string(html_content, False) ax.imshow(plt.imread(img_data)) canvas.print_png(output_buffer)
测试结果
在我的机器上,测试数据完全不符合预期:
- 串行执行
test_seq:单次get_exported调用耗时约0.035秒,总耗时约0.14秒 - 多线程执行
test_parallel:单次get_exported调用耗时反而涨到了约0.18秒,总耗时仅约0.19秒,只比串行快了一点点
我原本以为多线程能利用多核并行处理,结果反而单个任务耗时变长,整体性能提升微乎其微。我猜测可能是export方法里的某些操作一直持有GIL,导致多线程无法真正并行。
想请教大家:
- 这个场景里,哪些操作可能会持续持有GIL?比如
imgkit的调用、matplotlib的渲染,还是Pandas的style渲染? - 有没有办法让这些操作释放GIL,从而提升多线程的性能?或者有没有其他替代方案来优化这类导出任务的并行效率?
备注:内容来源于stack exchange,提问作者radio23
相关产品推荐
相关产品推荐

