From: Jim Weigang <jimw@chilton.com>
Newsgroups: comp.lang.apl
Date: Fri, 05 Jun 1998 17:52:38 -0400
Subject: Beware selective assignment

Here's an example of an aspect of interpreter internals that APL users
should be aware of.  Given the understandable reluctance of vendors to
reveal proprietary implementation details (especially unfavorable ones),
it's up to users to discover and publicize this type of information.

The topic is selective assignment, statements like (3{take}V){<-}4 5 6.
In some APL systems, these statements are apparently implemented by
replacing the target variable V with {iota}{rho}V (for vector V),
applying the selection expression, and then using the resulting indices
to decide which elements of V to replace.  One consequence of this
behavior is slow execution time when replacing elements in a large
array.  APL has to materialize all of {iota}{rho}V even if you're
replacing just a few elements.  In contrast, the execution time for
simple indexed assignment (e.g., V[1 2 3]{<-}4 5 6) depends only on how
much data you're replacing, not on how big V is.  Another consequence is
that the index array takes memory, and materializing it can result in a
WS FULL.  Indexed assignment doesn't usually require extra memory
(unless the target is a synonym of another variable or has the wrong
datatype).

It doesn't matter what selection expression you use, (1{pick}V){<-}4,
(1{rho}V){<-}4, or (1{take}V){<-}4; they all use the same technique.
Both Dyalog APLW (v7) and APL+ seem to use this algorithm for selective
assignment.  I suspect that APL2 does also, but this is based solely on
the observation that TryAPL2 crashes if there isn't enough memory to
materialize the index array.

You can confirm this behavior in your APL by creating a V that nearly
fills the workspace.  Then try executing V[1]{<-}3.  It should work
just fine.  Try (1{take}V){<-}3.  It'll probably give a WS FULL.  You
can also try timing these expressions, preferably using some precise
timing tools such as #MF and the RESTIME program supplied with APL+DOS.
The speed differences can be quite dramatic.  When replacing a single
element in a 1000-by-1000 integer matrix, selective assignment is about
12,000 times slower than indexed assignment.

Selective assignment isn't a useless feature, but you have to be
cautious about using it.  If you're replacing items in a relatively
small array, the cost of materializing {iota}{rho}V will be modest and
there's no reason to avoid selective assignment.  But for large arrays,
you should stick with indexed assignment.

The behavior when setting items in a nested array is somewhat more
complicated.  The APL+ systems that I use apparently don't materialize
the entire per-recursive index array all the way down to the simple
leaves.  Instead, the index arrays seem to be generated as needed on a
level-by-level basis.  For example, if N is 3 4 (1000000{rho}5), doing
(1{take}N){<-}30 takes about the same amount of time as N[1]{<-}30.

When working on the blob finder program (see my c.l.a posting from May
24), I discovered something I wasn't previously aware of.  Indexed
assignment with nested indices (e.g., M[{enclose}5 10]{<-}20, which is
equivalent to M[5;10]{<-}20) apparently uses the same technique as
selective assignment and suffers from the same problems.  (I suspect
that M[N]{<-}B for nested N is implemented as (N{pick}{each}M){<-}B.)
I found this initially by observing that line of [42] of BLOBS, which
does M[T]{<-}U, was consuming far more time than the entire rest of the
program.  I also got unexpected WS FULL errors when trying this
statement with large arrays.  Simply replacing the statement with
M[1{pick}{disclose}T;2{pick}{disclose}T]{<-}U made the program run a LOT
faster.  Not surprising--the original version forced the program to
generate a million-element index array for every 1 in the Boolean
argument!

Indexed reference with nested indices doesn't suffer from the same
problems.  This is to be expected.  Even if Z{<-}M[N] is implemented as
Z{<-}N{pick}{each}M, APL doesn't have to generate the index array; that
happens only when the expression is an assignment target.  So you don't
have to worry about indexing with nested indices to the right of the
assignment arrow, only to the left.

The slow execution speed for M[N]{<-}B seems quite unnecessary.  (And,
indeed, Dyalog APL doesn't have this problem.)  The user has already
provided the indices of the items to replace.  Using the indices as a
selection expression to produce the very same indices is obviously
redundant.  Perhaps this will be sped up in a future release of the APL+
systems.

                                                Jim



JimW's Home Page