同时应用谓词来过滤列表(SWI Prolog)

问题描述 投票:1回答:2

My problem: apply a predicate to filter a list in parallel

我有一个列表,我有一个谓词。在实践中,它是一个很长的列表,谓词需要一段时间。我想只输出满足谓词的列表元素。

我可以访问大量的CPU内核,所以我认为尝试并行测试列表中的元素是有意义的。

Stuff I've tried:

[concurrent_] maplist不起作用

好像它应该很容易。 concurrent_maplist似乎是maplist的简单并发版本。

根据another answer on this sitemaplist/3应该完全按照我的意愿行事。 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 [编辑]运行!但它是平行的吗?

我一直试图使用concurrent/3使用部分遵循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(用更长时间运行的谓词代替这个f),它似乎只运行一个核心:-(。

我该如何并行执行此操作?

有没有办法用concurrent_maplist,或类似简单的include并行版本,或其他方式实现我想要的目标?

concurrency prolog swi-prolog
2个回答
1
投票

让我们测试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并将结果存储在数据库,全局变量等中......


0
投票

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 = []
    ).

或类似的东西(未经测试)。

毕竟,我可能没有真正回答问题的“并发”部分。

© www.soinside.com 2019 - 2024. All rights reserved.