Skip to content

Commit 6d7d759

Browse files
committed
auto merge of #6752 : osaut/rust/tutorial-tasks, r=graydon
* Add a short section and an example illustrating the use of ARC. * Header for the section of Future changed to be more descriptive: "Backgrounding computations: Futures".
2 parents 2061ce9 + dea6677 commit 6d7d759

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

doc/tutorial-tasks.md

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ let result = ports.foldl(0, |accum, port| *accum + port.recv() );
284284
# fn some_expensive_computation(_i: uint) -> int { 42 }
285285
~~~
286286

287-
## Futures
287+
## Backgrounding computations: Futures
288288
With `extra::future`, rust has a mechanism for requesting a computation and getting the result
289289
later.
290290

@@ -329,6 +329,77 @@ fn main() {
329329
}
330330
~~~
331331

332+
## Sharing immutable data without copy: ARC
333+
334+
To share immutable data between tasks, a first approach would be to only use pipes as we have seen
335+
previously. A copy of the data to share would then be made for each task. In some cases, this would
336+
add up to a significant amount of wasted memory and would require copying the same data more than
337+
necessary.
338+
339+
To tackle this issue, one can use an Atomically Reference Counted wrapper (`ARC`) as implemented in
340+
the `extra` library of Rust. With an ARC, the data will no longer be copied for each task. The ARC
341+
acts as a reference to the shared data and only this reference is shared and cloned.
342+
343+
Here is a small example showing how to use ARCs. We wish to run concurrently several computations on
344+
a single large vector of floats. Each task needs the full vector to perform its duty.
345+
~~~
346+
use extra::arc::ARC;
347+
348+
fn pnorm(nums: &~[float], p: uint) -> float {
349+
(vec::foldl(0.0, *nums, |a,b| a+(*b).pow(p as float) )).pow(1f / (p as float))
350+
}
351+
352+
fn main() {
353+
let numbers=vec::from_fn(1000000, |_| rand::random::<float>());
354+
println(fmt!("Inf-norm = %?", numbers.max()));
355+
356+
let numbers_arc = ARC(numbers);
357+
358+
for uint::range(1,10) |num| {
359+
let (port, chan) = stream();
360+
chan.send(numbers_arc.clone());
361+
362+
do spawn {
363+
let local_arc : ARC<~[float]> = port.recv();
364+
let task_numbers = local_arc.get();
365+
println(fmt!("%u-norm = %?", num, pnorm(task_numbers, num)));
366+
}
367+
}
368+
}
369+
~~~
370+
371+
The function `pnorm` performs a simple computation on the vector (it computes the sum of its items
372+
at the power given as argument and takes the inverse power of this value). The ARC on the vector is
373+
created by the line
374+
~~~
375+
# use extra::arc::ARC;
376+
# let numbers=vec::from_fn(1000000, |_| rand::random::<float>());
377+
let numbers_arc=ARC(numbers);
378+
~~~
379+
and a clone of it is sent to each task
380+
~~~
381+
# use extra::arc::ARC;
382+
# let numbers=vec::from_fn(1000000, |_| rand::random::<float>());
383+
# let numbers_arc = ARC(numbers);
384+
# let (port, chan) = stream();
385+
chan.send(numbers_arc.clone());
386+
~~~
387+
copying only the wrapper and not its contents.
388+
389+
Each task recovers the underlying data by
390+
~~~
391+
# use extra::arc::ARC;
392+
# let numbers=vec::from_fn(1000000, |_| rand::random::<float>());
393+
# let numbers_arc=ARC(numbers);
394+
# let (port, chan) = stream();
395+
# chan.send(numbers_arc.clone());
396+
# let local_arc : ARC<~[float]> = port.recv();
397+
let task_numbers = local_arc.get();
398+
~~~
399+
and can use it as if it were local.
400+
401+
The `arc` module also implements ARCs around mutable data that are not covered here.
402+
332403
# Handling task failure
333404

334405
Rust has a built-in mechanism for raising exceptions. The `fail!()` macro

0 commit comments

Comments
 (0)