Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!oitnews.harvard.edu!purdue!haven.umd.edu!hecate.umd.edu!bloom-beacon.mit.edu!howland.erols.net!feed1.news.erols.com!news.ecn.uoknor.edu!munnari.OZ.AU!cs.mu.OZ.AU!mundook.cs.mu.OZ.AU!fjh
From: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Newsgroups: comp.lang.prolog
Subject: Re: Prolog parser in Prolog?
Date: 25 Nov 1996 08:01:05 GMT
Organization: Comp Sci, University of Melbourne
Lines: 162
Message-ID: <57bjo1$nc7@mulga.cs.mu.OZ.AU>
References: <tjrambthzg.fsf@sfs.nphil.uni-tuebingen.de> <55ru7i$rb2$1@goanna.cs.rmit.edu.au> <tj3eymta1f.fsf@sfs.nphil.uni-tuebingen.de> <573ofb$5f2$1@goanna.cs.rmit.edu.au> <tj7mnb371p.fsf@sfs.nphil.uni-tuebingen.de>
NNTP-Posting-Host: mundook.cs.mu.oz.au

John Griffith <griffith@sfs.nphil.uni-tuebingen.de> writes:

>When I first performed these tests along time ago, I really wanted to
>see how the "hook" method compared.  The reason is that I like the
>expressiveness of meta-predicates, but I don't like the overhead.

I too like the expressiveness of meta-predicates, and dislike overhead --
so I couldn't resist trying these tests with Mercury ;-)

>However, I usually use meta-predicates only as sort of short hand.
>That is I know at compile-time what will be called.  So the hook idea
>seemed like a nice compromise between expressiveness and speed.  The
>results were positive: the hook method is around 20% slower than the
>specialized predicate while the true meta-predicates with call are
>over an order of magnitude slower.

Well, with Mercury I get quite different results.  With full
optimization enabled, all four methods compiled down to _exactly_ the
same code for the inner loop, and so the speed was of course the same
for all of them, modulo timing noise.

>However, I'm now interested in just compiling out the meta-predicate
>altogether and just ending up the specialized predicate.

That's what the Mercury compiler does, whenever it can.
(The latest public release only does this within a module, but the next
release will support cross-module inlining and cross-module specialization.)

>Of course one nice advantage of meta-predicates is the
>smaller code space.  If the overhead of call can be significantly
>reduced then there doesn't seem to be any reason not to use real
>meta-predicates.

Mercury's type/mode/determinism systems allow call/N to be compiled
quite efficiently.  If I disable inlining and specialization of
higher-order predicates, then the version using call/2 is only
about 33% slower than the version using a direct call to is_a/1.

I've appended the changes from the Prolog version.
(This was written for Mercury 0.7; it probably won't work in 0.6,
which is the latest public release, without some minor changes.)

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.

--- ho_benchmark.pl	Mon Nov 25 04:56:06 1996
+++ ho_benchmark.m	Mon Nov 25 06:55:41 1996
@@ -1,3 +1,30 @@
+%-----------------------------------------------------------------------------%
+
+:- module ho_benchmark.
+:- interface.
+:- import_module io.
+:- pred main(io__state::di, io__state::uo) is det.
+
+:- implementation.
+:- import_module int, list, bool, char, time.
+
+main --> test1, test2.
+
+time(Pred, Count, _InternalCount) -->
+	{ benchmark_det( ( pred(_I::in, O::out) is det :-
+				(call(Pred) -> O = yes ; O = no) ),
+			42, _Out, Count, Time) },
+	write("Count = "), io__write_int(Count), nl,
+	write("Time = "), io__write_int(Time), nl,
+	io__report_stats, nl.
+
+write(S) --> io__write_string(S).
+nl --> write("\n").
+
+:- type hook ---> hook_is_a.
+
+%-----------------------------------------------------------------------------%
+
 %% ===================================================================
 
 %% Description: Speed tests for meta predicates.
@@ -5,6 +32,7 @@
 %% RCS: $Id: meta_tests.pl,v 1.1 1996/11/24 11:48:47 griffith Exp $
 
 %%% Simple test predicate --------------------------------------------
+:- mode is_a(in) is semidet.
 is_a(a).
 
 %%% Non-meta predicate -----------------------------------------------
@@ -22,10 +50,7 @@
 %%% Meta predicate with call -----------------------------------------
 map_list_1([],_G).
 map_list_1([F|R],G) :-
-  %% Simulate call/2.
-  functor(Term,G,1),
-  arg(1,Term,F),
-  call(Term),
+  call(G, F),
   map_list_1(R,G).
 
 %%% Meta predicate with hook -----------------------------------------
@@ -34,38 +59,47 @@
   map_list_hook(G,F),
   map_list_2(R,G).
 
-map_list_hook(is_a,X) :- is_a(X).
+map_list_hook(hook_is_a,X) :- is_a(X).
 
 %%% Generate a list of atoms -----------------------------------------
-gen_a_list(0,[]) :- !.
-gen_a_list(N,[a|R]) :-
-  N1 is N - 1,
-  gen_a_list(N1,R).
+gen_a_list(N,L) :-
+	( N = 0 ->
+		L = []
+	;
+		L = [a|R],
+		N1 is N - 1,
+		gen_a_list(N1,R)
+	).
 
 %%% Test suite -------------------------------------------------------
 
 %% For an alternative, see the definition of cpu_time/3 in "The Craft
 %% of Prolog", by Richard A. O'Keefe.
-:- use_module(library(benchmark),[time/3]).
+% :- use_module(library(benchmark),[time/3]).
 
+:- mode test_is_a_list_1(in) is semidet.
+:- mode test_is_a_list_2(in) is semidet.
+:- mode test_map_list_1(in) is semidet.
+:- mode test_map_list_2(in) is semidet.
 test_is_a_list_1(N) :- gen_a_list(N,L), is_a_list_1(L).
 test_is_a_list_2(N) :- gen_a_list(N,L), is_a_list_2(L).
 test_map_list_1(N)  :- gen_a_list(N,L), map_list_1(L,is_a).
-test_map_list_2(N)  :- gen_a_list(N,L), map_list_2(L,is_a).
+test_map_list_2(N)  :- gen_a_list(N,L), map_list_2(L,hook_is_a).
 
-test_all(ListSize,Count,InternalCount) :-
-  write('Non-meta predicate:'),
+test_all(ListSize,Count,InternalCount) -->
+  write("Non-meta predicate:"),
   time(test_is_a_list_1(ListSize),Count,InternalCount),nl,
   %%
-  write('Non-meta predicate with call:'),
+  write("Non-meta predicate with call:"),
   time(test_is_a_list_2(ListSize),Count,InternalCount),nl,
   %%
-  write('Meta-predicate with call:'),
+  write("Meta-predicate with call:"),
   time(test_map_list_1(ListSize),Count,InternalCount),nl,
   %%
-  write('Meta-predicate with hook:'),
+  write("Meta-predicate with hook:"),
   time(test_map_list_2(ListSize),Count,InternalCount).
 
-test1 :- test_all(1000,10,10).
-test2 :- test_all(10000,10,10).
+test1 --> test_all(1000,100,10).
+test2 --> test_all(10000,100,10).
 
+%-----------------------------------------------------------------------------%
