## DEV Community 👩‍💻👨‍💻

Billy Chan

Posted on

Most program we wrote is single threaded. Since most of the task is IO bound. It make sense to keep it simple by writing a single threaded program. But what if we have a CPU bound task? Let say, cracking a MD5 hash? Don't ask me why cracking MD5 hash, it's just one of CPU intensive task that I can think of, purely for demo purpose.

A naive hashing algorithm will be like this.

// Iterate from 0 util we found the target number
let mut i = 0_u32;
loop {
// Hash our guessing number and compare with the target hash
// Stop the loop if we found the target number
if md5::compute(i.to_string()).eq(&TARGET_HASH) {
break;
}
// What's our next guess?
i += 1;
}

For the hashing task we can distribute the work to N threads. With each thread start search the target number starting from N and continue the search by incrementing the number by N.

So, for example we've 3 threads. The search space for each thread would be:

Thread 1: 1, 4, 7, 10...
Thread 2: 2, 5, 8, 11...
Thread 3: 3, 6, 9, 12...

That's how we divide the task to N threads with each thread work on a unique and non-overlapping search space.

// A MPSC channel to inform the main thread some worker found the solution
let (tx, rx) = mpsc::channel();
print!("Parallel hashing with {} threads: ", num_workers);

// Spawn workers
for worker in 0..num_workers {
// Clone the transmission end and move it into the spawned worker
let tx1 = tx.clone();
// The guess number start from the worker number
let mut i = worker;
// Iterate from 0 util we found the target number
loop {
// Hash our guessing number and compare with the target hash
let hash = md5::compute(i.to_string());
// Stop the loop if we found the target number
// And inform the main thread that we found the target number
if hash.eq(&TARGET_HASH) {
tx1.send("Found!").unwrap();
break;
}
// What's our next guess?
i += num_workers;
}
});
}

while let Ok(_) = rx.recv() {