无法从IO任务中的Python多线程中受益?

时间:2018-07-04 01:53:49

标签: python multithreading python-3.x multiprocessing

我正在尝试在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%)。这里的教训是不要对任务做任何假设,而是自己检查一下。

1 个答案:

答案 0 :(得分:1)

按类别检查的一些事情:

代码

  • wave.open的效率如何?当它只是读取标题信息时,是否会将整个文件加载到内存中?
  • 为什么将max_workers设置为1?
  • 您是否尝试过使用cProfile甚至timeit来了解代码的哪个特定部分会花费更多时间?

硬件

通过一些硬盘活动,内存使用和CPU监视重新运行现有设置,以确认硬件不是您的限制因素。如果您看到硬盘以最大IO运行,则内存已满或所有CPU内核都达到100%-其中之一可能已达到极限。

全局解释器锁定(GIL)

如果没有明显的硬件限制,很可能会遇到Python的全局解释器锁(GIL)的问题,如this answer中所述。如果您的代码仅限于在单个内核上运行,或者正在运行的线程中没有有效的并发,则可能会出现这种现象。  在这种情况下,我肯定会更改为multiprocessing,首先是为每个CPU内核创建一个进程,然后运行该进程,然后将硬件监视结果与上一次运行进行比较。