我有一个包含多个数据框的列表。这些数据帧可能非常大,写入 csv 文件需要一些时间。我正在尝试使用 Pandas 将它们同时写入 csv 文件,并尝试使用多线程来减少时间。为什么多线程版本比顺序版本花费更多时间?使用 Pandas 将文件写入 csv 是不是 IO 绑定进程,还是我没有正确实现它?
多线程:
list_of_dfs = [df_a, df_b, df_c]
start = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
results = executor.map(lambda x: list_of_dfs[x].to_csv('Rough/'+str(x)+'.csv', index=False), range(0,3))
print(time.time()-start)
>>> 18.202364921569824
顺序:
start = time.time()
for i in range(0,3):
list_of_dfs[i].to_csv('Rough/'+str(i)+'.csv', index=False)
print(time.time() - start)
>>> 13.783314228057861
答案 0 :(得分:1)
我假设您使用的是常用的 CPython 解释器。
<块引用>为什么多线程版本比顺序版本花费更多时间?
答案可能在于 CPython 全局解释器锁 (GIL)。
确实,Pandas 使用 CPython 的内部 csv 库来编写 CSV 文件。但是,AFAIK,csv 库(用 C 编写)从内存中读取基本的 Python 类型(因此它不知道 Numpy)并将它们格式化为字符串,以便它们可以写入您的存储设备。对 CPython 对象的访问受 GIL 保护,防止任何加速(假设大部分时间都花在访问 CPython 对象上)。
<块引用>使用 Pandas 将文件写入 csv 是不是 IO 绑定进程,还是我没有正确实现它?
使用 Pandas 编写 CSV 文件显然在现代机器上不受 IO 限制(使用任何不错的 SSD)。格式化过程非常缓慢(整数和浮点数转换以及字符串处理)并且应该花费大部分时间。此外,它与缓慢的 CPython 对象访问交错。这就解释了为什么你不应该得到任何加速。
两个线程之间的上下文切换通常会导致性能降低。您可以在 CPython documentation 本身中找到更多相关信息:
<块引用>即使 GIL 不是瓶颈,它也会降低性能。总结链接的幻灯片:系统调用开销很大,尤其是在多核硬件上。两个线程调用一个函数所花费的时间可能是单个线程调用该函数两次所花费的时间的两倍。 GIL 会导致 I/O 密集型线程在 CPU 密集型线程之前被调度,并阻止信号的传递。
答案 1 :(得分:1)
另外:在这种情况下,“统治约束”确实是硬件。如果多个不同的线程或进程都在同时尝试写入数据,您可能会不经意间“让您那可怜的、勤劳的、公务员的磁盘驱动器变成猴子舞”。一次写入一个可能会更好,这样磁盘的读/写磁头机制就不必移动太多。