Ответ 1
Вкратце: я не могу воспроизвести вашу проблему. Если вы находитесь в Windows, вы должны использовать защитник для вашего основного цикла: документация joblib.Parallel
. Единственная проблема, которую я вижу, - это много накладных расходов на копирование данных, но ваши цифры кажутся нереалистичными из-за этого.
В долгу, вот мои тайминги с вашим кодом:
На моем i7 3770k (4 ядра, 8 потоков) я получаю следующие результаты для разных n_jobs
:
For-loop: Finished in 33.8521318436 sec
n_jobs=1: Finished in 33.5527760983 sec
n_jobs=2: Finished in 18.9543449879 sec
n_jobs=3: Finished in 13.4856410027 sec
n_jobs=4: Finished in 15.0832719803 sec
n_jobs=5: Finished in 14.7227740288 sec
n_jobs=6: Finished in 15.6106669903 sec
Таким образом, выигрыш в использовании нескольких процессов. Однако, хотя у меня есть четыре ядра, выигрыш уже насыщается при трех процессах. Поэтому я предполагаю, что время выполнения фактически ограничено доступом к памяти, а не временем процессора.
Вы должны заметить, что аргументы для каждой отдельной записи цикла копируются в процесс, выполняющий ее. Это означает, что вы копируете a
для каждого элемента в b
. Это неэффективно. Поэтому вместо этого вы получите доступ к глобальному a
. (Parallel
будет разветвлять процесс, копируя все глобальные переменные для вновь созданных процессов, поэтому a
доступен). Это дает мне следующий код (с настройкой времени и основного цикла, так как документация joblib
рекомендует:
import numpy as np
from matplotlib.path import Path
from joblib import Parallel, delayed
import time
import sys
## Check if one line segment contains another.
def check_paths(path):
for other_path in a:
res='no cross'
chck = Path(other_path)
if chck.contains_path(path)==1:
res= 'cross'
break
return res
if __name__ == '__main__':
## Create pairs of points for line segments
a = zip(np.random.rand(5000,2),np.random.rand(5000,2))
b = zip(np.random.rand(300,2),np.random.rand(300,2))
now = time.time()
if len(sys.argv) >= 2:
res = Parallel(n_jobs=int(sys.argv[1])) (delayed(check_paths) (Path(points)) for points in b)
else:
res = [check_paths(Path(points)) for points in b]
print "Finished in", time.time()-now , "sec"
Результаты синхронизации:
n_jobs=1: Finished in 34.2845709324 sec
n_jobs=2: Finished in 16.6254048347 sec
n_jobs=3: Finished in 11.219119072 sec
n_jobs=4: Finished in 8.61683392525 sec
n_jobs=5: Finished in 8.51907801628 sec
n_jobs=6: Finished in 8.21842098236 sec
n_jobs=7: Finished in 8.21816396713 sec
n_jobs=8: Finished in 7.81841087341 sec
Насыщенность теперь немного перемещена в n_jobs=4
, которая является ожидаемым значением.
check_paths
выполняет несколько избыточных вычислений, которые можно легко устранить. Во-первых, для всех элементов в other_paths=a
строка Path(...)
выполняется в каждом вызове. Предсчитайте это. Во-вторых, строка res='no cross'
записывается в каждом повороте цикла, хотя она может меняться только один раз (с последующим перерывом и возвратом). Переместите линию перед контуром. Затем код выглядит следующим образом:
import numpy as np
from matplotlib.path import Path
from joblib import Parallel, delayed
import time
import sys
## Check if one line segment contains another.
def check_paths(path):
#global a
#print(path, a[:10])
res='no cross'
for other_path in a:
if other_path.contains_path(path)==1:
res= 'cross'
break
return res
if __name__ == '__main__':
## Create pairs of points for line segments
a = zip(np.random.rand(5000,2),np.random.rand(5000,2))
a = [Path(x) for x in a]
b = zip(np.random.rand(300,2),np.random.rand(300,2))
now = time.time()
if len(sys.argv) >= 2:
res = Parallel(n_jobs=int(sys.argv[1])) (delayed(check_paths) (Path(points)) for points in b)
else:
res = [check_paths(Path(points)) for points in b]
print "Finished in", time.time()-now , "sec"
с таймингами:
n_jobs=1: Finished in 5.33742594719 sec
n_jobs=2: Finished in 2.70858597755 sec
n_jobs=3: Finished in 1.80810618401 sec
n_jobs=4: Finished in 1.40814709663 sec
n_jobs=5: Finished in 1.50854086876 sec
n_jobs=6: Finished in 1.50901818275 sec
n_jobs=7: Finished in 1.51030707359 sec
n_jobs=8: Finished in 1.51062297821 sec
Сторона node на вашем коде, хотя я не выполнял ее цели, поскольку это не было связано с вашим вопросом, contains_path
вернет только True
if this path completely contains the given path.
(см. документация). Поэтому ваша функция будет в основном всегда возвращать no cross
с учетом случайного ввода.