【发布时间】:2018-06-02 01:56:31
【问题描述】:
我有一些分类器,我想对一个样本进行评估。此任务可以并行运行,因为它们彼此独立。这意味着我想并行化它。
我用 python 和 bash 脚本尝试过。问题是,当我第一次运行该程序时,大约需要 30 到 40 秒才能完成。当我连续多次运行该程序时,只需 1s-3s 即可完成。即使我给分类器提供不同的输入,我也会得到不同的结果,所以似乎没有缓存。当我运行一些其他程序然后重新运行该程序时,它又需要 40 秒才能完成。
我还在 htop 中观察到,当程序第一次运行时,CPU 的利用率并不高,但是当我一次又一次地重新运行它时,CPU 就被充分利用了。
有人可以解释一下这种奇怪的行为吗?我怎样才能避免它,即使程序的第一次运行也会很快?
这是python代码:
import time
import os
from fastText import load_model
from joblib import delayed, Parallel, cpu_count
import json
os.system("taskset -p 0xff %d" % os.getpid())
def format_duration(start_time, end_time):
m, s = divmod(end_time - start_time, 60)
h, m = divmod(m, 60)
return "%d:%02d:%02d" % (h, m, s)
def classify(x, classifier_name, path):
f = load_model(path + os.path.sep + classifier_name)
labels, probabilities = f.predict(x, 2)
if labels[0] == '__label__True':
return classifier_name
else:
return None
if __name__ == '__main__':
with open('classifier_names.json') as json_data:
classifiers = json.load(json_data)
x = "input_text"
Parallel(n_jobs=cpu_count(), verbose=100, backend='multiprocessing', pre_dispatch='all') \
(delayed(perform_binary_classification)
(x, classifier, 'clfs/') for
classifier in classifiers)
end_time = time.time()
print(format_duration(start_time, end_time))
这是 bash 代码:
#!/usr/bin/env bash
N=4
START_TIME=$SECONDS
open_sem(){
mkfifo pipe-$$
exec 3<>pipe-$$
rm pipe-$$
local i=$1
for((;i>0;i--)); do
printf %s 000 >&3
done
}
run_with_lock(){
local x
read -u 3 -n 3 x && ((0==x)) || exit $x
(
"$@"
printf '%.3d' $? >&3
)&
}
open_sem $N
for d in classifiers/* ; do
run_with_lock ~/fastText/fasttext predict "$d" test.txt
done
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo time taken $ELAPSED_TIME seconds
已编辑
更大的图景是我正在使用 2 个 API 方法运行烧瓶应用程序。他们每个人都调用并行化分类的函数。当我在做请求时,它的行为方式与下面的这个程序相同。对方法 A 的第一次请求需要很多时间,然后后续请求需要 1 秒。当我切换到方法 B 时,它的行为与方法 A 相同。如果我在方法 A 和方法 B 之间多次切换,例如 A、B、A、B,那么每个请求大约需要 40 秒才能完成。
【问题讨论】:
-
可能是因为它必须加载库 int 内存和缓存程序等。
-
@WillemVanOnsem 但它应该释放内存对吗?为什么在没有导入库的 bash 脚本中也会观察到类似的行为。 RAM 总是在程序运行后被释放。如果它以某种方式缓存程序,我该如何预先缓存它?
-
没有程序留在内存中,除非需要内存。它是“免费的”,因为它可以被其他东西占用,但现代操作系统会标记最初存在的内容,因为您可能希望以后重用相同的程序。看是这样的:你执行程序
a,程序a(部分)加载到内存中,程序a终止,操作系统将a的内存标记为空闲,你再次执行a,每次你如果它仍在内存中的某处,则加载操作系统看起来的一段程序。 -
另外,如果你在内存中有一个程序的实例并且你执行另一个实例,则代码部分在实例之间共享(并且不需要再次加载),而每个实例都有自己的自己的数据。当您第一次执行时,您想要执行的“预加载”由操作系统为您完成。
-
好的。我得到了它。那么为什么 htop 中的核心第一次没有显示出高利用率?然后他们通过连续调用程序来显示它。顺便说一句,我更新了问题并添加了我的问题的更大图景。
标签: python bash parallel-processing joblib