【发布时间】:2021-12-28 16:21:16
【问题描述】:
根据我阅读的内容 - 例如here - 我了解 I/O 操作会释放 GIL。所以,如果我必须读取本地文件系统上的大量文件,我的理解是线程执行应该加快速度。
为了测试这一点——我有一个文件夹 (input),里面有大约 100k 个文件——每个文件只有一行和一个随机整数。我有两个函数 - 一个“顺序”和一个“并发”,只是添加所有数字
import glob
import concurrent.futures
ALL_FILES = glob.glob('./input/*.txt')
def extract_num_from_file(fname):
#time.sleep(0.1)
with open(fname, 'r') as f:
file_contents = int(f.read().strip())
return file_contents
def seq_sum_map_based():
return sum(map(extract_num_from_file, ALL_FILES))
def conc_sum_map_based():
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
return sum(executor.map(extract_num_from_file, ALL_FILES))
虽然这两个函数给我的结果相同 - “并发”版本慢了大约 3-4 倍。
In [2]: %timeit ss.seq_sum_map_based()
3.77 s ± 50.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %timeit ss.conc_sum_map_based()
12.8 s ± 240 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
我的代码或我的理解有问题吗?
【问题讨论】:
-
我对此不是很胜任,但我认为并发版本只是在生成线程、切换上下文、传递 GIL 等方面浪费了额外的资源。如果 CPU工作比简单的添加更密集。需要分析您的代码才能确定
-
如果它与您提到的开销有关,我认为增加要读取的文件数量应该会对性能差异产生影响。但我没有看到 - 对于 10 个文件或 100k 个文件,并发版本仍然同样慢
-
我怀疑磁盘 I/O 是否可以很好地并行化,至少在通常的磁盘/文件系统上是这样。最后,所有的 I/O 操作都必须排在同一个磁盘上。
-
线程肯定可以更快,但如果你有空闲的 I/O,它只会加快速度。如果您的磁盘读取速度很慢/饱和,那么线程对您没有好处......在许多情况下,它实际上会减慢您的速度,因为随机读取比顺序读取慢得多。最好的情况是文件有一些相当大的大小,可以利用顺序读取(更多的 I/O 来玩)。在我自己的 SSD 上测试了大约 1000 个不同大小的文件(平均 8800 个字符),5 个线程的线程确实有所帮助,3 个线程甚至更快。
标签: python python-3.x multithreading concurrency gil