Before going into parallelization, Iād suggest improvements to the algorithm. As I called out, essentially all of its time is spent in a highly suboptimal, quadratic-time, duplicates check.
A trivial usage of a HashSet
already makes the whole thing go 10x
valid = true;
let mut set = HashSet::with_capacity(c);
for &s in &sums[..c] {
if !set.insert(s) {
valid = false;
break;
}
}
An even faster representation of the set could get me about another 2-3x
pub fn main() {
println!("start key-gen-face-five");
// init
let mut t: u32; // t=trial key, k=index searched key
let mut c: usize; // sums counter
let mut valid: bool; // true if key is valid
let mut sums = [0; 50000]; // array of all possible sums of key[c[1-5]]
// collects the set of values originally in `sums`
// value `s` is in the set if the entry `set[s]` contains the current `t` value
let mut set = Vec::new();
let mut key = [0, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // init keys - empirical
println!("bootstrap -> keys={:?}", key);
let start = Instant::now();
t = 5;
for k in 3..11 {
println!("searching for k={}", k);
// sanity check, `t` is always just increasing;
// An always unique / fresh `t` value is important for correct representation of the
// `set` array which thus allows us to skip any step of *clear*ing the set
// since with incremented `t` all entries are considered empty again.
assert_eq!(key[k - 1] + 1u32, t + 1);
t = key[k - 1] + 1u32;
let interm = Instant::now();
valid = false;
while !valid {
key[k] = t;
let c_max = k + 1;
c = 0;
for (c1, &k1) in zip(0.., key[0..c_max].iter()) {
for (c2, &k2) in zip(c1.., key[c1..c_max].iter()) {
for (c3, &k3) in zip(c2.., key[c2..c_max].iter()) {
for (c4, &k4) in zip(c3.., key[c3..c_max].iter()) {
for (c5, &k5) in zip(c4.., key[c4..c_max].iter()) {
if c1 != c5 {
sums[c] = k1 + k2 + k3 + k4 + k5;
c += 1;
}
}
}
}
}
}
valid = true;
// conservative estimate of largest k1 + k2 + k3 + k4 + k5 value
// which is always less than 5*t
set.resize(t as usize * 5, 0);
for &s in &sums[..c] {
let e = &mut set[s as usize];
if *e == t {
valid = false;
break;
} else {
*e = t;
}
}
if valid {
println!("key[{}]={:?}", k, t);
println!("c={:?}", c);
let end = Instant::now();
println!("\truntime for key[{}] = {:?}", k, (end - interm));
println!("\truntime for key[{}] = {:?}", k, (end - start));
} else {
t += 1;
}
}
// k += 1;
}
let end = Instant::now();
println!("runtime = {:?}", (end - start));
}
and finally, integrating the duplicate detection with the loop that used to create sums
is another 4-5x faster
pub fn main() {
println!("start key-gen-face-five");
// init
let mut t: u32; // t=trial key, k=index searched key
let mut c: usize; // sums counter
let mut valid: bool; // true if key is valid
// let mut sums = [0; 50000]; // array of all possible sums of key[c[1-5]]
// collects the set of values originally in `sums`
// value `s` is in the set if the entry `set[s]` contains the current `t` value
let mut set = Vec::new();
let mut key = [0, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // init keys - empirical
println!("bootstrap -> keys={:?}", key);
let start = Instant::now();
t = 5;
for k in 3..11 {
println!("searching for k={}", k);
// sanity check, `t` is always just increasing;
// An always unique / fresh `t` value is important for correct representation of the
// `set` array which thus allows us to skip any step of *clear*ing the set
// since with incremented `t` all entries are considered empty again.
assert_eq!(key[k - 1] + 1u32, t + 1);
t = key[k - 1] + 1u32;
let interm = Instant::now();
valid = false;
while !valid {
key[k] = t;
let c_max = k + 1;
c = 0;
valid = true;
set.resize(t as usize * 5, 0);
'outer: for (c1, &k1) in zip(0.., key[0..c_max].iter()) {
for (c2, &k2) in zip(c1.., key[c1..c_max].iter()) {
for (c3, &k3) in zip(c2.., key[c2..c_max].iter()) {
for (c4, &k4) in zip(c3.., key[c3..c_max].iter()) {
for (c5, &k5) in zip(c4.., key[c4..c_max].iter()) {
if c1 != c5 {
let e = &mut set[(k1 + k2 + k3 + k4 + k5) as usize];
if *e == t {
valid = false;
break 'outer;
} else {
*e = t;
}
}
}
}
}
}
}
if valid {
println!("key[{}]={:?}", k, t);
println!("c={:?}", c);
let end = Instant::now();
println!("\truntime for key[{}] = {:?}", k, (end - interm));
println!("\truntime for key[{}] = {:?}", k, (end - start));
} else {
t += 1;
}
}
// k += 1;
}
let end = Instant::now();
println!("runtime = {:?}", (end - start));
}