python - 调用外部模块时多处理池变慢
问题描述
我的脚本调用librosa模块来计算短音频的梅尔频率倒谱系数 (MFCC)。加载音频后,我想尽可能快地计算这些(以及其他一些音频功能) - 因此进行多处理。
问题:多处理变体比顺序处理慢得多。分析表明我的代码 90% 以上的时间都花在<method 'acquire' of '_thread.lock' objects>
. 如果它是许多小任务,这并不奇怪,但在一个测试案例中,我将我的音频分成 4 个块,然后在单独的进程中处理。我认为开销应该是最小的,但在实践中,它几乎和许多小任务一样糟糕。
据我了解,多处理模块应该分叉几乎所有东西,并且不应该有任何争夺锁。然而,结果似乎显示了一些不同的东西。会不会是下面的librosa模块保留了某种内部锁?
我的分析结果为纯文本:https ://drive.google.com/open?id=17DHfmwtVOJOZVnwIueeoWClUaWkvhTPc
作为图片:https ://drive.google.com/open?id=1KuZyo0CurHd9GjXge5CYQhdWn2Q6OG8Z
重现“问题”的代码:
import time
import numpy as np
import librosa
from functools import partial
from multiprocessing import Pool
n_proc = 4
y, sr = librosa.load(librosa.util.example_audio_file(), duration=60) # load audio sample
y = np.repeat(y, 10) # repeat signal so that we can get more reliable measurements
sample_len = int(sr * 0.2) # We will compute MFCC for short pieces of audio
def get_mfcc_in_loop(audio, sr, sample_len):
# We split long array into small ones of lenth sample_len
y_windowed = np.array_split(audio, np.arange(sample_len, len(audio), sample_len))
for sample in y_windowed:
mfcc = librosa.feature.mfcc(y=sample, sr=sr)
start = time.time()
get_mfcc_in_loop(y, sr, sample_len)
print('Time single process:', time.time() - start)
# Let's test now feeding these small arrays to pool of 4 workers. Since computing
# MFCCs for these small arrays is fast, I'd expect this to be not that fast
start = time.time()
y_windowed = np.array_split(y, np.arange(sample_len, len(y), sample_len))
with Pool(n_proc) as pool:
func = partial(librosa.feature.mfcc, sr=sr)
result = pool.map(func, y_windowed)
print('Time multiprocessing (many small tasks):', time.time() - start)
# Here we split the audio into 4 chunks and process them separately. This I'd expect
# to be fast and somehow it isn't. What could be the cause? Anything to do about it?
start = time.time()
y_split = np.array_split(y, n_proc)
with Pool(n_proc) as pool:
func = partial(get_mfcc_in_loop, sr=sr, sample_len=sample_len)
result = pool.map(func, y_split)
print('Time multiprocessing (a few large tasks):', time.time() - start)
我的机器上的结果:
- 单进程时间:8.48s
- 时间多处理(许多小任务):44.20s
- 时间多处理(一些大型任务):41.99s
任何想法是什么原因造成的?更好的是,如何使它变得更好?
解决方案
为了调查发生了什么,我运行top -H
并注意到产生了 +60 个线程!就是这样。原来librosa和依赖项产生了许多额外的线程,这些线程一起破坏了并行性。
解决方案
joblib 文档中很好地描述了超额订阅的问题。那我们就用它吧。
import time
import numpy as np
import librosa
from joblib import Parallel, delayed
n_proc = 4
y, sr = librosa.load(librosa.util.example_audio_file(), duration=60) # load audio sample
y = np.repeat(y, 10) # repeat signal so that we can get more reliable measurements
sample_len = int(sr * 0.2) # We will compute MFCC for short pieces of audio
def get_mfcc_in_loop(audio, sr, sample_len):
# We split long array into small ones of lenth sample_len
y_windowed = np.array_split(audio, np.arange(sample_len, len(audio), sample_len))
for sample in y_windowed:
mfcc = librosa.feature.mfcc(y=sample, sr=sr)
start = time.time()
y_windowed = np.array_split(y, np.arange(sample_len, len(y), sample_len))
Parallel(n_jobs=n_proc, backend='multiprocessing')(delayed(get_mfcc_in_loop)(audio=data, sr=sr, sample_len=sample_len) for data in y_windowed)
print('Time multiprocessing with joblib (many small tasks):', time.time() - start)
y_split = np.array_split(y, n_proc)
start = time.time()
Parallel(n_jobs=n_proc, backend='multiprocessing')(delayed(get_mfcc_in_loop)(audio=data, sr=sr, sample_len=sample_len) for data in y_split)
print('Time multiprocessing with joblib (a few large tasks):', time.time() - start)
结果:
- 使用 joblib 进行时间多处理(许多小任务):2.66
- 使用 joblib 进行时间多处理(一些大型任务):2.65
比使用多处理模块快 15 倍。
推荐阅读
- elasticsearch - Elastichsearch 匹配查询在我的产品服务器上不起作用
- excel - 如何在 LibreOffice Calc 中使用下拉菜单或选择框显示和隐藏图表?
- sql - 使用实体框架订购混合字符串/数字列
- asp.net - ASP.NET Core 上的身份验证在 IIS 中不起作用
- java - 仅对按钮的颜色进行动画处理
- python - 使用最新版本创建新站点或使用旧站点
- java - 如何回滚 android sqlite 数据库中已删除的行?
- c++ - 如何修复“g++:致命错误:没有输入文件编译终止。” 错误
- macos - MacOs 的 Docker-desktop 无法在 Preferences/File-Sharing 中添加 /usr/local 文件夹
- c# - 在长时间运行的工作中防止更改