Refactoring if
/else
statements
if
/else
statements are a popular instrument of control flow.
However, they can be detrimental to circuit performance as the use of if
/else
statements results in an exponential growth of the constraint table.
The rate at which new constraints are generated is 2^N, where N is the number of if
/else
statements in a circuit.
The goal of avoiding if
/else
statements is to generate select
LLVM instructions that do not produce unnecessary IR branches. To learn more, click here.
To avoid complex and nested if
/else
statements, divide a circuit into several smaller functions where it would be possible to use the logical AND operator.
- C++
- Rust
bool is_same(
typename hashes::sha2<256>::block_type block0,
typename hashes::sha2<256>::block_type block1) {
return block0[0] == block1[0] && block0[1] == block1[1];
}
use ark_pallas::Fq;
type BlockType = [Fq; 2];
fn is_same(first_input_block: BlockType, second_input_block: BlockType) -> bool {
let res = first_input_block[0] == second_input_block[0]
& first_input_block[1] == second_input_block[1];
res
}
These functions can then be used with ternary operators, match
statements and expressions, or similar tools replacing the if
/else
flow.
- C++
- Rust
[[circuit]] typename typename hashes::sha2<256>::block_type circuit_func(
typename hashes::sha2<256>::block_type block0,
typename hashes::sha2<256>::block_type block1) {
typename typename hashes::sha2<256>::block_type res = (is_same(a, b)) ? block0 : block1;
return res;
}
#[circuit]
pub fn circuit_func(first_input_block: BlockType, second_input_block: BlockType) -> BlockType {
let res: BlockType = match is_same(first_input_block, second_input_block) {
true => first_input_block,
_ => second_input_block
}
res
}