Documentation Index
Fetch the complete documentation index at: https://docs.privora.xyz/llms.txt
Use this file to discover all available pages before exploring further.
FHE operations are computationally expensive. This guide covers optimization strategies.
Operation Costs
| Operation | Relative Cost |
|---|
| Addition | 1x |
| Subtraction | 1x |
| Multiplication | 2-3x |
| Comparison | 5-10x |
| Min/Max | 5-10x |
| Select | 5-10x |
Key Strategies
1. Minimize Comparisons
Comparisons are expensive. Batch when possible:
// Bad: Multiple comparisons
let a_gt_b = a.gt(&b)?;
let b_gt_c = b.gt(&c)?;
let c_gt_d = c.gt(&d)?;
// Better: Rethink the algorithm to minimize comparisons
2. Use Smallest Types
// Good: u8 for small values
let price: Encrypted<u8> = price_ref.load()?; // ~10KB ciphertext
// Avoid: u64 when not needed
let price: Encrypted<u64> = price_ref.load()?; // ~80KB ciphertext
3. Load Once, Use Multiple Times
// Good
let price = price_ref.load()?;
let result1 = price.add(&a)?;
let result2 = price.add(&b)?;
// Bad
let result1 = price_ref.load()?.add(&a)?;
let result2 = price_ref.load()?.add(&b)?; // Extra load!
4. Batch Arithmetic Before Comparisons
// Good: All arithmetic first
let sum1 = a.add(&b)?;
let sum2 = c.add(&d)?;
let comparison = sum1.ge(&sum2)?;
// Avoid: Interleaving
let comparison1 = a.ge(&b)?;
let sum = c.add(&d)?;
let comparison2 = sum.ge(&e)?;
5. Use Built-in Min/Max
// Good
let min = a.min(&b)?;
// Bad
let is_smaller = a.lt(&b)?;
let min = is_smaller.select(&a, &b)?;
6. Store Results at End
// Good: Single store
let result = a.add(&b)?.mul(&c)?;
let result_ref = result.store()?;
// Avoid: Multiple stores
let sum = a.add(&b)?;
let sum_ref = sum.store()?; // Unnecessary intermediate store
Memory Optimization
Account Design
// Good: Minimal on-chain storage
pub struct Order {
pub owner: Pubkey, // 32 bytes
pub price_ref: EncryptedRef<u8>, // 32 bytes (not 10KB!)
pub qty_ref: EncryptedRef<u8>, // 32 bytes
}
// Total: 96 bytes
// Bad: Storing raw ciphertexts
pub struct Order {
pub price_data: [u8; 10000], // 10KB on-chain!
}
Heap Usage
Monitor heap usage for complex programs:
// Default: ~1MB heap
privora_sdk_program::setup_fhe_allocator!();
// If needed: larger heap
privora_sdk_program::setup_fhe_allocator!(2 * 1024 * 1024);
Algorithm Optimization
Avoid Unnecessary Comparisons
// Instead of checking both conditions
let a_ge_b = a.ge(&b)?;
let a_lt_b = a.lt(&b)?; // Redundant!
// Use one comparison and derive
let a_ge_b = a.ge(&b)?;
// a_lt_b is the logical negation
Precompute When Possible
If values are known at instruction time, compute before encryption:
// If computing: encrypted_a + plaintext_constant
// Consider: encrypt(plaintext_a + plaintext_constant) on client
// Then send single encrypted value
Benchmarking
In tests, measure operation counts:
#[test]
fn benchmark_operations() {
let mut env = FheTestEnv::new();
let start = std::time::Instant::now();
// Perform operations...
println!("Time: {:?}", start.elapsed());
}
Summary
- Minimize comparisons - they’re 5-10x more expensive
- Use smallest types - smaller ciphertexts, faster operations
- Load once - avoid redundant fetches
- Batch arithmetic - do all arithmetic before comparisons
- Store at end - minimize data store writes