rayon is a popular crate to easily write multi-threaded code in Rust.
It is possible to use rayon to write multi-threaded TFHE-rs code. However due to internal details of rayon and TFHE-rs, there is some special setup that needs to be done.
Single Client Application
The Problem
The high level api requires to call set_server_key on each thread where computations needs to be done. So a first attempt at using rayon with TFHE-rs might look like this:
use rayon::prelude::*;use tfhe::prelude::*;use tfhe::{ConfigBuilder, set_server_key, FheUint8, generate_keys};fnmain() {let (cks, sks) =generate_keys(ConfigBuilder::default());let xs = [FheUint8::encrypt(1u8, &cks),FheUint8::encrypt(2u8, &cks), ];let ys = [FheUint8::encrypt(3u8, &cks),FheUint8::encrypt(4u8, &cks), ];// set_server_key in each closure as they might be// running in different threadslet (a, b) = rayon::join(|| {set_server_key(sks.clone());&xs[0] +&ys[0] },|| {set_server_key(sks.clone());&xs[1] +&ys[1] } );}
However, due to rayon's work stealing mechanism and TFHE-rs's internals, this may create `BorrowMutError'.
Working Example
The correct way is to call rayon::broadcast
use rayon::prelude::*;use tfhe::prelude::*;use tfhe::{ConfigBuilder, set_server_key, FheUint8, generate_keys};fnmain() {let (cks, sks) =generate_keys(ConfigBuilder::default());// set the server key in all of the rayon's threads so that// we won't need to do it later rayon::broadcast(|_|set_server_key(sks.clone()));// Set the server key in the main threadset_server_key(sks);let xs = [FheUint8::encrypt(1u8, &cks),FheUint8::encrypt(2u8, &cks), ];let ys = [FheUint8::encrypt(3u8, &cks),FheUint8::encrypt(4u8, &cks), ];let (a, b) = rayon::join(|| {&xs[0] +&ys[0] },|| {&xs[1] +&ys[1] } );let a:u8= a.decrypt(&cks);let b:u8= b.decrypt(&cks);assert_eq!(a, 4u8);assert_eq!(b, 6u8);}
Multi-Client Applications
If your application needs to operate on data from different clients concurrently, and that you want each client to use multiple threads, you will need to create different rayon thread pools