我有一个带有繁重计算的代码:执行pred(In, Out)
让我们说64K次,每次执行需要1-10秒。
我想使用多线程(64)机器来加速这个过程。
我使用concurrent_maplist
:
concurrent_maplist(pred, List_of_64K_In, List_of_64K_Out).
我的速度增加了大约8倍但不超过这个速度。
我认为原因是concurrent_maplist
上的以下通知:
请注意,此谓词的开销很大 因此,在达到加速之前,目标必须相当昂贵。
为了使目标相当昂贵,我将代码修改为:
% putting 1K pred/2 in heavy_pred/2
concurrent_maplist(heavy_pred, List_of_64_List_of_1k_In, List_of_64_List_of_1k_Out).
heavy_pred(List_of_In, List_of_Out) :-
maplist(pred, List_of_In, List_of_Out).
令人惊讶的是(对我而言),我没有进一步加快这一变化。
我想知道如何通过多线程进一步加快速度?
其他一些细节:
架构:x86_64, AMD, 14.04.1-Ubuntu
。
swipl -v:SWI-Prolog version 6.6.4 for amd64
。
pred / 2是一个定理证明器,它采用公式并试图证明它们。
它使用的标准谓词几乎没有非标准谓词:cyclic_term/1
,write/1
,copy_term/2
等。
答案 0 :(得分:0)
要使内核正常工作,可以使用可由应用程序启动的线程。使用最新的swipl版本可以帮助您获得其提供的最新改进。
您还可以启动多个应用程序,每个应用程序例如8个线程。结果:更多的内核可以工作,尽管与运行具有更多线程的单个应用程序相比,总内核占用的内存更多。现在,您将需要管理不同的应用程序实例,以便整体工作进行(并且不会重复在其他应用程序上完成的工作)。我想即使在具有多个线程的单个应用程序的情况下也需要这样做。我的答案在技术上不是很严格,但是可以让您为CPU密集型工作使用更多的内核。
答案 1 :(得分:0)
当结果合并时,多核 CPU 不会线性扩展。我检查了 concurrent_maplist/2 和新的 concurrent_and/2 之间是否存在差异。我使用的示例包含来自 here 的相当大的工作项。
我添加了对 concurrent_maplist/2 的测试:
/* parallel, concurrent_maplist */
count3(N) :-
findall(Y, between(1, 1000, Y), L),
concurrent_maplist(slice, L, R),
aggregate_all(sum(M), member(M, R), N).
有趣的是 concurrent_and/2 和 concurrent_maplist/2 之间没有太大区别。可能是因为工作项的数量只有 1000,因为工作项本身相当大。尽管如此,还是检查了 1'000'000 个数字的素性:
?- current_prolog_flag(cpu_count, X).
X = 8.
/* sequential */
?- time(count(N)).
% 138,647,812 inferences, 9.781 CPU in 9.792 seconds (100% CPU, 14174856 Lips)
N = 78499.
/* parallel, concurrent_and */
?- time(count2(N)).
% 4,450 inferences, 0.000 CPU in 2.458 seconds (0% CPU, Infinite Lips)
N = 78499.
/* parallel, concurrent_maplist */
?- time(count3(N)).
% 23,183 inferences, 0.000 CPU in 2.423 seconds (0% CPU, Infinite Lips)
N = 78499.
虽然机器有 8 个逻辑核心,但多亏了超线程。加速接近物理核心的数量,只有 4 个。所以如果你的机器有 64 个逻辑核心,这就是 cpu_count Prolog 标志返回的,你应该检查有多少物理核心。