Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

[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" ;