]> git.lizzy.rs Git - rust.git/commitdiff
shootout-meteor improvement
authorGuillaume Pinot <texitoi@texitoi.eu>
Fri, 9 May 2014 15:32:06 +0000 (17:32 +0200)
committerGuillaume Pinot <texitoi@texitoi.eu>
Fri, 9 May 2014 15:39:00 +0000 (17:39 +0200)
- 5-10% of raw speedup
- parallelization of the search

src/test/bench/shootout-meteor.rs

index cb46c542f5bc84c03af85f141d9877f5226b301c..45f8b1b9a839c5d7aac957c2a916ac237d0006b5 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,11 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(phase)]
+#[phase(syntax)] extern crate green;
+extern crate sync;
+
+use sync::Arc;
+
+green_start!(main)
+
 //
 // Utilities.
 //
 
-
 // returns an infinite iterator of repeated applications of f to x,
 // i.e. [x, f(x), f(f(x)), ...], as haskell iterate function.
 fn iterate<'a, T>(x: T, f: |&T|: 'a -> T) -> Iterate<'a, T> {
@@ -93,7 +100,7 @@ fn transform(piece: Vec<(int, int)> , all: bool) -> Vec<Vec<(int, int)>> {
 // Takes a piece with minimum coordinate (0, 0) (as generated by
 // transform).  Returns the corresponding mask if p translated by (dy,
 // dx) is on the board.
-fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
+fn mask(dy: int, dx: int, id: uint, p: &Vec<(int, int)>) -> Option<u64> {
     let mut m = 1 << (50 + id);
     for &(y, x) in p.iter() {
         let x = x + dx + (y + (dy % 2)) / 2;
@@ -105,7 +112,7 @@ fn mask(dy: int, dx: int, id: uint, p: &[(int, int)]) -> Option<u64> {
     Some(m)
 }
 
-// Makes every possible masks.  masks[id][i] correspond to every
+// Makes every possible masks.  masks[i][id] correspond to every
 // possible masks for piece with identifier id with minimum coordinate
 // (i/5, i%5).
 fn make_masks() -> Vec<Vec<Vec<u64> > > {
@@ -120,168 +127,182 @@ fn make_masks() -> Vec<Vec<Vec<u64> > > {
         vec!((0,0),(0,1),(0,2),(1,0),(1,2)),
         vec!((0,0),(0,1),(0,2),(1,2),(1,3)),
         vec!((0,0),(0,1),(0,2),(0,3),(1,2)));
-    let mut res = Vec::new();
-    for (id, p) in pieces.move_iter().enumerate() {
-        // To break the central symetry of the problem, every
-        // transformation must be taken except for one piece (piece 3
-        // here).
-        let trans = transform(p, id != 3);
-        let mut cur_piece = Vec::new();
-        for dy in range(0, 10) {
-            for dx in range(0, 5) {
-                let masks =
-                    trans.iter()
-                    .filter_map(|t| mask(dy, dx, id, t.as_slice()))
-                    .collect();
-                cur_piece.push(masks);
-            }
-        }
-        res.push(cur_piece);
-    }
-    res
+
+    // To break the central symetry of the problem, every
+    // transformation must be taken except for one piece (piece 3
+    // here).
+    let transforms: Vec<Vec<Vec<(int, int)>>> =
+        pieces.move_iter().enumerate()
+        .map(|(id, p)| transform(p, id != 3))
+        .collect();
+
+    range(0, 50).map(|yx| {
+        transforms.iter().enumerate().map(|(id, t)| {
+            t.iter().filter_map(|p| mask(yx / 5, yx % 5, id, p)).collect()
+        }).collect()
+    }).collect()
 }
 
 // Check if all coordinates can be covered by an unused piece and that
 // all unused piece can be placed on the board.
-fn is_board_unfeasible(board: u64, masks: &[Vec<Vec<u64> > ]) -> bool {
+fn is_board_unfeasible(board: u64, masks: &Vec<Vec<Vec<u64>>>) -> bool {
     let mut coverable = board;
-    for i in range(0, 50).filter(|&i| board & 1 << i == 0) {
-        for (cur_id, pos_masks) in masks.iter().enumerate() {
-            if board & 1 << (50 + cur_id) != 0 {continue;}
-            for &cur_m in pos_masks.get(i as uint).iter() {
-                if cur_m & board == 0 {coverable |= cur_m;}
+    for (i, masks_at) in masks.iter().enumerate() {
+        if board & 1 << i != 0 { continue; }
+        for (cur_id, pos_masks) in masks_at.iter().enumerate() {
+            if board & 1 << (50 + cur_id) != 0 { continue; }
+            for &cur_m in pos_masks.iter() {
+                if cur_m & board != 0 { continue; }
+                coverable |= cur_m;
+                // if every coordinates can be covered and every
+                // piece can be used.
+                if coverable == (1 << 60) - 1 { return false; }
             }
         }
-        if coverable & (1 << i) == 0 {return true;}
+        if coverable & 1 << i == 0 { return true; }
     }
-    // check if every coordinates can be covered and every piece can
-    // be used.
-    coverable != (1 << 60) - 1
+    true
 }
 
 // Filter the masks that we can prove to result to unfeasible board.
-fn filter_masks(masks: &[Vec<Vec<u64> > ]) -> Vec<Vec<Vec<u64> > > {
-    masks.iter().map(
-        |p| p.iter().map(
-            |p| p.iter()
-                .map(|&m| m)
+fn filter_masks(masks: &mut Vec<Vec<Vec<u64>>>) {
+    for i in range(0, masks.len()) {
+        for j in range(0, masks.get(i).len()) {
+            *masks.get_mut(i).get_mut(j) =
+                masks.get(i).get(j).iter().map(|&m| m)
                 .filter(|&m| !is_board_unfeasible(m, masks))
-                .collect())
-            .collect())
-        .collect()
+                .collect();
+        }
+    }
 }
 
 // Gets the identifier of a mask.
 fn get_id(m: u64) -> u8 {
-    for id in range(0, 10) {
-        if m & (1 << (id + 50)) != 0 {return id as u8;}
+    for id in range(0u8, 10) {
+        if m & (1 << (id + 50)) != 0 {return id;}
     }
     fail!("{:016x} does not have a valid identifier", m);
 }
 
 // Converts a list of mask to a ~str.
-fn to_utf8(raw_sol: &List<u64>) -> ~str {
-    let mut sol: Vec<u8> = Vec::from_elem(50, '.' as u8);
+fn to_vec(raw_sol: &List<u64>) -> Vec<u8> {
+    let mut sol = Vec::from_elem(50, '.' as u8);
     for &m in raw_sol.iter() {
-        let id = get_id(m);
-        for i in range(0, 50) {
+        let id = '0' as u8 + get_id(m);
+        for i in range(0u, 50) {
             if m & 1 << i != 0 {
-                *sol.get_mut(i as uint) = '0' as u8 + id;
+                *sol.get_mut(i) = id;
             }
         }
     }
-    std::str::from_utf8(sol.as_slice()).unwrap().to_owned()
+    sol
 }
 
 // Prints a solution in ~str form.
-fn print_sol(sol: &str) {
-    for (i, c) in sol.chars().enumerate() {
+fn print_sol(sol: &Vec<u8>) {
+    for (i, c) in sol.iter().enumerate() {
         if (i) % 5 == 0 { println!(""); }
         if (i + 5) % 10 == 0 { print!(" "); }
-        print!("{} ", c);
+        print!("{} ", *c as char);
     }
     println!("");
 }
 
 // The data managed during the search
 struct Data {
-    // If more than stop_after is found, stop the search.
-    stop_after: int,
     // Number of solution found.
     nb: int,
     // Lexicographically minimal solution found.
-    min: ~str,
+    min: Vec<u8>,
     // Lexicographically maximal solution found.
-    max: ~str
+    max: Vec<u8>
+}
+impl Data {
+    fn new() -> Data {
+        Data {nb: 0, min: vec!(), max: vec!()}
+    }
+    fn reduce_from(&mut self, other: Data) {
+        self.nb += other.nb;
+        let Data { min: min, max: max, ..} = other;
+        if min < self.min { self.min = min; }
+        if max > self.max { self.max = max; }
+    }
 }
 
 // Records a new found solution.  Returns false if the search must be
 // stopped.
-fn handle_sol(raw_sol: &List<u64>, data: &mut Data) -> bool {
+fn handle_sol(raw_sol: &List<u64>, data: &mut Data) {
     // because we break the symetry, 2 solutions correspond to a call
     // to this method: the normal solution, and the same solution in
     // reverse order, i.e. the board rotated by half a turn.
     data.nb += 2;
-    let sol1 = to_utf8(raw_sol);
-    let sol2: ~str = sol1.chars().rev().collect();
+    let sol1 = to_vec(raw_sol);
+    let sol2: Vec<u8> = sol1.iter().rev().map(|x| *x).collect();
 
     if data.nb == 2 {
         data.min = sol1.clone();
         data.max = sol1.clone();
     }
 
-    if sol1 < data.min {data.min = sol1.clone();}
-    if sol2 < data.min {data.min = sol2.clone();}
-    if sol1 > data.max {data.max = sol1;}
-    if sol2 > data.max {data.max = sol2;}
-    data.nb < data.stop_after
+    if sol1 < data.min {data.min = sol1;}
+    else if sol1 > data.max {data.max = sol1;}
+    if sol2 < data.min {data.min = sol2;}
+    else if sol2 > data.max {data.max = sol2;}
 }
 
-// Search for every solutions.  Returns false if the search was
-// stopped before the end.
 fn search(
-    masks: &[Vec<Vec<u64> > ],
+    masks: &Vec<Vec<Vec<u64>>>,
     board: u64,
-    mut i: int,
+    mut i: uint,
     cur: List<u64>,
     data: &mut Data)
-    -> bool
 {
     // Search for the lesser empty coordinate.
     while board & (1 << i)  != 0 && i < 50 {i += 1;}
     // the board is full: a solution is found.
     if i >= 50 {return handle_sol(&cur, data);}
+    let masks_at = masks.get(i);
 
     // for every unused piece
-    for id in range(0, 10).filter(|id| board & (1 << (id + 50)) == 0) {
+    for id in range(0u, 10).filter(|id| board & (1 << (id + 50)) == 0) {
         // for each mask that fits on the board
-        for &m in masks[id as uint].get(i as uint)
-                                   .iter()
-                                   .filter(|&m| board & *m == 0) {
+        for &m in masks_at.get(id).iter().filter(|&m| board & *m == 0) {
             // This check is too costy.
             //if is_board_unfeasible(board | m, masks) {continue;}
-            if !search(masks, board | m, i + 1, Cons(m, &cur), data) {
-                return false;
-            }
+            search(masks, board | m, i + 1, Cons(m, &cur), data);
         }
     }
-    return true;
+}
+
+fn par_search(masks: Vec<Vec<Vec<u64>>>) -> Data {
+    let masks = Arc::new(masks);
+    let (tx, rx) = channel();
+
+    // launching the search in parallel on every masks at minimum
+    // coordinate (0,0)
+    for &m in masks.get(0).iter().flat_map(|masks_pos| masks_pos.iter()) {
+        let masks = masks.clone();
+        let tx = tx.clone();
+        spawn(proc() {
+            let mut data = Data::new();
+            search(&*masks, m, 1, Cons(m, &Nil), &mut data);
+            tx.send(data);
+        });
+    }
+
+    // collecting the results
+    drop(tx);
+    let mut data = rx.recv();
+    for d in rx.iter() { data.reduce_from(d); }
+    data
 }
 
 fn main () {
-    let args = std::os::args();
-    let args = args.as_slice();
-    let stop_after = if args.len() <= 1 {
-        2098
-    } else {
-        from_str(args[1]).unwrap()
-    };
-    let masks = make_masks();
-    let masks = filter_masks(masks.as_slice());
-    let mut data = Data {stop_after: stop_after, nb: 0, min: "".to_owned(), max: "".to_owned()};
-    search(masks.as_slice(), 0, 0, Nil, &mut data);
+    let mut masks = make_masks();
+    filter_masks(&mut masks);
+    let data = par_search(masks);
     println!("{} solutions found", data.nb);
-    print_sol(data.min);
-    print_sol(data.max);
+    print_sol(&data.min);
+    print_sol(&data.max);
     println!("");
 }