Skip to content

Commit

Permalink
Document gate savings around if, loop, and no_predicates
Browse files Browse the repository at this point in the history
  • Loading branch information
jzaki committed Dec 13, 2024
1 parent b88db67 commit e9cf10a
Showing 1 changed file with 23 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docs/docs/explainers/explainer-writing-noir.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ Use arrays and indices that are known at compile time where possible.
Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time.
:::

### Reduce what is inside loops and conditional logic

Putting less logic inside an `if` (`else`, etc) paths, or inside a loop, translates to less gates required to represent the program.

A loop duplicates the gates for each iteration of the loop, or put another way, "unwraps" or "flattens" the loop. Any calculations/calls that are unchanged in the loop should be calculated once before, and the result used in the loop.

An `if` will create gates representing each path even though execution will use only one. Furthermore, there are additional operations required for each path. Sometimes this can have a multiplying effect on the operations in the `if` and `else` etc.

:::tip
Only have essential computation inside conditional logic and loops, and calculate anything else once (before, or after, depending).
:::

### Leverage unconstrained execution

Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates.
Expand All @@ -153,6 +165,17 @@ Use ` if is_unconstrained() { /`, to conditionally execute code if being called

Unless you're well into the depth of gate optimization, this advanced section can be ignored.

### `#[no_predicates]` over for "pure" functions

When conditional logic is compiled into gates, it is predicated with boolean expressions like X, where `X * V1() + (1-X) * V2()` is calculated. This returns `V1()` when `X` is true or `V2()` if `X` is false.

If a function is guaranteed to never fail an assertion no matter the inputs, or roughly considered "pure", it can use the macro `#[no_predicates]`, telling the compiler to not add such predicates. This can beneficially be used in say hashing functions to reduce the gate count when they intuitively must be written/used inside an `if`.

:::note
If a function may call other predicated functions, do not mark it with `#[no_predicates]`.
:::
Having a `#[no_predicates]` function call a regular function severely breaks the intended representation, proof generation, and verification of the program.

### Combine arithmetic operations

A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly.
Expand Down

0 comments on commit e9cf10a

Please sign in to comment.