我正在尝试在 python 中读取数千小时的 wav 文件并获取它们的持续时间。这基本上需要打开 wav 文件,获取帧数并考虑采样率。以下是相关代码:
def wav_duration(file_name):
wv = wave.open(file_name, 'r')
nframes = wv.getnframes()
samp_rate = wv.getframerate()
duration = nframes / samp_rate
wv.close()
return duration
def build_datum(wav_file):
key = "/".join(wav_file.split('/')[-3:])[:-4]
try:
datum = {"wav_file" : wav_file,
"labels" : all_labels[key],
"duration" : wav_duration(wav_file)}
return datum
except KeyError:
return "key_error"
except:
return "wav_error"
按顺序执行此操作将花费太长时间。我的理解是多线程在这里应该有所帮助,因为它本质上是一个 IO 任务。因此,我这样做:
all_wav_files = all_wav_files[:1000000]
data, key_errors, wav_errors = list(), list(), list()
start = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
# submit jobs and get the mapping from futures to wav_file
future2wav = {executor.submit(build_datum, wav_file): wav_file for wav_file in all_wav_files}
for future in concurrent.futures.as_completed(future2wav):
wav_file = future2wav[future]
try:
datum = future.result()
if datum == "key_error":
key_errors.append(wav_file)
elif datum == "wav_error":
wav_errors.append(wav_file)
else:
data.append(datum)
except:
print("Generated exception from thread processing: {}".format(wav_file))
print("Time : {}".format(time.time() - start))
令我沮丧的是,我得到了以下结果(以秒为单位):
Num threads | 100k wavs | 1M wavs
1 | 4.5 | 39.5
2 | 6.8 | 54.77
10 | 9.5 | 64.14
100 | 9.07 | 68.55
这是预期的吗?这是 CPU 密集型任务吗?多重处理会有帮助吗?我怎样才能加快速度?我正在从本地驱动器读取文件,这是在 Jupyter 笔记本上运行的。 Python 3.5。
编辑:我知道 GIL。我只是假设打开和关闭文件本质上是 IO。 People's analysis已经表明,在 IO 情况下,使用多处理可能会适得其反。因此我决定改用多处理。
我想现在的问题是:这个任务是否受 IO 限制?
编辑编辑:对于那些想知道的人,我认为它受 CPU 限制(一个内核已达到 100%)。这里的教训是不要对任务做出假设并自己检查。
请您参考如下方法:
一些要按类别检查的内容:
代码
- wave.open 的效率如何?当它可以简单地读取标题信息时,它是否将整个文件加载到内存中?
- 为什么将 max_workers 设置为 1?
- 你试过使用cProfile吗?甚至 timeit了解代码的哪个特定部分花费了更多时间?
硬件
使用一些硬盘事件、内存使用和 CPU 监控重新运行您现有的设置,以确认硬件不是您的限制因素。如果您看到您的硬盘以最大 IO 运行,您的内存已满或所有 CPU 核心都达到 100% - 其中之一可能已达到极限。
全局解释器锁 (GIL)
如果没有明显的硬件限制,您很可能会遇到 Python 的全局解释器锁 (GIL) 问题,如 this answer 中所述。 .如果您的代码仅限于在单核上运行或者在运行的线程中没有有效的并发性,则这种行为是可以预料的。 在这种情况下,我肯定会更改为 multiprocessing ,首先为每个 CPU 内核创建一个进程,运行该进程,然后将硬件监控结果与上一次运行进行比较。
