Compare commits

...

4 Commits

Author SHA1 Message Date
David Testé
c047ee700d WIP: try inline never on hlapi bench functions 2026-01-16 11:29:10 +01:00
Theo Souchon
5e0a2ee366 chore(bench): add missing operation in hlapi benches 2026-01-15 09:10:28 +01:00
Nicolas Sarlin
6300a025d9 chore(docs): fix api levels description 2026-01-13 09:43:49 +01:00
David Testé
7222bff5d6 chore(ci): fix artifact naming for hpu benchmarks
Prior to this commit, all generated artifacts would be identified
as integer benchmarks.
2026-01-12 15:42:24 +01:00
10 changed files with 737 additions and 71 deletions

View File

@@ -187,7 +187,7 @@ jobs:
- name: Upload parsed results artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with:
name: ${{ github.sha }}_${{ matrix.bench_type }}_integer_benchmarks
name: ${{ github.sha }}_${{ matrix.bench_type }}_${{ matrix.command }}_benchmarks
path: ${{ env.RESULTS_FILENAME }}
- name: Checkout Slab repo

View File

@@ -1639,7 +1639,7 @@ bench_hlapi: install_rs_check_toolchain
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_BENCH_BIT_SIZES_SET=$(BIT_SIZES_SET) \
cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
--bench hlapi \
--features=integer,internal-keycache,pbs-stats -p tfhe-benchmark --
--features=integer,internal-keycache,pbs-stats -p tfhe-benchmark -- '::add|::mul|::bitand'
.PHONY: bench_hlapi_gpu # Run benchmarks for integer operations on GPU
bench_hlapi_gpu: install_rs_check_toolchain

View File

@@ -3,6 +3,7 @@ use benchmark::utilities::{
};
use criterion::{black_box, Criterion, Throughput};
use oprf::oprf_any_range2;
use rand::distributions::Standard;
use rand::prelude::*;
use rayon::prelude::*;
use std::marker::PhantomData;
@@ -13,13 +14,144 @@ use tfhe::keycache::NamedParam;
use tfhe::named::Named;
use tfhe::prelude::*;
use tfhe::{
ClientKey, CompressedServerKey, FheIntegerType, FheUint, FheUint10, FheUint12, FheUint128,
FheUint14, FheUint16, FheUint2, FheUint32, FheUint4, FheUint6, FheUint64, FheUint8, FheUintId,
IntegerId, KVStore,
ClientKey, CompressedServerKey, FheBool, FheInt, FheInt10, FheInt12, FheInt128, FheInt14,
FheInt16, FheInt2, FheInt32, FheInt4, FheInt6, FheInt64, FheInt8, FheIntId, FheIntegerType,
FheUint, FheUint10, FheUint12, FheUint128, FheUint14, FheUint16, FheUint2, FheUint32, FheUint4,
FheUint6, FheUint64, FheUint8, FheUintId, IntegerId, KVStore,
};
trait NumConsts {
fn zero() -> Self;
fn one() -> Self;
}
impl NumConsts for u8 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for u16 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for u32 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for u64 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for u128 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for i8 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for i16 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for i32 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for i64 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
impl NumConsts for i128 {
fn zero() -> Self {
0
}
fn one() -> Self {
1
}
}
mod oprf;
fn random_non_zero<T>() -> T
where
Standard: Distribution<T>,
T: Copy + PartialEq + NumConsts,
{
let mut rng = rand::thread_rng();
loop {
let v: T = rng.gen();
if v != T::zero() {
return v;
}
}
}
fn random_not_power_of_two<T>() -> T
where
Standard: Distribution<T>,
T: Copy + PartialEq + BitAnd<Output = T> + Sub<Output = T> + NumConsts,
{
let mut rng = rand::thread_rng();
loop {
let v: T = rng.gen();
if !(v != T::zero() && (v & (v - T::one())) == T::zero()) {
return v;
}
}
}
fn get_one<T>() -> T
where
T: From<u8>,
{
T::from(1)
}
fn random<T>() -> T
where
Standard: Distribution<T>,
{
rand::thread_rng().gen()
}
trait BenchWait {
fn wait_bench(&self);
}
@@ -30,24 +162,185 @@ impl<Id: FheUintId> BenchWait for FheUint<Id> {
}
}
impl<Id: FheIntId> BenchWait for FheInt<Id> {
fn wait_bench(&self) {
self.wait()
}
}
impl BenchWait for FheBool {
fn wait_bench(&self) {
self.wait()
}
}
impl<T1: FheWait, T2> BenchWait for (T1, T2) {
fn wait_bench(&self) {
self.0.wait()
}
}
fn bench_fhe_type_op<FheType, F, R>(
trait BenchmarkOp<FheType> {
type Output: BenchWait;
type Inputs;
/// Setup the encrypted inputs for the operation
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs;
/// Execute the operation with the inputs
fn execute(&self, inputs: &Self::Inputs) -> Self::Output;
}
struct UnaryOp<F, EncryptType> {
func: F,
_encrypt: PhantomData<EncryptType>,
}
impl<FheType, F, R, EncryptType> BenchmarkOp<FheType> for UnaryOp<F, EncryptType>
where
F: Fn(&FheType) -> R,
R: BenchWait,
FheType: FheEncrypt<EncryptType, ClientKey>,
Standard: Distribution<EncryptType>,
{
type Output = R;
type Inputs = FheType;
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs {
FheType::encrypt(rng.gen(), client_key)
}
fn execute(&self, inputs: &Self::Inputs) -> Self::Output {
(self.func)(inputs)
}
}
struct ScalarBinaryOp<F, G, EncryptType> {
func: F,
rng_function: G,
_encrypt: PhantomData<EncryptType>,
}
impl<FheType, F, R, G, T, EncryptType> BenchmarkOp<FheType> for ScalarBinaryOp<F, G, EncryptType>
where
F: Fn(&FheType, &T) -> R,
R: BenchWait,
G: Fn() -> T,
FheType: FheEncrypt<EncryptType, ClientKey>,
Standard: Distribution<EncryptType>,
{
type Output = R;
type Inputs = (FheType, T);
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs {
(
FheType::encrypt(rng.gen(), client_key),
(self.rng_function)(),
)
}
fn execute(&self, inputs: &Self::Inputs) -> Self::Output {
(self.func)(&inputs.0, &inputs.1)
}
}
struct BinaryOp<F, EncryptLhsType, EncryptRhsType, FheRhsType> {
func: F,
_encrypt_lhs: PhantomData<EncryptLhsType>,
_encrypt_rhs: PhantomData<EncryptRhsType>,
_rhs_type: PhantomData<FheRhsType>,
}
impl<FheType, FheRhsType, F, R, EncryptLhsType, EncryptRhsType> BenchmarkOp<FheType>
for BinaryOp<F, EncryptLhsType, EncryptRhsType, FheRhsType>
where
F: Fn(&FheType, &FheRhsType) -> R,
R: BenchWait,
FheType: FheEncrypt<EncryptLhsType, ClientKey>,
FheRhsType: FheEncrypt<EncryptRhsType, ClientKey>,
Standard: Distribution<EncryptLhsType>,
Standard: Distribution<EncryptRhsType>,
{
type Output = R;
type Inputs = (FheType, FheRhsType);
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs {
(
FheType::encrypt(rng.gen(), client_key),
FheRhsType::encrypt(rng.gen(), client_key),
)
}
fn execute(&self, inputs: &Self::Inputs) -> Self::Output {
(self.func)(&inputs.0, &inputs.1)
}
}
struct TernaryOp<F, EncryptType> {
func: F,
_encrypt: PhantomData<EncryptType>,
}
impl<FheType, F, R, EncryptType> BenchmarkOp<FheType> for TernaryOp<F, EncryptType>
where
F: Fn(&FheBool, &FheType, &FheType) -> R,
R: BenchWait,
FheType: FheEncrypt<EncryptType, ClientKey>,
Standard: Distribution<EncryptType>,
{
type Output = R;
type Inputs = (FheBool, FheType, FheType);
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs {
(
FheBool::encrypt(rng.gen::<bool>(), client_key),
FheType::encrypt(rng.gen(), client_key),
FheType::encrypt(rng.gen(), client_key),
)
}
fn execute(&self, inputs: &Self::Inputs) -> Self::Output {
(self.func)(&inputs.0, &inputs.1, &inputs.2)
}
}
struct ArrayOp<F, EncryptType> {
func: F,
array_size: usize,
_encrypt: PhantomData<EncryptType>,
}
impl<FheType, F, EncryptType> BenchmarkOp<FheType> for ArrayOp<F, EncryptType>
where
F: for<'a> Fn(std::iter::Cloned<std::slice::Iter<'a, FheType>>) -> FheType,
FheType: FheEncrypt<EncryptType, ClientKey> + Clone + BenchWait,
Standard: Distribution<EncryptType>,
{
type Output = FheType;
type Inputs = Vec<FheType>;
fn setup_inputs(&self, client_key: &ClientKey, rng: &mut ThreadRng) -> Self::Inputs {
(0..self.array_size)
.map(|_| FheType::encrypt(rng.gen(), client_key))
.collect()
}
fn execute(&self, inputs: &Self::Inputs) -> Self::Output {
(self.func)(inputs.iter().cloned())
}
}
#[inline(never)]
fn bench_fhe_type_op<FheType, Op>(
c: &mut Criterion,
client_key: &ClientKey,
type_name: &str,
bit_size: usize,
display_name: &str,
func_name: &str,
func: F,
op: Op,
) where
F: Fn(&FheType, &FheType) -> R,
R: BenchWait,
FheType: FheEncrypt<u128, ClientKey>,
Op: BenchmarkOp<FheType>,
FheType: FheWait,
{
let mut bench_group = c.benchmark_group(type_name);
@@ -66,101 +359,340 @@ fn bench_fhe_type_op<FheType, F, R>(
let param_name = param.name();
let bit_size = bit_size as u32;
let write_record = |bench_id: String, display_name| {
write_to_json::<u64, _>(
&bench_id,
param,
&param_name,
display_name,
&OperatorType::Atomic,
bit_size,
vec![],
);
};
let lhs = FheType::encrypt(rng.gen(), client_key);
let rhs = FheType::encrypt(rng.gen(), client_key);
let inputs = op.setup_inputs(client_key, &mut rng);
let bench_id = format!("{bench_prefix}::{func_name}::{param_name}::{type_name}");
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
let res = func(&lhs, &rhs);
let res = op.execute(&inputs);
res.wait_bench();
black_box(res)
})
});
write_record(bench_id, display_name);
write_to_json::<u64, _>(
&bench_id,
param,
&param_name,
display_name,
&OperatorType::Atomic,
bit_size,
vec![],
);
}
macro_rules! bench_type_op (
(type_name: $fhe_type:ident, display_name: $display_name:literal, operation: $op:ident) => {
macro_rules! bench_type_binary_op {
(type_name: $fhe_type:ident, right_type_name: $fhe_right_type:ident,left_type: $left_type:ty, right_type: $right_type:ty, display_name: $display_name:literal, operation: $op:ident) => {
::paste::paste! {
#[inline(never)]
fn [<bench_ $fhe_type:snake _ $op>](c: &mut Criterion, cks: &ClientKey) {
bench_fhe_type_op::<$fhe_type, _, _>(
bench_fhe_type_op(
c,
cks,
stringify!($fhe_type),
$fhe_type::num_bits(),
$display_name,
stringify!($op),
|lhs, rhs| lhs.$op(rhs)
BinaryOp {
func: |lhs: &$fhe_type, rhs: &$fhe_right_type| lhs.$op(rhs),
_encrypt_lhs: PhantomData::<$left_type>,
_encrypt_rhs: PhantomData::<$right_type>,
_rhs_type: PhantomData::<$fhe_right_type>,
}
);
}
}
};
);
}
macro_rules! bench_type_binary_scalar_op {
(
type_name: $fhe_type:ident,
integer_type: $integer_type:ty,
scalar_type: $scalar_ty:ty,
display_name: $display_name:literal,
operation: $op:ident,
rng: $rng_fn:expr
) => {
::paste::paste! {
#[inline(never)]
fn [<bench_ $fhe_type:snake _scalar_ $op>](c: &mut Criterion, cks: &ClientKey) {
bench_fhe_type_op(
c,
cks,
stringify!($fhe_type),
$fhe_type::num_bits(),
$display_name,
stringify!($op),
ScalarBinaryOp {
func: |lhs: &$fhe_type, rhs: &$scalar_ty| lhs.$op(*rhs),
rng_function: $rng_fn,
_encrypt: PhantomData::<$integer_type>,
}
);
}
}
};
}
macro_rules! bench_type_unary_op {
(type_name: $fhe_type:ident, integer_type: $integer_type:ty, display_name: $display_name:literal, operation: $op:ident) => {
::paste::paste! {
#[inline(never)]
fn [<bench_ $fhe_type:snake _ $op>](c: &mut Criterion, cks: &ClientKey) {
bench_fhe_type_op(
c,
cks,
stringify!($fhe_type),
$fhe_type::num_bits(),
$display_name,
stringify!($op),
UnaryOp {
func: |lhs: &$fhe_type| lhs.$op(),
_encrypt: PhantomData::<$integer_type>
}
);
}
}
};
}
macro_rules! bench_type_ternary_op {
(type_name: $fhe_type:ident, integer_type: $integer_type:ty, display_name: $display_name:literal, operation: $op:ident) => {
::paste::paste! {
#[inline(never)]
fn [<bench_ $fhe_type:snake _ $op>](c: &mut Criterion, cks: &ClientKey) {
bench_fhe_type_op(
c,
cks,
stringify!($fhe_type),
$fhe_type::num_bits(),
$display_name,
stringify!($op),
TernaryOp {
func: |cond: &FheBool, lhs: &$fhe_type, rhs: &$fhe_type| cond.$op(lhs, rhs),
_encrypt: PhantomData::<$integer_type>
}
);
}
}
};
}
macro_rules! bench_type_array_op {
(type_name: $fhe_type:ident, integer_type: $integer_type:ty, display_name: $display_name:literal, operation: $op:ident) => {
::paste::paste! {
#[inline(never)]
fn [<bench_ $fhe_type:snake _ $op>](c: &mut Criterion, cks: &ClientKey) {
bench_fhe_type_op(
c,
cks,
stringify!($fhe_type),
$fhe_type::num_bits(),
$display_name,
stringify!($op),
ArrayOp {
func: |iter: std::iter::Cloned<std::slice::Iter<'_, $fhe_type>>| iter.$op(),
array_size: 10,
_encrypt: PhantomData::<$integer_type>,
}
);
}
}
};
}
macro_rules! generate_typed_benches {
($fhe_type:ident) => {
bench_type_op!(type_name: $fhe_type, display_name: "add", operation: add);
bench_type_op!(type_name: $fhe_type, display_name: "overflowing_add", operation: overflowing_add);
bench_type_op!(type_name: $fhe_type, display_name: "sub", operation: sub);
bench_type_op!(type_name: $fhe_type, display_name: "overflowing_sub", operation: overflowing_sub);
bench_type_op!(type_name: $fhe_type, display_name: "mul", operation: mul);
bench_type_op!(type_name: $fhe_type, display_name: "bitand", operation: bitand);
bench_type_op!(type_name: $fhe_type, display_name: "bitor", operation: bitor);
bench_type_op!(type_name: $fhe_type, display_name: "bitxor", operation: bitxor);
bench_type_op!(type_name: $fhe_type, display_name: "left_shift", operation: shl);
bench_type_op!(type_name: $fhe_type, display_name: "right_shift", operation: shr);
bench_type_op!(type_name: $fhe_type, display_name: "left_rotate", operation: rotate_left);
bench_type_op!(type_name: $fhe_type, display_name: "right_rotate", operation: rotate_right);
bench_type_op!(type_name: $fhe_type, display_name: "min", operation: min);
bench_type_op!(type_name: $fhe_type, display_name: "max", operation: max);
($fhe_type:ident, $integer_type:ty) => {
// bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "bitnot", operation: bitnot);
bench_type_array_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "sum", operation: sum);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "add", operation: add);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "bitand", operation: bitand);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "bitor", operation: bitor);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "bitxor", operation: bitxor);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "div", operation: div);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "div_rem", operation: div_rem);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "eq", operation: eq);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "ge", operation: ge);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "gt", operation: gt);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "le", operation: le);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: FheUint128, left_type: $integer_type, right_type: u128, display_name: "left_rotate", operation: rotate_left);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: FheUint128, left_type: $integer_type, right_type: u128, display_name: "left_shift", operation: shl);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "lt", operation: lt);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "max", operation: max);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "min", operation: min);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "mul", operation: mul);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "ne", operation: ne);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "overflowing_add", operation: overflowing_add);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "overflowing_mul", operation: overflowing_mul);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "overflowing_sub", operation: overflowing_sub);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "rem", operation: rem);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: FheUint128, left_type: $integer_type, right_type: u128, display_name: "right_rotate", operation: rotate_right);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: FheUint128, left_type: $integer_type, right_type: u128, display_name: "right_shift", operation: shr);
bench_type_binary_op!(type_name: $fhe_type, right_type_name: $fhe_type, left_type: $integer_type, right_type: $integer_type, display_name: "sub", operation: sub);
bench_type_ternary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "flip", operation: flip);
bench_type_ternary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "if_then_else", operation: if_then_else);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "checked_ilog2", operation: checked_ilog2);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "count_ones", operation: count_ones);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "count_zeros", operation: count_zeros);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "ilog2", operation: ilog2);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "is_even", operation: is_even);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "is_odd", operation: is_odd);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "leading_ones", operation: leading_ones);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "leading_zeros", operation: leading_zeros);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "neg", operation: neg);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "not", operation: not);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "overflowing_neg", operation: overflowing_neg);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "reverse_bits", operation: reverse_bits);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "trailing_ones", operation: trailing_ones);
bench_type_unary_op!(type_name: $fhe_type, integer_type: $integer_type, display_name: "trailing_zeros", operation: trailing_zeros);
};
}
macro_rules! generate_typed_scalar_benches {
($fhe_type:ident, $integer_type:ty, $scalar_ty:ty, $specific_ty:ty) => {
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "add_scalar", operation: add, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "bitand_scalar", operation: bitand, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "bitor_scalar", operation: bitor, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "bitxor_scalar", operation: bitxor, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "div_scalar", operation: div, rng: || random_non_zero::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "eq_scalar", operation: eq, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "ge_scalar", operation: ge, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "gt_scalar", operation: gt, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "le_scalar", operation: le, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "lt_scalar", operation: lt, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "max_scalar", operation: max, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "min_scalar", operation: min, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "mul_scalar", operation: mul, rng: || random_not_power_of_two::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "ne_scalar", operation: ne, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "overflowing_add_scalar", operation: overflowing_add, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "overflowing_sub_scalar", operation: overflowing_sub, rng: || random::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "rem_scalar", operation: rem, rng: || random_non_zero::<$scalar_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $specific_ty, display_name: "rotate_left_scalar", operation: rotate_left, rng: || random::<$specific_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $specific_ty, display_name: "rotate_right_scalar", operation: rotate_right, rng: || random::<$specific_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $specific_ty, display_name: "shift_left_scalar", operation: shl, rng: || get_one::<$specific_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $specific_ty, display_name: "shift_right_scalar", operation: shr, rng: || get_one::<$specific_ty>());
bench_type_binary_scalar_op!(type_name: $fhe_type, integer_type: $integer_type, scalar_type: $scalar_ty, display_name: "sub_scalar", operation: sub, rng: || random::<$scalar_ty>());
};
}
// Generate benches for all FheUint types
generate_typed_benches!(FheUint2);
generate_typed_benches!(FheUint4);
generate_typed_benches!(FheUint6);
generate_typed_benches!(FheUint8);
generate_typed_benches!(FheUint10);
generate_typed_benches!(FheUint12);
generate_typed_benches!(FheUint14);
generate_typed_benches!(FheUint16);
generate_typed_benches!(FheUint32);
generate_typed_benches!(FheUint64);
generate_typed_benches!(FheUint128);
generate_typed_benches!(FheUint2, u128);
generate_typed_benches!(FheUint4, u128);
generate_typed_benches!(FheUint6, u128);
generate_typed_benches!(FheUint8, u128);
generate_typed_benches!(FheUint10, u128);
generate_typed_benches!(FheUint12, u128);
generate_typed_benches!(FheUint14, u128);
generate_typed_benches!(FheUint16, u128);
generate_typed_benches!(FheUint32, u128);
generate_typed_benches!(FheUint64, u128);
generate_typed_benches!(FheUint128, u128);
generate_typed_benches!(FheInt2, i128);
generate_typed_benches!(FheInt4, i128);
generate_typed_benches!(FheInt6, i128);
generate_typed_benches!(FheInt8, i128);
generate_typed_benches!(FheInt10, i128);
generate_typed_benches!(FheInt12, i128);
generate_typed_benches!(FheInt14, i128);
generate_typed_benches!(FheInt16, i128);
generate_typed_benches!(FheInt32, i128);
generate_typed_benches!(FheInt64, i128);
generate_typed_benches!(FheInt128, i128);
generate_typed_scalar_benches!(FheUint2, u128, u8, u8);
generate_typed_scalar_benches!(FheUint4, u128, u8, u8);
generate_typed_scalar_benches!(FheUint6, u128, u8, u8);
generate_typed_scalar_benches!(FheUint8, u128, u8, u8);
generate_typed_scalar_benches!(FheUint10, u128, u16, u16);
generate_typed_scalar_benches!(FheUint12, u128, u16, u16);
generate_typed_scalar_benches!(FheUint14, u128, u16, u16);
generate_typed_scalar_benches!(FheUint16, u128, u16, u16);
generate_typed_scalar_benches!(FheUint32, u128, u32, u32);
generate_typed_scalar_benches!(FheUint64, u128, u64, u64);
generate_typed_scalar_benches!(FheUint128, u128, u128, u128);
generate_typed_scalar_benches!(FheInt2, i128, i8, u8);
generate_typed_scalar_benches!(FheInt4, i128, i8, u8);
generate_typed_scalar_benches!(FheInt6, i128, i8, u8);
generate_typed_scalar_benches!(FheInt8, i128, i8, u8);
generate_typed_scalar_benches!(FheInt10, i128, i16, u16);
generate_typed_scalar_benches!(FheInt12, i128, i16, u16);
generate_typed_scalar_benches!(FheInt14, i128, i16, u16);
generate_typed_scalar_benches!(FheInt16, i128, i16, u16);
generate_typed_scalar_benches!(FheInt32, i128, i32, u32);
generate_typed_scalar_benches!(FheInt64, i128, i64, u64);
generate_typed_scalar_benches!(FheInt128, i128, i128, u128);
macro_rules! run_benches {
($c:expr, $cks:expr, $($fhe_type:ident),+ $(,)?) => {
$(
::paste::paste! {
[<bench_ $fhe_type:snake _add>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_add>]($c, $cks);
[<bench_ $fhe_type:snake _sub>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_sub>]($c, $cks);
[<bench_ $fhe_type:snake _mul>]($c, $cks);
[<bench_ $fhe_type:snake _bitand>]($c, $cks);
[<bench_ $fhe_type:snake _bitor>]($c, $cks);
[<bench_ $fhe_type:snake _bitxor>]($c, $cks);
[<bench_ $fhe_type:snake _shl>]($c, $cks);
[<bench_ $fhe_type:snake _shr>]($c, $cks);
[<bench_ $fhe_type:snake _checked_ilog2>]($c, $cks);
[<bench_ $fhe_type:snake _count_ones>]($c, $cks);
[<bench_ $fhe_type:snake _count_zeros>]($c, $cks);
[<bench_ $fhe_type:snake _div>]($c, $cks);
[<bench_ $fhe_type:snake _div_rem>]($c, $cks);
[<bench_ $fhe_type:snake _eq>]($c, $cks);
[<bench_ $fhe_type:snake _flip>]($c, $cks);
[<bench_ $fhe_type:snake _ge>]($c, $cks);
[<bench_ $fhe_type:snake _gt>]($c, $cks);
[<bench_ $fhe_type:snake _if_then_else>]($c, $cks);
[<bench_ $fhe_type:snake _ilog2>]($c, $cks);
[<bench_ $fhe_type:snake _is_even>]($c, $cks);
[<bench_ $fhe_type:snake _is_odd>]($c, $cks);
[<bench_ $fhe_type:snake _le>]($c, $cks);
[<bench_ $fhe_type:snake _leading_ones>]($c, $cks);
[<bench_ $fhe_type:snake _leading_zeros>]($c, $cks);
[<bench_ $fhe_type:snake _lt>]($c, $cks);
[<bench_ $fhe_type:snake _max>]($c, $cks);
[<bench_ $fhe_type:snake _min>]($c, $cks);
[<bench_ $fhe_type:snake _mul>]($c, $cks);
[<bench_ $fhe_type:snake _ne>]($c, $cks);
[<bench_ $fhe_type:snake _neg>]($c, $cks);
[<bench_ $fhe_type:snake _not>]($c, $cks);
[<bench_ $fhe_type:snake _not>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_add>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_mul>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_neg>]($c, $cks);
[<bench_ $fhe_type:snake _overflowing_sub>]($c, $cks);
[<bench_ $fhe_type:snake _rem>]($c, $cks);
[<bench_ $fhe_type:snake _reverse_bits>]($c, $cks);
[<bench_ $fhe_type:snake _rotate_left>]($c, $cks);
[<bench_ $fhe_type:snake _rotate_right>]($c, $cks);
[<bench_ $fhe_type:snake _min>]($c, $cks);
[<bench_ $fhe_type:snake _max>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_add>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_bitand>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_bitor>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_bitxor>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_div>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_eq>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_ge>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_gt>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_le>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_lt>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_max>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_min>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_mul>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_ne>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_overflowing_add>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_overflowing_sub>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_rem>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_rotate_left>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_rotate_right>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_shl>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_shr>]($c, $cks);
[<bench_ $fhe_type:snake _scalar_sub>]($c, $cks);
[<bench_ $fhe_type:snake _shl>]($c, $cks);
[<bench_ $fhe_type:snake _shr>]($c, $cks);
[<bench_ $fhe_type:snake _sub>]($c, $cks);
[<bench_ $fhe_type:snake _sum>]($c, $cks);
[<bench_ $fhe_type:snake _trailing_ones>]($c, $cks);
[<bench_ $fhe_type:snake _trailing_zeros>]($c, $cks);
}
)+
};
@@ -180,6 +712,12 @@ impl TypeDisplay for u32 {}
impl TypeDisplay for u64 {}
impl TypeDisplay for u128 {}
impl TypeDisplay for i8 {}
impl TypeDisplay for i16 {}
impl TypeDisplay for i32 {}
impl TypeDisplay for i64 {}
impl TypeDisplay for i128 {}
impl<Id: FheUintId> TypeDisplay for tfhe::FheUint<Id> {
fn fmt(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write_fhe_type_name::<Self>(f)
@@ -385,7 +923,8 @@ fn main() {
// Call all benchmarks for all types
run_benches!(
&mut c, &cks, FheUint2, FheUint4, FheUint6, FheUint8, FheUint10, FheUint12,
FheUint14, FheUint16, FheUint32, FheUint64, FheUint128
FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheInt2, FheInt4, FheInt6,
FheInt8, FheInt10, FheInt12, FheInt14, FheInt16, FheInt32, FheInt64, FheInt128
);
// KVStore Benches

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 181 KiB

View File

@@ -19,11 +19,13 @@ The overall process to write an homomorphic program is the same for all types. T
This library has different modules, with different levels of abstraction.
There is the **core\_crypto** module, which is the lowest level API with the primitive functions and types of the TFHE scheme.
There is the [core\_crypto](../core-crypto-api/presentation.md) module, which is the lowest level API with the primitive functions and types of the TFHE scheme.
Above the core\_crypto module, there are the **Boolean**, **shortint**, and **integer** modules, which contain easy to use APIs enabling evaluation of Boolean, short integer, and integer circuits.
Above the core\_crypto module, there are the [Boolean](boolean/README.md), [shortint](shortint/README.md), and [integer](integer/README.md) modules, which contain easy to use APIs enabling evaluation of Boolean, short integer, and integer circuits.
Finally, there is the high-level module built on top of the Boolean, shortint, integer modules. This module is meant to abstract cryptographic complexities: no cryptographical knowledge is required to start developing an FHE application. Another benefit of the high-level module is the drastically simplified development process compared to lower level modules.
Finally, there is the high-level module built on top of the shortint and integer modules. This module is meant to abstract cryptographic complexities: no cryptographical knowledge is required to start developing an FHE application. Another benefit of the high-level module is the drastically simplified development process compared to lower level modules.
![API levels diagram](../../.gitbook/assets/api-levels.svg)
#### high-level API

View File

@@ -19,6 +19,7 @@ use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext;
use crate::integer::prelude::*;
use crate::integer::BooleanBlock;
use crate::named::Named;
use crate::prelude::FheWait;
use crate::shortint::ciphertext::NotTrivialCiphertextError;
use crate::shortint::parameters::CiphertextConformanceParams;
use crate::shortint::AtomicPatternParameters;
@@ -73,6 +74,12 @@ impl Named for FheBool {
const NAME: &'static str = "high_level_api::FheBool";
}
impl FheWait for FheBool {
fn wait(&self) {
self.ciphertext.wait()
}
}
#[derive(Copy, Clone)]
pub struct FheBoolConformanceParams(pub(crate) CiphertextConformanceParams);

View File

@@ -139,6 +139,16 @@ impl InnerBoolean {
}
}
pub(crate) fn wait(&self) {
match self {
Self::Cpu(_) => {}
#[cfg(feature = "gpu")]
Self::Cuda(_) => {}
#[cfg(feature = "hpu")]
Self::Hpu(ct) => ct.wait(),
}
}
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
pub(crate) fn on_cpu(&self) -> MaybeCloned<'_, BooleanBlock> {

View File

@@ -10,7 +10,7 @@ use crate::high_level_api::global_state;
use crate::high_level_api::integers::{FheIntegerType, FheUint, FheUintId, IntegerId};
use crate::high_level_api::keys::{CompactPublicKey, InternalServerKey};
use crate::high_level_api::re_randomization::ReRandomizationMetadata;
use crate::high_level_api::traits::{ReRandomize, Tagged};
use crate::high_level_api::traits::{FheWait, ReRandomize, Tagged};
use crate::integer::block_decomposition::{DecomposableInto, RecomposableSignedInteger};
use crate::integer::ciphertext::ReRandomizationSeed;
use crate::integer::parameters::RadixCiphertextConformanceParams;
@@ -1161,6 +1161,15 @@ where
}
}
impl<Id> FheWait for FheInt<Id>
where
Id: FheIntId,
{
fn wait(&self) {
self.ciphertext.wait()
}
}
impl<Id> ReRandomize for FheInt<Id>
where
Id: FheIntId,

View File

@@ -120,6 +120,14 @@ impl SignedRadixCiphertext {
}
}
pub(crate) fn wait(&self) {
match self {
Self::Cpu(_) => {}
#[cfg(feature = "gpu")]
Self::Cuda(_) => {}
}
}
/// Returns the a ref to the inner cpu ciphertext if self is on the CPU, otherwise, returns a
/// copy that is on the CPU
pub(crate) fn on_cpu(&self) -> MaybeCloned<'_, crate::integer::SignedRadixCiphertext> {

View File

@@ -24,6 +24,96 @@ use std::ops::{
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
impl<Id> std::iter::Sum<Self> for FheInt<Id>
where
Id: FheIntId,
{
/// Sums multiple ciphertexts together.
///
/// This is much more efficient than manually calling the `+` operator, thus
/// using sum should always be preferred.
///
/// # Example
///
/// ```rust
/// use tfhe::prelude::*;
/// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt16};
///
/// let (client_key, server_key) = generate_keys(ConfigBuilder::default());
/// set_server_key(server_key);
///
/// let clears = [-1i16, 2, 3, 4, -5];
/// let encrypted = clears
/// .iter()
/// .copied()
/// .map(|x| FheInt16::encrypt(x, &client_key))
/// .collect::<Vec<_>>();
///
/// // Iter and sum on references
/// let result = encrypted.iter().sum::<FheInt16>();
///
/// let decrypted: i16 = result.decrypt(&client_key);
/// assert_eq!(decrypted, clears.into_iter().sum::<i16>());
/// ```
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
global_state::with_internal_keys(|key| match key {
InternalServerKey::Cpu(cpu_key) => {
let ciphertexts = iter
.map(|elem| elem.ciphertext.on_cpu().to_owned())
.collect::<Vec<_>>();
cpu_key
.pbs_key()
.sum_ciphertexts_parallelized(ciphertexts.iter())
.map_or_else(
|| {
let radix: crate::integer::SignedRadixCiphertext =
cpu_key.pbs_key().create_trivial_zero_radix(Id::num_blocks(
cpu_key.message_modulus(),
));
Self::new(
radix,
cpu_key.tag.clone(),
ReRandomizationMetadata::default(),
)
},
|ct| Self::new(ct, cpu_key.tag.clone(), ReRandomizationMetadata::default()),
)
}
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(cuda_key) => {
let streams = &cuda_key.streams;
let cts = iter
.map(|fhe_uint| match fhe_uint.ciphertext.on_gpu(streams) {
MaybeCloned::Borrowed(gpu_ct) => gpu_ct.duplicate(streams),
MaybeCloned::Cloned(gpu_ct) => gpu_ct,
})
.collect::<Vec<_>>();
let inner = cuda_key
.key
.key
.sum_ciphertexts(cts, streams)
.unwrap_or_else(|| {
cuda_key.key.key.create_trivial_radix(
0,
Id::num_blocks(cuda_key.message_modulus()),
streams,
)
});
Self::new(
inner,
cuda_key.tag.clone(),
ReRandomizationMetadata::default(),
)
}
#[cfg(feature = "hpu")]
InternalServerKey::Hpu(_device) => {
panic!("Hpu does not support this operation yet.")
}
})
}
}
impl<'a, Id> std::iter::Sum<&'a Self> for FheInt<Id>
where
Id: FheIntId,