os.listdir多次运行后性能下降问题排查求助
问题:多次运行os.listdir遍历大量文件后耗时剧增
dir_ = "/path/to/folder/with/huge/number/of/files" subdirs = [os.path.join(dir_, file) for file in os.listdir(dir_)] # one of subdirs contain huge number of files files = [os.path.join(file, f) for file in subdirs for f in os.listdir(file)]
这段代码首次运行能在30秒内完成,但多次运行后耗时涨到11分钟,现在甚至跑不完,怀疑是os.listdir导致异常。
补充说明:仅需读取文件路径传入多进程函数,内存充足,程序占用内存不到总量的1/10。
可能的原因
- 系统缓存失效:首次运行时操作系统会把目录的元数据缓存到内存,后续运行时缓存可能被其他进程占用、系统主动回收,导致每次都要从磁盘读取原始数据,磁盘IO速度远慢于内存,耗时剧增。
- 大目录遍历开销:某个子目录文件数量极大,
os.listdir会一次性把所有文件名加载到内存列表中,多次运行时如果系统资源调度紧张,会进一步放大这种遍历的开销。
优化方案
- 用
os.scandir替代os.listdiros.scandir是Python 3.5+引入的API,比os.listdir效率更高:它直接返回包含文件路径和属性的DirEntry对象,无需额外拼接路径;同时能利用操作系统的底层缓存机制,减少磁盘IO操作。
示例代码:
import os dir_ = "/path/to/folder/with/huge/number/of/files" files = [] for subdir in os.scandir(dir_): if subdir.is_dir(): for entry in os.scandir(subdir.path): if entry.is_file(): files.append(entry.path)
- 改用生成器避免一次性构建大列表
如果不需要提前把所有文件路径存在内存里,用生成器逐个输出路径,既能减少内存占用(哪怕你内存充足,也能避免大列表的构建开销),还能直接对接多进程的迭代需求:
import os def get_file_paths(root_dir): for subdir in os.scandir(root_dir): if subdir.is_dir(): for entry in os.scandir(subdir.path): if entry.is_file(): yield entry.path dir_ = "/path/to/folder/with/huge/number/of/files" # 生成器直接传给多进程函数,比如multiprocessing.Pool的imap方法 file_paths = get_file_paths(dir_)
- 排查系统层面问题
可以用系统工具(如iostat、vmstat)检查磁盘IO负载、内存缓存状态,看看是否有其他进程在抢占该目录的磁盘资源,或者磁盘是否出现碎片化、文件系统错误等情况。
内容的提问来源于stack exchange,提问作者Mann
相关产品推荐
相关产品推荐

