Change:
Just a few parallel algorithms. This is unused and probably not efficient enough for real use. If we're going to do this it will need to be rethought from scratch.
Is it possible to add an API that would spawn a task assigned to an exclusive OS thread?
There is already a way to do that by creating a task with the scheduler mode. The mechanism may change with the new scheduler, but it's an important use case so there will continue to be a way to dedicate an entire thread to a single task.
assign=me
did this land? if not do you want to rebase your patch?

Discussion:
pub mod term; pub mod time; pub mod arena; <del> pub mod par; </del> pub mod base64; pub mod rl; pub mod workcache;

<del> // Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::cast; use std::num; use std::ptr; use std::sys; use std::vec; use future::Future; /** * The maximum number of tasks this module will spawn for a single * operation. */ static MAX_TASKS : uint = 32u; /// The minimum number of elements each task will process. static MIN_GRANULARITY : uint = 1024u; /** * An internal helper to map a function over a large vector and * return the intermediate results. * * This is used to build most of the other parallel vector functions, * like map or alli. */ fn map_slices<A:Clone + Send,B:Clone + Send>( xs: &[A], f: &fn() -> ~fn(uint, v: &[A]) -> B) -> ~[B] { let len = xs.len(); if len < MIN_GRANULARITY { info!("small slice"); // This is a small vector, fall back on the normal map. ~[f()(0u, xs)] } else { let num_tasks = num::min(MAX_TASKS, len / MIN_GRANULARITY); let items_per_task = len / num_tasks; let mut futures = ~[]; let mut base = 0u; info!("spawning tasks"); while base < len { let end = num::min(len, base + items_per_task); do xs.as_imm_buf |p, _len| { let f = f(); let base = base; let f = do Future::spawn() || { unsafe { let len = end - base; let slice = (ptr::offset(p, base as int), len * sys::size_of::<A>()); info!("pre-slice: %?", (base, slice)); let slice : &[A] = cast::transmute(slice); info!("slice: %?", (base, slice.len(), end - base)); assert_eq!(slice.len(), end - base); f(base, slice) } }; futures.push(f); }; base += items_per_task; } info!("tasks spawned"); info!("num_tasks: %?", (num_tasks, futures.len())); assert_eq!(num_tasks, futures.len()); do futures.move_iter().map |ys| { let mut ys = ys; ys.get() }.collect() } } /// A parallel version of map. pub fn map<A:Clone + Send,B:Clone + Send>( xs: &[A], fn_factory: &fn() -> ~fn(&A) -> B) -> ~[B] { vec::concat(map_slices(xs, || { let f = fn_factory(); let result: ~fn(uint, &[A]) -> ~[B] = |_, slice| slice.iter().map(|x| f(x)).collect(); result })) } /// A parallel version of mapi. pub fn mapi<A:Clone + Send,B:Clone + Send>( xs: &[A], fn_factory: &fn() -> ~fn(uint, &A) -> B) -> ~[B] { let slices = map_slices(xs, || { let f = fn_factory(); let result: ~fn(uint, &[A]) -> ~[B] = |base, slice| { slice.iter().enumerate().map(|(i, x)| { f(i + base, x) }).collect() }; result }); let r = vec::concat(slices); info!("%?", (r.len(), xs.len())); assert_eq!(r.len(), xs.len()); r } /// Returns true if the function holds for all elements in the vector. pub fn alli<A:Clone + Send>( xs: &[A], fn_factory: &fn() -> ~fn(uint, &A) -> bool) -> bool { let mapped = map_slices(xs, || { let f = fn_factory(); let result: ~fn(uint, &[A]) -> bool = |base, slice| { slice.iter().enumerate().all(|(i, x)| f(i + base, x)) }; result }); mapped.iter().all(|&x| x) } /// Returns true if the function holds for any elements in the vector. pub fn any<A:Clone + Send>( xs: &[A], fn_factory: &fn() -> ~fn(&A) -> bool) -> bool { let mapped = map_slices(xs, || { let f = fn_factory(); let result: ~fn(uint, &[A]) -> bool = |_, slice| slice.iter().any(f); result }); mapped.iter().any(|&x| x) } </del>

<del> // xfail-pretty // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! An implementation of the Graph500 Breadth First Search problem in Rust. */ extern mod extra; use extra::arc; use extra::time; use extra::ringbuf::RingBuf; use extra::container::Deque; use extra::par; use std::hashmap::HashSet; use std::num::abs; use std::io; use std::os; use std::rand::RngUtil; use std::rand; use std::uint; use std::vec; type node_id = i64; type graph = ~[~[node_id]]; type bfs_result = ~[node_id]; fn make_edges(scale: uint, edgefactor: uint) -> ~[(node_id, node_id)] { let mut r = rand::XorShiftRng::new(); fn choose_edge<R: rand::Rng>(i: node_id, j: node_id, scale: uint, r: &mut R) -> (node_id, node_id) { let A = 0.57; let B = 0.19; let C = 0.19; if scale == 0u { (i, j) } else { let i = i * 2i64; let j = j * 2i64; let scale = scale - 1u; let x = r.gen::<float>(); if x < A { choose_edge(i, j, scale, r) } else { let x = x - A; if x < B { choose_edge(i + 1i64, j, scale, r) } else { let x = x - B; if x < C { choose_edge(i, j + 1i64, scale, r) } else { choose_edge(i + 1i64, j + 1i64, scale, r) } } } } } do vec::from_fn((1u << scale) * edgefactor) |_i| { choose_edge(0i64, 0i64, scale, &mut r) } } fn make_graph(N: uint, edges: ~[(node_id, node_id)]) -> graph { let mut graph = do vec::from_fn(N) |_i| { HashSet::new() }; for e in edges.iter() { match *e { (i, j) => { graph[i].insert(j); graph[j].insert(i); } } } do graph.move_iter().map |v| { let mut vec = ~[]; for i in v.move_iter() { vec.push(i); } vec }.collect() } fn gen_search_keys(graph: &[~[node_id]], n: uint) -> ~[node_id] { let mut keys = HashSet::new(); let mut r = rand::rng(); while keys.len() < n { let k = r.gen_uint_range(0u, graph.len()); if graph[k].len() > 0u && graph[k].iter().any(|i| { *i != k as node_id }) { keys.insert(k as node_id); } } let mut vec = ~[]; for i in keys.move_iter() { vec.push(i); } return vec; } /** * Returns a vector of all the parents in the BFS tree rooted at key. * * Nodes that are unreachable have a parent of -1. */ fn bfs(graph: graph, key: node_id) -> bfs_result { let mut marks : ~[node_id] = vec::from_elem(graph.len(), -1i64); let mut q = RingBuf::new(); q.push_back(key); marks[key] = key; while !q.is_empty() { let t = q.pop_front().unwrap(); do graph[t].iter().advance |k| { if marks[*k] == -1i64 { marks[*k] = t; q.push_back(*k); } true }; } marks } #[deriving(Clone)] enum color { white, // node_id marks which node turned this gray/black. // the node id later becomes the parent. gray(node_id), black(node_id) } /** * Another version of the bfs function. * * This one uses the same algorithm as the parallel one, just without * using the parallel vector operators. */ fn bfs2(graph: graph, key: node_id) -> bfs_result { // This works by doing functional updates of a color vector. let mut colors = do vec::from_fn(graph.len()) |i| { if i as node_id == key { gray(key) } else { white } }; fn is_gray(c: &color) -> bool { match *c { gray(_) => { true } _ => { false } } } let mut i = 0; while colors.iter().any(is_gray) { // Do the BFS. info!("PBFS iteration %?", i); i += 1; colors = do colors.iter().enumerate().map |(i, c)| { let c : color = *c; match c { white => { let i = i as node_id; let neighbors = &graph[i]; let mut color = white; do neighbors.iter().advance |k| { if is_gray(&colors[*k]) { color = gray(*k); false } else { true } }; color } gray(parent) => { black(parent) } black(parent) => { black(parent) } } }.collect() } // Convert the results. do colors.iter().map |c| { match *c { white => { -1i64 } black(parent) => { parent } _ => { fail!("Found remaining gray nodes in BFS") } } }.collect() } /// A parallel version of the bfs function. fn pbfs(graph: &arc::Arc<graph>, key: node_id) -> bfs_result { // This works by doing functional updates of a color vector. let graph_vec = graph.get(); // FIXME #3387 requires this temp let mut colors = do vec::from_fn(graph_vec.len()) |i| { if i as node_id == key { gray(key) } else { white } }; #[inline(always)] fn is_gray(c: &color) -> bool { match *c { gray(_) => { true } _ => { false } } } fn is_gray_factory() -> ~fn(c: &color) -> bool { let r: ~fn(c: &color) -> bool = is_gray; r } let mut i = 0; while par::any(colors, is_gray_factory) { // Do the BFS. info!("PBFS iteration %?", i); i += 1; let old_len = colors.len(); let color = arc::Arc::new(colors); let color_vec = color.get(); // FIXME #3387 requires this temp colors = do par::mapi(*color_vec) { let colors = color.clone(); let graph = graph.clone(); let result: ~fn(x: uint, y: &color) -> color = |i, c| { let colors = colors.get(); let graph = graph.get(); match *c { white => { let i = i as node_id; let neighbors = graph[i].clone(); let mut color = white; do neighbors.iter().advance |k| { if is_gray(&colors[*k]) { color = gray(*k); false } else { true } }; color } gray(parent) => { black(parent) } black(parent) => { black(parent) } } }; result }; assert_eq!(colors.len(), old_len); } // Convert the results. do par::map(colors) { let result: ~fn(c: &color) -> i64 = |c| { match *c { white => { -1i64 } black(parent) => { parent } _ => { fail!("Found remaining gray nodes in BFS") } } }; result } } /// Performs at least some of the validation in the Graph500 spec. fn validate(edges: ~[(node_id, node_id)], root: node_id, tree: bfs_result) -> bool { // There are 5 things to test. Below is code for each of them. // 1. The BFS tree is a tree and does not contain cycles. // // We do this by iterating over the tree, and tracing each of the // parent chains back to the root. While we do this, we also // compute the levels for each node. info!(~"Verifying tree structure..."); let mut status = true; let level = do tree.map() |parent| { let mut parent = *parent; let mut path = ~[]; if parent == -1i64 { // This node was not in the tree. -1 } else { while parent != root { if path.contains(&parent) { status = false; } path.push(parent); parent = tree[parent]; } // The length of the path back to the root is the current // level. path.len() as int } }; if !status { return status } // 2. Each tree edge connects vertices whose BFS levels differ by //    exactly one. info!(~"Verifying tree edges..."); let status = do tree.iter().enumerate().all |(k, parent)| { if *parent != root && *parent != -1i64 { level[*parent] == level[k] - 1 } else { true } }; if !status { return status } // 3. Every edge in the input list has vertices with levels that //    differ by at most one or that both are not in the BFS tree. info!(~"Verifying graph edges..."); let status = do edges.iter().all |e| { let (u, v) = *e; abs(level[u] - level[v]) <= 1 }; if !status { return status } // 4. The BFS tree spans an entire connected component's vertices. // This is harder. We'll skip it for now... // 5. A node and its parent are joined by an edge of the original //    graph. info!(~"Verifying tree and graph edges..."); let status = do par::alli(tree) { let edges = edges.clone(); let result: ~fn(x: uint, v: &i64) -> bool = |u, v| { let u = u as node_id; if *v == -1i64 || u == root { true } else { edges.iter().any(|x| x == &(u, *v)) || edges.iter().any(|x| x == &(*v, u)) } }; result }; if !status { return status } // If we get through here, all the tests passed! true } fn main() { let args = os::args(); let args = if os::getenv("RUST_BENCH").is_some() { ~[~"", ~"15", ~"48"] } else if args.len() <= 1 { ~[~"", ~"10", ~"16"] } else { args }; let scale = from_str::<uint>(args[1]).unwrap(); let num_keys = from_str::<uint>(args[2]).unwrap(); let do_validate = false; let do_sequential = true; let start = time::precise_time_s(); let edges = make_edges(scale, 16); let stop = time::precise_time_s(); io::stdout().write_line(fmt!("Generated %? edges in %? seconds.", edges.len(), stop - start)); let start = time::precise_time_s(); let graph = make_graph(1 << scale, edges.clone()); let stop = time::precise_time_s(); let mut total_edges = 0; for edges in graph.iter() { total_edges += edges.len(); } io::stdout().write_line(fmt!("Generated graph with %? edges in %? seconds.", total_edges / 2, stop - start)); let mut total_seq = 0.0; let mut total_par = 0.0; let graph_arc = arc::Arc::new(graph.clone()); do gen_search_keys(graph, num_keys).map() |root| { io::stdout().write_line(""); io::stdout().write_line(fmt!("Search key: %?", root)); if do_sequential { let start = time::precise_time_s(); let bfs_tree = bfs(graph.clone(), *root); let stop = time::precise_time_s(); //total_seq += stop - start; io::stdout().write_line( fmt!("Sequential BFS completed in %? seconds.", stop - start)); if do_validate { let start = time::precise_time_s(); assert!((validate(edges.clone(), *root, bfs_tree))); let stop = time::precise_time_s(); io::stdout().write_line( fmt!("Validation completed in %? seconds.", stop - start)); } let start = time::precise_time_s(); let bfs_tree = bfs2(graph.clone(), *root); let stop = time::precise_time_s(); total_seq += stop - start; io::stdout().write_line( fmt!("Alternate Sequential BFS completed in %? seconds.", stop - start)); if do_validate { let start = time::precise_time_s(); assert!((validate(edges.clone(), *root, bfs_tree))); let stop = time::precise_time_s(); io::stdout().write_line( fmt!("Validation completed in %? seconds.", stop - start)); } } let start = time::precise_time_s(); let bfs_tree = pbfs(&graph_arc, *root); let stop = time::precise_time_s(); total_par += stop - start; io::stdout().write_line(fmt!("Parallel BFS completed in %? seconds.", stop - start)); if do_validate { let start = time::precise_time_s(); assert!((validate(edges.clone(), *root, bfs_tree))); let stop = time::precise_time_s(); io::stdout().write_line(fmt!("Validation completed in %? seconds.", stop - start)); } }; io::stdout().write_line(""); io::stdout().write_line( fmt!("Total sequential: %? t Total Parallel: %? t Speedup: %?x", total_seq, total_par, total_seq / total_par)); } </del>