Newsgroups: comp.lang.apl
Path: watmath!watserv2.uwaterloo.ca!torn!utcsri!rpi!zaphod.mps.ohio-state.edu!uunet.ca!geac!itcyyz!yrloc!hui
From: hui@yrloc.ipsa.reuter.COM (Roger Hui)
Subject: Re: explicit <-> tacit, space/time usage on prime numbers
Message-ID: <1992Nov7.233813.15681@yrloc.ipsa.reuter.COM>
Organization: Iverson Software Inc.
References: <1cr4ajINNngp@nestroy.wu-wien.ac.at>
Date: Sat, 7 Nov 92 23:38:13 GMT
Lines: 144

Johann Mitloehner writes:
 
> Here are some experiments (by T. Kolarik and myself) with the
> well-known primes-idiom on several machines, comparing tacit and
> explicit definition of the verb:
>
> On an HP 9000/720 (HP-UX):
> J 4.1   Copyright (c) 1990-1992, Iverson Software Inc.  All Rights Reserved.
>
>    0!:2 <'ta-ex.js'
>    eprimes =. '(i.y.)#~2=+/0=|/~i.y.':'' NB. explicit def
>    tprimes =.  (=&2 @  (+/ @ (=&0 @ |/~ @ i.))) # i. NB. tacit def
>    time =. 6!:2
>    space =. 7!:2
>    ts =. (time, space)
>    ts 'tprimes 100'
> 2.71 2528
>    ts 'eprimes 100'
> 0.15 464
>    ...
>
> Result: explicit definition is much faster.
>
> But tacitly defined verbs are supposed to be (much?) faster, aren't they?
> Or is this just a bad example? (Or is our tacit version so bad :-(
 
In July 1990, at the APL90 Conference in Copenhagen, Eugene McDonnell
presented timings indicating that a tacitly defined nub was faster
than an explicitly defined nub, and was as fast as the primitive ~. .
I was in the audience and was surprised by this result.  Tacit defns 
arise from the use of adverbs and conjunctions, function assignment, 
and forks; that they happen to be faster is an unexpected benefit.
 
The slower times for "tprimes" obtained above can be accounted for by
the difference between =&0@|/ vs. =&0@(|/).  I applied the tacit
translator to the explicit defn, and obtained the following time/space
results (J6 on a PC486/33):
 
   ts 'tprimes 100'
2.86 24692
   ts 'eprimes 100'
0.05 53420
   f0 =. '(i.y.)#~2=+/0=|/~i.y.': 11
   ts 'f0 100'
0.05 50312
   lr =. 5!:5
   lr <'f0'
i.@] #~ 2&=@(+/@(0&=@(|/~@(i.@]))))
 
This shows that tacit defn is at least no slower than explicit defn.
The following table shows that it is in fact faster.  All times are
obtained using 100 time 'f n' on J6 PC486/33.
 
	     200       100       10        5        1
tprimes    11.0334    2.8456   0.0341   0.0109   0.0033
eprimes     0.2230    0.0582   0.0049   0.0044   0.0044
f0          0.2213    0.0565   0.0032   0.0027   0.0027
f1          0.2208    0.0566   0.0033   0.0027   0.0026
f2          0.2208    0.0566   0.0033   0.0027   0.0027
f3          0.0193    0.0138   0.0038   0.0033   0.0038
f4          0.0143    0.0088   0.0017   0.0016   0.0016
 
"f0" is faster than "eprime" by a fixed amount, 0.0017.  This is
consistent with the explanation of why tacit is faster than explicit:
a tacit defn is not reparsed on execution, and 0.0017 is the time it takes
to parse "eprimes".  The relative advantage of tacit over explicit is thus
greatest for small vectors, when parsing time dominates, and diminishes as
vector size increases.  This fact is of interest to compiler writers.
 
"f1" is an attempt to see how much better or worse a "hand-written" tacit
defn may be.  f0=.i.@] #~ 2&=@(+/@(0&=@(|/~@(i.@])))) , the defn produced
by the translator, is periphrastic.  It is easy to see that the two uses
of @] are unnecessary, and the use of passive can be eliminated by
rearranging the tines of the fork.  As well, I tend to shy away from
deeply right-nested compositions.  Putting it all together:
 
   f1 =. 2&=@(+/)@(0&=)@(|/~)@i. # i.
 
The timings in the table indicate that "f1" is slightly faster than "f0".
Not too surprising, as "f1" is only slightly different from "f0".
 
As indicated at the start, speed is not the main point of tacit defn.
Some of the advantages of tacit defn were outlined in a msg two weeks ago:
 
  0) The tacit form encourages the use of components, primitive and
  user-defined.  Use of components is one of the few effective ways of
  dealing with complexity.
  1) The tacit form focuses on how components are combined, i.e. it focuses
  on the interface between components, where errors tend to occur.
  2) Tacit expressions are algebraic, i.e. they are amenable to proof
  and other formal manipulations.
  3) Tacit expressions are constructed using operators (adverbs and
  conjunctions), ideas debugged in mathematics over hundreds of years.
 
Even for something as small as this primes problem, it is possible to
see how these benefits may be applicable.  "f1" can be redefined to
use a sub-function, of interest in its own right:
   sieve =. 2&=@(+/)@(0&=)@(|/~)@i.
   f2    =. sieve # i.
 
"f2" is slightly clearer than the others, in bringing into sharper relief the
structure of the computation.  It is about as fast as the others (see table).
 
I'd previously written a recursive version of "primes" which avoids
generating the n^2 table:
   t=.i. 0 0
   t=.t,'$.=.1+30<y.'
   t=.t,'y.(>: # ])2 3 5 7 11 13 17 19 23 29'
   t=.t,'p,(*./0~:p|/s)#s=.k+i.>:y.-k=.{:p=.f3 <.%:y.'
   f3 =. t :''
 
A hand-translated tacit version of same:
   basis   =. (>: # ])&2 3 5 7 11 13 17 19 23 29
   sieve   =. *./@(0&~:)@(|/)
   suffix  =. {:@[ ([ + i.@-.@-) ]
   extend  =. [ , [ (sieve # ]) suffix
   f4      =. ($:@<.@%: extend ])`basis @. (30&>)
 
The time and space complexity of "f3" and "f4" are of a lesser order
than the others, so that the advantage shown in the table increases with
argument size:
 
   100 time 'f0 500'           100 time 'f0 1000'
1.3583                      ws full
   100 time 'f3 500'           100 time 'f3 1000'
0.038                       0.09
   100 time 'f4 500'           100 time 'f4 1000'
0.0335                      0.0829
 
Finally, 2) speaks of tacit expressions as being more amenable
to proof and other formal manipulations.  Optimization is a class of
formal manipulation, and in the future the implementation may exploit
formal properties of tacit defns to effect faster execution.  I described
some possible approaches at the Minnowbrook '92 conference in mid September.
 
> (Note: we used J 4.1 in the first example because we have not yet
> succeeded in compiling J5.1a for the HP 9000/720 and 730.)
 
J6 is now available.  When compiling for the HP9000/7x0, try setting
SYS_DOUBLE in js.h to 1.

------------------------------------
Roger Hui, Iverson Software Inc., 33 Major Street, Toronto, Ontario  M5S 2K9
Phone: (416) 925 6096;  Fax: (416) 488 7559
