Arc Forumnew | comments | leaders | submitlogin
1 point by highCs 3282 days ago | link | parent

I'm under the impression that when I create a future it doesn't run until I call touch - this behavior denies parallelism if I'm correct. Is it supposed to do that, no I guess? Any example that works?

  (= f ($.future (fn () (for i 0 (< i 100) (++ i) (prn i)))))
  ($.touch f)
  0
  1
  2
  3
  4
  5
  6
  ...


3 points by rocketnia 3278 days ago | link

Going by http://docs.racket-lang.org/guide/parallelism.html#%28part._..., it looks like any operation in a future that might be expensive is a "blocking operation," which can only be resumed by touching the future. Even multiplying a floating-point number by fixed-point integer is expensive enough to be blocking!

Without testing it myself, I'd guess there are a few things that might be blocking in your example:

* Converting a number to a string.

* Looking up the current value of stdout. This depends on the current parameterization, which is probably carried on the continuation in the form of continuation marks. According to http://docs.racket-lang.org/reference/futures.html, "work in a future is suspended if it depends in some way on the current continuation, such as raising an exception."

* Actually writing to the output stream.

Maybe "visualize-futures" would show you what's going on in particular.

-----

3 points by rocketnia 3275 days ago | link

I finally sat down to test it, and it looks like all three of those are blocking operations, just as I thought.

  arc> (= g 1)
  1
  arc> ($.future:fn () (= g 2))
  #<future>
  arc> g
  2
As a baseline, that future seems to work. It simply assigns a variable, which the documentation explicitly says is a supported operation, so there wasn't a lot that could go wrong.

  arc> (= f ($.future:fn () (= g $.number->string.3)))
  #<future>
  arc> g
  2
  arc> $.touch.f
  "3"
  arc> g
  "3"
That future had to call Racket's number->string, and it blocked until it was touched. The same thing happens with Arc's (= g string.3).

  arc> (= f ($.future:fn () (= g ($.current-output-port))))
  #<future>
  arc> g
  "3"
  arc> $.touch.f
  #<output-port:stdout>
  arc> g
  #<output-port:stdout>
That future blocked due to calling Racket's current-output-port. The same thing happens with Arc's (= g (stdout)).

  arc> (= sout (stdout))
  #<output-port:stdout>
  arc> (= f ($.future:fn () ($.display #\! sout) (= g 5)))
  #<future>
  arc> g
  #<output-port:stdout>
  arc> $.touch.f
  !5
  arc> g
  5
That future blocked on calling Racket's display operation. It finally output the #\! character when it was touched. The same thing happens with Arc's (disp #\! out), and the same thing happens if I pass in a string instead of a single character.

I tried using visualize-futures from Arc, but I ran across some errors. Here's the first one:

  arc> ($:require future-visualizer)
  #<void>
  arc>
    (def visualize-futures-fn (body)
      (($:lambda (body) (visualize-futures (body))) body))
  #<procedure: visualize-futures-fn>
  arc>
    (mac visualize-futures body
      `(visualize-futures-fn:fn () ,@body))
  #(tagged mac #<procedure: visualize-futures>)
  arc> (def wrn (x) write.x (prn))
  #<procedure: wrn>
  arc>
    (visualize-futures:withs
        (g 5
         f ($.future:fn () (= g $.number->string.6)))
      wrn.g
      (wrn $.number->string.6)
      wrn.g
      (wrn $.touch.f)
      wrn.g)
  5
  "6"
  5
  "6"
  "6"
  inexact->exact: no exact representation
    number: +nan.0
    context...:
     C:\Program Files\Racket\share\pkgs\future-visualizer\future-visualizer\private\visualizer-drawing.rkt:344:4: for-loop
     C:\Program Files\Racket\share\pkgs\future-visualizer\future-visualizer\private\visualizer-drawing.rkt:387:0: calc-segments
     C:\Program Files\Racket\share\pkgs\future-visualizer\future-visualizer\private\visualizer-gui.rkt:106:0: show-visualizer3
     C:\mine\prog\repo\anarki\ac.scm:1234:4
It seems to be dividing by zero there. I tried it in Racket, but I got the same error. This error can be fixed by tacking on (sleep 0.1) so that the total duration isn't close to zero:

    (visualize-futures:withs
        (g 5
         f ($.future:fn () (= g $.number->string.6)))
      (sleep 0.1)
      wrn.g
      (wrn $.number->string.6)
      wrn.g
      (wrn $.touch.f)
      wrn.g)
However, even that code gives me trouble in Anarki; the window that Racket pops up is unresponsive for some reason. So here's the same test in Racket, where the window actually works:

  Welcome to Racket v6.1.1.
  > (require future-visualizer)
  > (define (wrn x) (write x) (display "\n"))
  >
    (visualize-futures
      (let* ([g 5]
             [f (future (lambda () (set! g (number->string 6))))])
        (sleep 0.1)
        (wrn g)
        (wrn (number->string 6))
        (wrn g)
        (wrn (touch f))
        (wrn g)))
  5
  "6"
  5
  #<void>
  "6"
  >
In the pop-up, the panel at the left shows a summary of expensive operations:

  Blocks (1)
    number->string (1)
  Syncs (0)
  GC's (0 total, 0.0 ms)
If I look in the timeline and select the two red dots, this information comes up:

  Event: block
  Time: +0.0 ms
  Future ID: 1
  Process ID: 1
  Primitive: number->string

  Event: block
  Time: +109.744140625 ms
  Future ID: 1
  Process ID: 0
  Primitive: number->string
It looks like the first one is the number->string call inside the future, and the second one is the call that occurs outside the future. I guess it's still considered a blocking operation even if it happens in the main process, but fortunately it doesn't stop the whole program. :)

So number->string is a primitive that's considered complicated enough to put the future in a blocked state. To speculate, maybe the Racket project doesn't want to incur the performance cost of having the future's process load the code for every single Racket primitive, or maybe they just haven't implemented this one operation yet.

Going by this, futures can be useful, but they have a pretty limited set of operations. Still, mutation is pretty powerful: If needed, maybe it's possible to set up an execution harness where the future assigns the operation it wants to perform to a variable, and then some monitoring thread takes care of it, assigning the result back to a variable that the future can read.

Meanwhile, I wonder why the pop-up doesn't seem to work from Anarki. I seem to remember other Racket GUI operations haven't worked for me either. If the GUI works for anyone else, it might be that I'm on Windows.

-----

3 points by highCs 3275 days ago | link

Oh I get it I think. Futures are for computing arithmetic in parallel.

-----