同时应用谓词来过滤列表(SWI Prolog)
Concurrently applying a predicate to filter a list (SWI Prolog)
我的问题:应用谓词过滤列表并行
我有一个列表,我有一个谓词。实际上,这是一个很长的列表,谓词需要一段时间。我只想输出列表中满足谓词的元素。
我可以访问很多 CPU 个核心,所以我认为尝试并行测试块中的列表元素是有意义的。
我试过的东西:
[concurrent_
]maplist
不起作用
看起来应该很容易。 concurrent_maplist
似乎是 maplist
.
的简单并发版本
根据 another answer on this site, maplist/3
should do exactly what I want. The docs 对于 SWI 的 maplist/3
建议,它的行为可能与答案中描述的不一样,但在评论中,答案的作者表明这是文档和它确实应该按预期工作。
它似乎对我不起作用。
我测试如下:
:- use_module(library(apply)).
f(A):-
(A*A < 10),
writeln(A).
set(0,[]).
set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T).
这不起作用:
?- set(4,L), concurrent_maplist(f, L, O).
ERROR: Undefined procedure: f/2
ERROR: However, there are definitions for:
ERROR: f/1
与普通 maplist
相同的问题:
set(4,L), maplist(f, L, O).
ERROR: Undefined procedure: f/2
ERROR: However, there are definitions for:
ERROR: f/1
include
有效但不平行
做的做我想做的(不是并行的)是include
:
?- set(11,L), include(f, L, O).
1
2
3
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
O = [1, 2, 3] .
concurrent
[编辑] 运行s!但它是平行的吗?
我一直在尝试使用 concurrent/3
partly following the example of another answer 来让它工作。
正在加载此文件:
set(0,[]):-!.
set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T).
f(A, B):-
(A*A < B),
writeln(A).
par_test(A, B):-
set(A, S),
findall(f(Good, B), (member(Good, S)), Goals),
concurrent(8, Goals, []).
...
?- set(4, S), findall(f(Good, 4), member(Good, S), Goals), concurrent(8, Goals, []).
1
false.
但是观察 htop
(用一个更长的 运行ning 谓词代替这个 f
),它似乎 运行 只在一个核心上 :- (.
如何并行执行此操作?
有没有办法用 concurrent_maplist
或类似简单的并行版本 include
或其他方法来实现我的目标?
maplist
将第一个参数作为谓词,并将作为参数的每个列表中的一个元素应用于此谓词,作为 maplist
的参数。这必然意味着 maplist
的所有列表参数都具有相同数量的元素。在这种情况下 f
接受一个参数,但 maplist(f, _, _)
期望 f
接受 2 个参数。因此,错误 Undefined procedure: f/2 这意味着 Prolog 找不到带有 2 个参数的 f
。
所以 maplist
并没有真正按照您的意愿行事。您想要在某些其他语言中称为 select
的东西,在这些语言中,您只在第二个列表中包含元素,这些元素通过了应用于第一个元素的过滤器。
这是一个示例实现:
select(_, [], []).
select(Filter, [R|Rs], Fs) :-
( call(Filter, R)
-> Fs = [R|SF]
; Fs = SF
),
select(Filter, Rs, SF).
并调用,例如,select(f, L, O)
。
如果像您的示例一样,您有任何带有 属性 的过滤器,它们将成功达到列表中的某个点,然后继续失败,您可能想要像这样优化过滤器,不要让它在第一次失败后继续遍历列表。
select_until_fail(_, [], []).
select_until_fail(Filter, [R|Rs], Fs) :-
( call(Filter, R)
-> Fs = [R|SF],
select_until_fail(Filter, Rs, SF)
; Fs = []
).
或类似的东西(未经测试)。
毕竟,我可能没有真正回答问题的 "concurrency" 部分。
让我们测试一下 concurrent_maplist:
test_concurrent_maplist(C) :-
numlist(1,C,Ns),
concurrent_maplist(prime,Ns,Ps),
sumlist(Ps,N),
format('we have ~d primes in first ~d natural numbers~n', [N,C]).
test_maplist(C) :-
numlist(1,C,Ns),
maplist(prime,Ns,Ps),
sumlist(Ps,N),
format('we have ~d primes in first ~d natural numbers~n', [N,C]).
prime(N,1) :-
prime(N), !.
prime(_,0).
% from https://en.wikipedia.org/wiki/Primality_test
prime(N) :- N =< 1, !, fail.
prime(N) :- N =< 3, !.
prime(N) :- (0 is N mod 2; 0 is N mod 3), !, fail.
prime(N) :- prime_(N,5).
prime_(N,I) :-
I * I =< N, !,
( ( 0 is N mod I; 0 is N mod (I + 2) ) -> fail
; J is I + 1, prime_(N,J)
).
prime_(_,_).
在 4 核机器上,它产生
?- time(test_concurrent_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 2,100,109 inferences, 1.799 CPU in 2.217 seconds (81% CPU, 1167205 Lips)
true.
?- time(test_concurrent_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 21,000,109 inferences, 18.244 CPU in 36.764 seconds (50% CPU, 1151063 Lips)
true.
?- time(test_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 16,151,437 inferences, 3.942 CPU in 3.951 seconds (100% CPU, 4096903 Lips)
true.
?- time(test_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 402,953,287 inferences, 102.334 CPU in 102.302 seconds (100% CPU, 3937617 Lips)
true.
毫无疑问,一个改进:
?- F1 is (2.217/3.951)*100, F2 is (36.764/102.334)*100.
对于更长的列表,我们接近运行时间的 1/3。
而不是 concurrent_maplist/3,我们可以坚持 concurrent_maplist/2 并将结果存储在数据库、全局变量等中...
我的问题:应用谓词过滤列表并行
我有一个列表,我有一个谓词。实际上,这是一个很长的列表,谓词需要一段时间。我只想输出列表中满足谓词的元素。
我可以访问很多 CPU 个核心,所以我认为尝试并行测试块中的列表元素是有意义的。
我试过的东西:
[concurrent_
]maplist
不起作用
看起来应该很容易。 concurrent_maplist
似乎是 maplist
.
根据 another answer on this site, maplist/3
should do exactly what I want. The docs 对于 SWI 的 maplist/3
建议,它的行为可能与答案中描述的不一样,但在评论中,答案的作者表明这是文档和它确实应该按预期工作。
它似乎对我不起作用。
我测试如下:
:- use_module(library(apply)).
f(A):-
(A*A < 10),
writeln(A).
set(0,[]).
set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T).
这不起作用:
?- set(4,L), concurrent_maplist(f, L, O).
ERROR: Undefined procedure: f/2
ERROR: However, there are definitions for:
ERROR: f/1
与普通 maplist
相同的问题:
set(4,L), maplist(f, L, O).
ERROR: Undefined procedure: f/2
ERROR: However, there are definitions for:
ERROR: f/1
include
有效但不平行
做的做我想做的(不是并行的)是include
:
?- set(11,L), include(f, L, O).
1
2
3
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
O = [1, 2, 3] .
concurrent
[编辑] 运行s!但它是平行的吗?
我一直在尝试使用 concurrent/3
partly following the example of another answer 来让它工作。
正在加载此文件:
set(0,[]):-!.
set(N,T):- N2 is N-1, set(N2,T1), append(T1,[N],T).
f(A, B):-
(A*A < B),
writeln(A).
par_test(A, B):-
set(A, S),
findall(f(Good, B), (member(Good, S)), Goals),
concurrent(8, Goals, []).
...
?- set(4, S), findall(f(Good, 4), member(Good, S), Goals), concurrent(8, Goals, []).
1
false.
但是观察 htop
(用一个更长的 运行ning 谓词代替这个 f
),它似乎 运行 只在一个核心上 :- (.
如何并行执行此操作?
有没有办法用 concurrent_maplist
或类似简单的并行版本 include
或其他方法来实现我的目标?
maplist
将第一个参数作为谓词,并将作为参数的每个列表中的一个元素应用于此谓词,作为 maplist
的参数。这必然意味着 maplist
的所有列表参数都具有相同数量的元素。在这种情况下 f
接受一个参数,但 maplist(f, _, _)
期望 f
接受 2 个参数。因此,错误 Undefined procedure: f/2 这意味着 Prolog 找不到带有 2 个参数的 f
。
所以 maplist
并没有真正按照您的意愿行事。您想要在某些其他语言中称为 select
的东西,在这些语言中,您只在第二个列表中包含元素,这些元素通过了应用于第一个元素的过滤器。
这是一个示例实现:
select(_, [], []).
select(Filter, [R|Rs], Fs) :-
( call(Filter, R)
-> Fs = [R|SF]
; Fs = SF
),
select(Filter, Rs, SF).
并调用,例如,select(f, L, O)
。
如果像您的示例一样,您有任何带有 属性 的过滤器,它们将成功达到列表中的某个点,然后继续失败,您可能想要像这样优化过滤器,不要让它在第一次失败后继续遍历列表。
select_until_fail(_, [], []).
select_until_fail(Filter, [R|Rs], Fs) :-
( call(Filter, R)
-> Fs = [R|SF],
select_until_fail(Filter, Rs, SF)
; Fs = []
).
或类似的东西(未经测试)。
毕竟,我可能没有真正回答问题的 "concurrency" 部分。
让我们测试一下 concurrent_maplist:
test_concurrent_maplist(C) :-
numlist(1,C,Ns),
concurrent_maplist(prime,Ns,Ps),
sumlist(Ps,N),
format('we have ~d primes in first ~d natural numbers~n', [N,C]).
test_maplist(C) :-
numlist(1,C,Ns),
maplist(prime,Ns,Ps),
sumlist(Ps,N),
format('we have ~d primes in first ~d natural numbers~n', [N,C]).
prime(N,1) :-
prime(N), !.
prime(_,0).
% from https://en.wikipedia.org/wiki/Primality_test
prime(N) :- N =< 1, !, fail.
prime(N) :- N =< 3, !.
prime(N) :- (0 is N mod 2; 0 is N mod 3), !, fail.
prime(N) :- prime_(N,5).
prime_(N,I) :-
I * I =< N, !,
( ( 0 is N mod I; 0 is N mod (I + 2) ) -> fail
; J is I + 1, prime_(N,J)
).
prime_(_,_).
在 4 核机器上,它产生
?- time(test_concurrent_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 2,100,109 inferences, 1.799 CPU in 2.217 seconds (81% CPU, 1167205 Lips)
true.
?- time(test_concurrent_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 21,000,109 inferences, 18.244 CPU in 36.764 seconds (50% CPU, 1151063 Lips)
true.
?- time(test_maplist(100000)).
we have 9592 primes in first 100000 natural numbers
% 16,151,437 inferences, 3.942 CPU in 3.951 seconds (100% CPU, 4096903 Lips)
true.
?- time(test_maplist(1000000)).
we have 78498 primes in first 1000000 natural numbers
% 402,953,287 inferences, 102.334 CPU in 102.302 seconds (100% CPU, 3937617 Lips)
true.
毫无疑问,一个改进:
?- F1 is (2.217/3.951)*100, F2 is (36.764/102.334)*100.
对于更长的列表,我们接近运行时间的 1/3。
而不是 concurrent_maplist/3,我们可以坚持 concurrent_maplist/2 并将结果存储在数据库、全局变量等中...