poulpy/poulpy-schemes/benches/bdd_arithmetic.rs
poulpy-schemes/benches/bdd_arithmetic.rsRaw
1use std::hint::black_box;
2
3use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
4use poulpy_core::{
5 GGSWNoise, GLWEDecrypt, GLWEEncryptSk, GLWEExternalProduct, LWEEncryptSk, ScratchTakeCore,
6 layouts::{
7 Base2K, Degree, Dnum, Dsize, GGLWEToGGSWKeyLayout, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout,
8 GLWELayout, GLWESecret, GLWESecretPrepared, GLWESecretPreparedFactory, GLWESwitchingKeyLayout, GLWEToLWEKeyLayout,
9 LWESecret, Rank, TorusPrecision,
10 },
11};
12
13#[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
14pub use poulpy_cpu_avx::FFT64Avx as BackendImpl;
15
16#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
17pub use poulpy_cpu_ref::FFT64Ref as BackendImpl;
18
19use poulpy_hal::{
20 api::{ModuleN, ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxRotateInplace},
21 layouts::{Backend, Module, Scratch, ScratchOwned},
22 source::Source,
23};
24use poulpy_schemes::bin_fhe::{
25 bdd_arithmetic::{
26 Add, And, BDDKey, BDDKeyEncryptSk, BDDKeyLayout, BDDKeyPrepared, BDDKeyPreparedFactory, ExecuteBDDCircuit2WTo1W, FheUint,
27 FheUintPrepare, FheUintPrepared, Or, Sll, Slt, Sltu, Sra, Srl, Sub, Xor,
28 },
29 blind_rotation::{
30 BlindRotationAlgo, BlindRotationKey, BlindRotationKeyFactory, BlindRotationKeyInfos, BlindRotationKeyLayout, CGGI,
31 },
32 circuit_bootstrapping::{
33 CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyLayout,
34 CircuitBootstrappingKeyPrepared, CircuitBootstrappingKeyPreparedFactory, CirtuitBootstrappingExecute,
35 },
36};
37
38// Common setup data structure
39struct BenchmarkSetup<BE: Backend, BRA: BlindRotationAlgo> {
40 module: Module<BE>,
41 scratch: ScratchOwned<BE>,
42 a_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE>,
43 b_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE>,
44 bdd_key_prepared: BDDKeyPrepared<Vec<u8>, BRA, BE>,
45 glwe_layout: GLWELayout,
46}
47
48struct Params {
49 name: String,
50 block_size: usize,
51 glwe_layout: GLWELayout,
52 ggsw_layout: GGSWLayout,
53 bdd_layout: BDDKeyLayout,
54}
55
56fn setup_benchmark<BE: Backend, BRA: BlindRotationAlgo>(params: &Params) -> BenchmarkSetup<BE, BRA>
57where
58 Module<BE>: ModuleNew<BE>
59 + ModuleN
60 + GLWESecretPreparedFactory<BE>
61 + GLWEExternalProduct<BE>
62 + GLWEDecrypt<BE>
63 + LWEEncryptSk<BE>
64 + CircuitBootstrappingKeyEncryptSk<BRA, BE>
65 + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
66 + CirtuitBootstrappingExecute<BRA, BE>
67 + GGSWPreparedFactory<BE>
68 + GGSWNoise<BE>
69 + GLWEEncryptSk<BE>
70 + VecZnxRotateInplace<BE>
71 + BDDKeyEncryptSk<BRA, BE>
72 + BDDKeyPreparedFactory<BRA, BE>
73 + FheUintPrepare<BRA, BE>
74 + ExecuteBDDCircuit2WTo1W<BE>,
75 BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>,
76 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
77 Scratch<BE>: ScratchTakeCore<BE>,
78{
79 // Scratch space (16MB)
80 let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 24);
81
82 let n_glwe: poulpy_core::layouts::Degree = params.bdd_layout.cbt_layout.brk_layout.n_glwe();
83 let n_lwe: poulpy_core::layouts::Degree = params.bdd_layout.cbt_layout.brk_layout.n_lwe();
84 let rank: poulpy_core::layouts::Rank = params.bdd_layout.cbt_layout.brk_layout.rank;
85
86 let module: Module<BE> = Module::<BE>::new(n_glwe.as_u32() as u64);
87
88 let mut source_xs: Source = Source::new([1u8; 32]);
89 let mut source_xa: Source = Source::new([1u8; 32]);
90 let mut source_xe: Source = Source::new([1u8; 32]);
91
92 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
93 sk_lwe.fill_binary_block(params.block_size, &mut source_xs);
94
95 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe, rank);
96 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
97
98 // Circuit bootstrapping evaluation key
99 let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> =
100 CircuitBootstrappingKey::alloc_from_infos(¶ms.bdd_layout.cbt_layout);
101 cbt_key.encrypt_sk(
102 &module,
103 &sk_lwe,
104 &sk_glwe,
105 &mut source_xa,
106 &mut source_xe,
107 scratch.borrow(),
108 );
109
110 let mut cbt_key_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, BE> =
111 CircuitBootstrappingKeyPrepared::alloc_from_infos(&module, ¶ms.bdd_layout.cbt_layout);
112 cbt_key_prepared.prepare(&module, &cbt_key, scratch.borrow());
113
114 let mut sk_glwe_prepared = GLWESecretPrepared::alloc_from_infos(&module, ¶ms.glwe_layout);
115 sk_glwe_prepared.prepare(&module, &sk_glwe);
116
117 let mut bdd_key: BDDKey<Vec<u8>, BRA> = BDDKey::alloc_from_infos(¶ms.bdd_layout);
118 bdd_key.encrypt_sk(
119 &module,
120 &sk_lwe,
121 &sk_glwe,
122 &mut source_xa,
123 &mut source_xe,
124 scratch.borrow(),
125 );
126
127 let input_a = 255_u32;
128 let input_b = 30_u32;
129
130 let mut a_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(¶ms.glwe_layout);
131 a_enc.encrypt_sk(
132 &module,
133 input_a,
134 &sk_glwe_prepared,
135 &mut source_xa,
136 &mut source_xe,
137 scratch.borrow(),
138 );
139
140 let mut b_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(¶ms.glwe_layout);
141 b_enc.encrypt_sk(
142 &module,
143 input_b,
144 &sk_glwe_prepared,
145 &mut source_xa,
146 &mut source_xe,
147 scratch.borrow(),
148 );
149
150 //////// Homomorphic computation starts here ////////
151
152 // Preparing the BDD Key
153 // The BDD key must be prepared once before any operation is performed
154 let mut bdd_key_prepared: BDDKeyPrepared<Vec<u8>, BRA, BE> = BDDKeyPrepared::alloc_from_infos(&module, ¶ms.bdd_layout);
155 bdd_key_prepared.prepare(&module, &bdd_key, scratch.borrow());
156
157 // Input Preparation
158 // Before each operation, the inputs to that operation must be prepared
159 // Preparation extracts each bit of the integer into a seperate GLWE ciphertext and bootstraps it into a GGSW ciphertext
160 let mut a_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, ¶ms.ggsw_layout);
161 a_enc_prepared.prepare(&module, &a_enc, &bdd_key_prepared, scratch.borrow());
162
163 let mut b_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, ¶ms.ggsw_layout);
164 b_enc_prepared.prepare(&module, &b_enc, &bdd_key_prepared, scratch.borrow());
165
166 BenchmarkSetup {
167 module,
168 scratch,
169 a_enc_prepared,
170 b_enc_prepared,
171 bdd_key_prepared,
172 glwe_layout: params.glwe_layout.clone(),
173 }
174}
175
176fn create_runner<BE: Backend, BRA: BlindRotationAlgo, F>(setup: BenchmarkSetup<BE, BRA>, operation: F) -> impl FnMut()
177where
178 Module<BE>: ExecuteBDDCircuit2WTo1W<BE>,
179 Scratch<BE>: ScratchTakeCore<BE>,
180 ScratchOwned<BE>: ScratchOwnedBorrow<BE>,
181 F: Fn(
182 &mut FheUint<Vec<u8>, u32>,
183 &Module<BE>,
184 &FheUintPrepared<Vec<u8>, u32, BE>,
185 &FheUintPrepared<Vec<u8>, u32, BE>,
186 &BDDKeyPrepared<Vec<u8>, BRA, BE>,
187 &mut Scratch<BE>,
188 ),
189{
190 let BenchmarkSetup {
191 module,
192 mut scratch,
193 a_enc_prepared,
194 b_enc_prepared,
195 bdd_key_prepared,
196 glwe_layout,
197 } = setup;
198
199 let mut c_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
200
201 move || {
202 operation(
203 &mut c_enc,
204 &module,
205 &a_enc_prepared,
206 &b_enc_prepared,
207 &bdd_key_prepared,
208 scratch.borrow(),
209 );
210 black_box(());
211 }
212}
213
214fn bench_operation<BE: Backend, BRA: BlindRotationAlgo, F>(
215 group: &mut criterion::BenchmarkGroup<'_, criterion::measurement::WallTime>,
216 params: &Params,
217 operation_name: &str,
218 operation: F,
219) where
220 Module<BE>: ModuleNew<BE>
221 + ModuleN
222 + GLWESecretPreparedFactory<BE>
223 + GLWEExternalProduct<BE>
224 + GLWEDecrypt<BE>
225 + LWEEncryptSk<BE>
226 + CircuitBootstrappingKeyEncryptSk<BRA, BE>
227 + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
228 + CirtuitBootstrappingExecute<BRA, BE>
229 + GGSWPreparedFactory<BE>
230 + GGSWNoise<BE>
231 + GLWEEncryptSk<BE>
232 + VecZnxRotateInplace<BE>
233 + BDDKeyEncryptSk<BRA, BE>
234 + BDDKeyPreparedFactory<BRA, BE>
235 + FheUintPrepare<BRA, BE>
236 + ExecuteBDDCircuit2WTo1W<BE>,
237 BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>,
238 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
239 Scratch<BE>: ScratchTakeCore<BE>,
240 F: Fn(
241 &mut FheUint<Vec<u8>, u32>,
242 &Module<BE>,
243 &FheUintPrepared<Vec<u8>, u32, BE>,
244 &FheUintPrepared<Vec<u8>, u32, BE>,
245 &BDDKeyPrepared<Vec<u8>, BRA, BE>,
246 &mut Scratch<BE>,
247 ) + 'static,
248{
249 let setup = setup_benchmark::<BE, BRA>(params);
250 let mut runner = create_runner(setup, operation);
251 let id = BenchmarkId::from_parameter(format!("{}_{}", params.name, operation_name));
252 group.bench_with_input(id, &(), |b, _| b.iter(&mut runner));
253}
254
255pub fn benc_bdd_arithmetic<BE: Backend, BRA: BlindRotationAlgo>(c: &mut Criterion, label: &str)
256where
257 Module<BE>: ModuleNew<BE>
258 + ModuleN
259 + GLWESecretPreparedFactory<BE>
260 + GLWEExternalProduct<BE>
261 + GLWEDecrypt<BE>
262 + LWEEncryptSk<BE>
263 + CircuitBootstrappingKeyEncryptSk<BRA, BE>
264 + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
265 + CirtuitBootstrappingExecute<BRA, BE>
266 + GGSWPreparedFactory<BE>
267 + GGSWNoise<BE>
268 + GLWEEncryptSk<BE>
269 + VecZnxRotateInplace<BE>
270 + BDDKeyEncryptSk<BRA, BE>
271 + BDDKeyPreparedFactory<BRA, BE>
272 + FheUintPrepare<BRA, BE>
273 + ExecuteBDDCircuit2WTo1W<BE>,
274 BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
275 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
276 Scratch<BE>: ScratchTakeCore<BE>,
277{
278 let group_name: String = format!("bdd_arithmetic::{label}");
279
280 let mut group = c.benchmark_group(group_name);
281
282 const N_GLWE: u32 = 1024;
283 const N_LWE: u32 = 679;
284 const BINARY_BLOCK_SIZE: u32 = 7;
285 const BASE2K: u32 = 15;
286 const RANK: u32 = 2;
287
288 let params: Params = Params {
289 name: String::from(format!("n_glwe={N_GLWE}")),
290 block_size: BINARY_BLOCK_SIZE as usize,
291 glwe_layout: GLWELayout {
292 n: Degree(N_GLWE),
293 base2k: Base2K(BASE2K),
294 k: TorusPrecision(2 * BASE2K),
295 rank: Rank(RANK),
296 },
297 ggsw_layout: GGSWLayout {
298 n: Degree(N_GLWE),
299 base2k: Base2K(BASE2K),
300 k: TorusPrecision(3 * BASE2K),
301 rank: Rank(RANK),
302 dnum: Dnum(3),
303 dsize: Dsize(1),
304 },
305 bdd_layout: BDDKeyLayout {
306 cbt_layout: CircuitBootstrappingKeyLayout {
307 brk_layout: BlindRotationKeyLayout {
308 n_glwe: Degree(N_GLWE),
309 n_lwe: Degree(N_LWE),
310 base2k: Base2K(BASE2K),
311 k: TorusPrecision(4 * BASE2K),
312 dnum: Dnum(4),
313 rank: Rank(RANK),
314 },
315 atk_layout: GLWEAutomorphismKeyLayout {
316 n: Degree(N_GLWE),
317 base2k: Base2K(BASE2K),
318 k: TorusPrecision(4 * BASE2K),
319 dnum: Dnum(4),
320 dsize: Dsize(1),
321 rank: Rank(RANK),
322 },
323 tsk_layout: GGLWEToGGSWKeyLayout {
324 n: Degree(N_GLWE),
325 base2k: Base2K(BASE2K),
326 k: TorusPrecision(4 * BASE2K),
327 dnum: Dnum(4),
328 dsize: Dsize(1),
329 rank: Rank(RANK),
330 },
331 },
332 ks_glwe_layout: Some(GLWESwitchingKeyLayout {
333 n: Degree(N_GLWE),
334 base2k: Base2K(BASE2K),
335 k: TorusPrecision(4 * BASE2K),
336 dnum: Dnum(4),
337 dsize: Dsize(1),
338 rank_in: Rank(RANK),
339 rank_out: Rank(1),
340 }),
341 ks_lwe_layout: GLWEToLWEKeyLayout {
342 n: Degree(N_GLWE),
343 base2k: Base2K(BASE2K),
344 k: TorusPrecision(4 * BASE2K),
345 rank_in: Rank(1),
346 dnum: Dnum(4),
347 },
348 },
349 };
350
351 // Benchmark each operation
352 bench_operation::<BE, BRA, _>(
353 &mut group,
354 ¶ms,
355 "add",
356 |c_enc, module, a, b, key, scratch| {
357 c_enc.add(module, a, b, key, scratch);
358 },
359 );
360
361 bench_operation::<BE, BRA, _>(
362 &mut group,
363 ¶ms,
364 "sub",
365 |c_enc, module, a, b, key, scratch| {
366 c_enc.sub(module, a, b, key, scratch);
367 },
368 );
369
370 bench_operation::<BE, BRA, _>(
371 &mut group,
372 ¶ms,
373 "sll",
374 |c_enc, module, a, b, key, scratch| {
375 c_enc.sll(module, a, b, key, scratch);
376 },
377 );
378
379 bench_operation::<BE, BRA, _>(
380 &mut group,
381 ¶ms,
382 "sra",
383 |c_enc, module, a, b, key, scratch| {
384 c_enc.sra(module, a, b, key, scratch);
385 },
386 );
387
388 bench_operation::<BE, BRA, _>(
389 &mut group,
390 ¶ms,
391 "srl",
392 |c_enc, module, a, b, key, scratch| {
393 c_enc.srl(module, a, b, key, scratch);
394 },
395 );
396
397 bench_operation::<BE, BRA, _>(
398 &mut group,
399 ¶ms,
400 "slt",
401 |c_enc, module, a, b, key, scratch| {
402 c_enc.slt(module, a, b, key, scratch);
403 },
404 );
405
406 bench_operation::<BE, BRA, _>(
407 &mut group,
408 ¶ms,
409 "sltu",
410 |c_enc, module, a, b, key, scratch| {
411 c_enc.sltu(module, a, b, key, scratch);
412 },
413 );
414
415 bench_operation::<BE, BRA, _>(
416 &mut group,
417 ¶ms,
418 "or",
419 |c_enc, module, a, b, key, scratch| {
420 c_enc.or(module, a, b, key, scratch);
421 },
422 );
423
424 bench_operation::<BE, BRA, _>(
425 &mut group,
426 ¶ms,
427 "and",
428 |c_enc, module, a, b, key, scratch| {
429 c_enc.and(module, a, b, key, scratch);
430 },
431 );
432
433 bench_operation::<BE, BRA, _>(
434 &mut group,
435 ¶ms,
436 "xor",
437 |c_enc, module, a, b, key, scratch| {
438 c_enc.xor(module, a, b, key, scratch);
439 },
440 );
441
442 group.finish();
443}
444
445fn bench_bdd_arithmetic_fft64(c: &mut Criterion) {
446 #[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
447 let label = "fft64_avx";
448 #[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
449 let label = "fft64_ref";
450 benc_bdd_arithmetic::<BackendImpl, CGGI>(c, label);
451}
452
453criterion_group!(benches, bench_bdd_arithmetic_fft64);
454criterion_main!(benches);