diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md index 320385c89a6..8c574e539fe 100644 --- a/doc/tutorial-tasks.md +++ b/doc/tutorial-tasks.md @@ -284,7 +284,7 @@ let result = ports.foldl(0, |accum, port| *accum + port.recv() ); # fn some_expensive_computation(_i: uint) -> int { 42 } ~~~ -## Futures +## Backgrounding computations: Futures With `extra::future`, rust has a mechanism for requesting a computation and getting the result later. @@ -329,6 +329,77 @@ fn main() { } ~~~ +## Sharing immutable data without copy: ARC + +To share immutable data between tasks, a first approach would be to only use pipes as we have seen +previously. A copy of the data to share would then be made for each task. In some cases, this would +add up to a significant amount of wasted memory and would require copying the same data more than +necessary. + +To tackle this issue, one can use an Atomically Reference Counted wrapper (`ARC`) as implemented in +the `extra` library of Rust. With an ARC, the data will no longer be copied for each task. The ARC +acts as a reference to the shared data and only this reference is shared and cloned. + +Here is a small example showing how to use ARCs. We wish to run concurrently several computations on +a single large vector of floats. Each task needs the full vector to perform its duty. +~~~ +use extra::arc::ARC; + +fn pnorm(nums: &~[float], p: uint) -> float { + (vec::foldl(0.0, *nums, |a,b| a+(*b).pow(p as float) )).pow(1f / (p as float)) +} + +fn main() { + let numbers=vec::from_fn(1000000, |_| rand::random::()); + println(fmt!("Inf-norm = %?", numbers.max())); + + let numbers_arc = ARC(numbers); + + for uint::range(1,10) |num| { + let (port, chan) = stream(); + chan.send(numbers_arc.clone()); + + do spawn { + let local_arc : ARC<~[float]> = port.recv(); + let task_numbers = local_arc.get(); + println(fmt!("%u-norm = %?", num, pnorm(task_numbers, num))); + } + } +} +~~~ + +The function `pnorm` performs a simple computation on the vector (it computes the sum of its items +at the power given as argument and takes the inverse power of this value). The ARC on the vector is +created by the line +~~~ +# use extra::arc::ARC; +# let numbers=vec::from_fn(1000000, |_| rand::random::()); +let numbers_arc=ARC(numbers); +~~~ +and a clone of it is sent to each task +~~~ +# use extra::arc::ARC; +# let numbers=vec::from_fn(1000000, |_| rand::random::()); +# let numbers_arc = ARC(numbers); +# let (port, chan) = stream(); +chan.send(numbers_arc.clone()); +~~~ +copying only the wrapper and not its contents. + +Each task recovers the underlying data by +~~~ +# use extra::arc::ARC; +# let numbers=vec::from_fn(1000000, |_| rand::random::()); +# let numbers_arc=ARC(numbers); +# let (port, chan) = stream(); +# chan.send(numbers_arc.clone()); +# let local_arc : ARC<~[float]> = port.recv(); +let task_numbers = local_arc.get(); +~~~ +and can use it as if it were local. + +The `arc` module also implements ARCs around mutable data that are not covered here. + # Handling task failure Rust has a built-in mechanism for raising exceptions. The `fail!()` macro