TFHE-rs v0.11 brings several major improvements and new features. This release introduces significantly enhanced performance for Zero Knowledge Proof and a new module for encrypted strings usable in the High Level API via the [.c-inline-code]FheAsciiString[.c-inline-code] type. Besides, default cryptographic parameters now follow a tweaked uniform (TUniform) noise distribution instead of a Gaussian one. GPU performance has also been improved: the 64-bit addition is 30% faster compared to v0.8. Finally, it is now possible to perform computations on arrays of ciphertexts easily on GPU.
The new Zero Knowledge proof construction is now faster both for generating and verifying a proof, as detailed in the benchmark table below.
.jpeg)
You can follow along the documentation guide to use Zero Knowledge Proofs in the High-Level API.
One of the highlights of this release is the FHE strings module, based on an open source contribution from user JoseSK999 during the Zama Bounty Program Season 5. It is also available in the High-Level API via the [.c-inline-code]FheAsciiString[.c-inline-code] type. FHE strings support the same APIs as Rust’s str type. You can find the guide on FHE strings and how to use them in the documentation.
Here is a preview of what you can do with the new primitive with the integer and strings features enabled:
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ClearString, ConfigBuilder, FheAsciiString};
fn main() {
let config = ConfigBuilder::default().build();
let (cks, sks) = generate_keys(config);
set_server_key(sks);
// Encrypt without padding, does not hide the string length and has better performance
let string = FheAsciiString::try_encrypt("TFHE-rs rocks!", &cks).unwrap();
// Encrypt with padding, hide the true length of the string
let search_string = FheAsciiString::try_encrypt_with_padding("is meh", 1, &cks).unwrap();
// We can also use clear strings
let clear_search_string = ClearString::new("rocks".to_string());
// Does our initial string contain "is meh"?
let does_not_contain = string.contains(&search_string);
// Does our initial string contain "rocks"?
let contains = string.contains(&clear_search_string);
// Decrypt
let decrypted_does_not_contain = does_not_contain.decrypt(&cks);
let decrypted_contains = contains.decrypt(&cks);
// Check all worked properly
assert!(!decrypted_does_not_contain);
assert!(decrypted_contains);
}
We expect most string processing to consist mostly of comparison and contains operation, here are the current benchmarks on an hpc7a.96xlarge AWS machine:
.jpeg)
TFHE-rs v0.11 introduces n-dimensional arrays (or tensors) for encrypted data on GPU. The supported operations are the same as on CPU, including the comparison between vectors and the search in a vector of encrypted data.
The following example displays how to use array types on GPU: it shows how to extract submatrices of size 2x2 from two 4x4 matrices, perform addition, and then add a clear matrix to the previous results. You can find more details on how to use homomorphic arrays in the documentation.
use tfhe::{ConfigBuilder, set_server_key, CpuFheUint32Array, ClearArray, ClientKey, CompressedServerKey};
use tfhe::array::GpuFheUint32Array;
use tfhe::prelude::*;
fn main() {
let config = ConfigBuilder::default().build();
let cks = ClientKey::generate(config);
let compressed_server_key = CompressedServerKey::new(&cks);
let gpu_key = compressed_server_key.decompress_to_gpu();
set_server_key(gpu_key);
let num_elems = 4 * 4;
let clear_xs = (0..num_elems as u32).collect::<Vec<_>>();
let clear_ys = vec![1u32; num_elems];
// Encrypted 2D array with values
// [[ 0, 1, 2, 3]
// [ 4, 5, 6, 7]
// [ 8, 9, 10, 11]
// [ 12, 13, 14, 15]]
// and shape 4x4
let xs = GpuFheUint32Array::try_encrypt((clear_xs.as_slice(), vec![4, 4]), &cks).unwrap();
// Encrypted 2D array with values
// [[ 1, 1, 1, 1]
// [ 1, 1, 1, 1]
// [ 1, 1, 1, 1]
// [ 1, 1, 1, 1]]
// and shape 4x4
let ys = GpuFheUint32Array::try_encrypt((clear_ys.as_slice(), vec![4, 4]), &cks).unwrap();
assert_eq!(xs.num_dim(), 2);
assert_eq!(xs.shape(), &[4, 4]);
assert_eq!(ys.num_dim(), 2);
assert_eq!(ys.shape(), &[4, 4]);
// Take a sub slice
// [[ 10, 11]
// [ 14, 15]]
let xss = xs.slice(&[2..4, 2..4]);
// Take a sub slice
// [[ 1, 1]
// [ 1, 1]]
let yss = ys.slice(&[2..4, 2..4]);
assert_eq!(xss.num_dim(), 2);
assert_eq!(xss.shape(), &[2, 2]);
assert_eq!(yss.num_dim(), 2);
assert_eq!(yss.shape(), &[2, 2]);
let r = &xss + &yss;
// Result is
// [[ 11, 12]
// [ 15, 16]]
let result: Vec = r.decrypt(&cks);
assert_eq!(result, vec![11, 12, 15, 16]);
// Clear 2D array with values
// [[ 10, 20]
// [ 30, 40]]
let clear_array = ClearArray::new(vec![10u32, 20u32, 30u32, 40u32], vec![2, 2]);
}
This version brings performance improvements to the GPU backend. The 64-bit addition is 30% faster than v0.8. The Figure 1 below shows the current performance on CPU & GPU for the 64-bit addition and multiplication.
.jpeg)
The optimal number of GPUs per operation varies depending on the operation itself and the integer precision specified by the user. Comprehensive arrays of benchmark results for both single and multiple GPUs across all specified precisions are available in the documentation.
TFHE-rs v0.11 introduces several other features or improvements, including:
The next release of TFHE-rs will continue to improve performance, features and stability! Stay tuned for the upcoming update!
News, research and product releases