Mailing List Archive: 49091 messages

## [REBOL] concurrent/parallel/multiple SETting

### From: joel::neely::fedex::com at: 5-Jan-2002 9:01

```
Another bit of language trivia:

Many languages and notations (e.g. Perl, LISP, Scheme, Dijkstra's
guarded command notation, Tony Hoare's CSP, etc.) have the notion
of concurrent/parallel/multiple assignment, in which several
expressions are evaluated and the results then associated with
variables.

This has at least two obvious benefits:

1)  At the purely practical level, this notion can be used to
express computation without the use of extra temporary
variables.  The simplest example I can think of is the exchange
of two values:

(a, b) := (b, a)

which in general requires another variable if done serially:

c:= a ; a := b ; b := c

This generalizes to the "rotation" of a fixed set of values,
such as:

(a, b, c, d, e) := (b, c, d, e, a)

which performs a circular exchange among five values, again
requiring one temporary variable if done serially.

Other examples at this level are easy to construct.

2)  At the conceptual level, it makes it easier to think of
several evaluations as being performed "simultaneously"
without having to complicate one's thinking with issues of which
might need to be performed before/after which other(s).  With

(x, i) := (a[i], i+1)

it's clear what's happening, without worrying about whether

x := a[i] ; i := i + 1

or

i := i + 1 ; x := a[i]

is more appropriate (or correct!)

REBOL also supports this concept, allowing SET to be applied
to blocks of words and values, but there's no free lunch.

Using the above example of rotating five values, I build the
following REBOL function to time repeated rotations done with
serial set-words (requiring an extra word) and done with
the block form of SET.

setter: func
[   n [integer!] m [integer!]
/local a b c d e f t0 t1 t2
][  t1: 0   a: 1  b: c: d: e: f: 0
loop m
[   t0: now/time/precise
loop n
[   f: e   e: d   d: c   c: b   b: a   a: f]
t1: t1 + now/time/precise - t0
]
t2: 0   a: 1  b: c: d: e: f: 0
loop m
[   t0: now/time/precise
loop n
[   set [a b c d e] reduce [b c d e a]]
t2: t2 + now/time/precise - t0
]
print [t1 t2]
]

I was *quite* surprised by the results:

>> setter 1000 1000  =>  0:00:08.69 0:00:41.29 4.75143843498274
>> setter 1000 2000  =>  0:00:17.5  0:01:22.91 4.73771428571429
>> setter 2000 1000  =>  0:00:16.93 0:01:21.95 4.84051978735972
>> setter 2000 2000  =>  0:00:33.03 0:02:46.65 5.04541326067212

(This was performed on a 'doze box, so subsecond details are
dubious at best...)

Notice that the block SET takes about 5 times as long as the
serial version, with the cost rising slowly as the number of
iterations increases.  This suggests to me that the overhead
is related to the memory management involved in constructing
(large numbers of) temporary blocks.

I be interested in whether there's any other way to get the
same result without so much overhead (or whether I've overlooked
anything in the above test).

-jn-

--
; sub REBOL {}; sub head (\$) {@_[0]}
REBOL []
# despam: func [e] [replace replace/all e ":" "." "#" "@"]
; sub despam {my (\$e) = @_; \$e =~ tr/:#/.@/; return "\n\$e"}
print head reverse despam "moc:xedef#yleen:leoj" ;
```