Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switching from pedersen to poseidon causes a bytecode and witness solving blowup #4688

Closed
sirasistant opened this issue Apr 2, 2024 · 5 comments · Fixed by #5252
Closed
Assignees
Labels
bug Something isn't working

Comments

@sirasistant
Copy link
Contributor

Aim

Aztec is trying to switch from pedersen to poseidon to reduce the constraints used in hashing.

Expected Behavior

We expected witness solving time and bytecode size for ACIR functions (private) to be in the same ballpark. I did expect some issues in brillig (unconstrained) due to #4535 but this problem appears to happen in ACIR, not brillig.

Bug

Switching our account contracts from pedersen to poseidon makes witness solving time to go from < 400ms to 7 seconds.
Account contract entrypoints can dispatch to up to 6 functions that can be public or private. Every dispatch requires hashing a call stack item, that is roughly 250 items for private and 200 items for public (altough some of the items for public are known to be zero at compile time).

Previously, these account functions had in the ballpark of 10k opcodes, and after the switch to poseidon we are seeing 400k+ ACIR opcodes. The flamegraphs for a test run of the sandbox before and after the switch to pedersen can be seen here:
AztecProtocol/aztec-packages#5523 (comment)

Here is a regular noir function equivalent of an account contract entrypoint:
https://github.com/sirasistant/noir-playground/blob/main/src/main.nr#L16
The entrypoint in the repo has been simplified to only dispatch to up to 2 functions instead of 6.

To Reproduce

  1. Clone the repository
  2. Use nargo 0.26.0+a6016b46abf6da6de4566cf6d35a675d805dd9b5
  3. Play with the Hash trait implementation, switching to/from poseidon and pedersen in PrivateCallStackItem, PublicCallStackItem and PrivateCircuitPublicInputs
  4. See the resulting ACIR go from to 1.5k witnesses to 70k

Project Impact

Blocker

Impact Context

This blocks the switch to Poseidon, since the witness solving blowup makes the sandbox unusable.

Workaround

None

Workaround Description

No response

Additional Context

No response

Installation Method

None

Nargo Version

0.26.0+a6016b46abf6da6de4566cf6d35a675d805dd9b5

NoirJS Version

No response

Would you like to submit a PR for this Issue?

None

Support Needs

No response

@sirasistant sirasistant added the bug Something isn't working label Apr 2, 2024
@sirasistant
Copy link
Contributor Author

sirasistant commented Apr 2, 2024

I think this is the same issue:

use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2};

global NUM_HASHES = 4;
global HASH_LENGTH = 20;

pub fn pedersen_hash<N>(inputs: [Field; N], hash_index: u32) -> Field {
    dep::std::hash::pedersen_hash_with_separator(inputs, hash_index)
}

pub fn poseidon_hash<N>(inputs: [Field; N]) -> Field {
    Poseidon2::hash(inputs, inputs.len())
}

fn main(
    to_hash: [[Field; HASH_LENGTH]; NUM_HASHES],
    enable: [bool; NUM_HASHES]
) -> pub [Field; NUM_HASHES] {
    let mut result = [0; NUM_HASHES];
    for i in 0..NUM_HASHES {
        let enable = enable[i];
        let to_hash = to_hash[i];
        if enable {
            // result[i] = pedersen_hash(to_hash, 0);
            result[i] = poseidon_hash(to_hash);
        }
    }
    result
}

(run nargo info and play with poseidon/pedersen and removing the if branch)
Conditionals wrapping poseidon hashes make it less efficient that conditionally wrapped pedersens. @TomAFrench pointed out that it's due to how predicates are being applied to the inactive branch

github-merge-queue bot pushed a commit that referenced this issue Apr 2, 2024
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## Summary\*

This PR adds some improvements for SSA generation for programs such as
in #4688 where we now make more use of information on casted values.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

---------

Co-authored-by: jfecher <jake@aztecprotocol.com>
TomAFrench added a commit that referenced this issue Apr 3, 2024
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## Summary\*

This PR adds some improvements for SSA generation for programs such as
in #4688 where we now make more use of information on casted values.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

---------

Co-authored-by: jfecher <jake@aztecprotocol.com>
@jfecher
Copy link
Contributor

jfecher commented Apr 5, 2024

Looks like #4716 only reduces this from a circuit of size 74602 to 70650 (-4k)

@n4ru
Copy link
Contributor

n4ru commented Apr 12, 2024

Even without if statements, I'm still getting a 4x constraint blowup vs pedersen.

github-merge-queue bot pushed a commit that referenced this issue Apr 18, 2024
# Description

## Problem\*

Resolves #4826. 

## Summary\*

When running `nargo info` with the following code:
```rust
use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2};

global NUM_HASHES = 2;
global HASH_LENGTH = 10;

#[fold]
pub fn poseidon_hash<N>(inputs: [Field; N]) -> Field {
    Poseidon2::hash(inputs, inputs.len())
}
fn main(
    to_hash: [[Field; HASH_LENGTH]; NUM_HASHES],
    enable: [bool; NUM_HASHES]
) -> pub [Field; NUM_HASHES] {
    let mut result = [0; NUM_HASHES];
    for i in 0..NUM_HASHES {
        let enable = enable[i];
        let to_hash = to_hash[i];
        if enable {
            result[i] = poseidon_hash(to_hash);
        }
    }
    result
}
```
<img width="739" alt="Screenshot 2024-04-17 at 3 46 54 PM"
src="https://github.com/noir-lang/noir/assets/43554004/fa31977d-96e9-45f7-8001-43865bc4a83c">

When using `pedersen_hash` we also get 10 opcodes for `main`. 

Once we get more granularity in our inline types (fold vs. inline for
proving) we can most likely fully resolve this issue
(#4688).

## Additional Context



## Documentation\*

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
github-merge-queue bot pushed a commit that referenced this issue Apr 30, 2024
# Description

## Problem\*

Resolves #4911 and
#4688

## Summary\*

~~We recently included the `#[inline(never)]` attribute to enable
developers to optimize codegen.~~ This has now been switched to the name
`no_predicates`. The main use-case in mind is for circuits in issue
#4688 where inlining a function with heavy array operations when
dependent upon witnesses is not always ideal. Specifically when the
function being inlined does not need to rely on the predicate for
correctness.

Originally I had in mind to delay inlining all the way to after ACIR gen
and inline the ACIR artifacts. However, this feels overly complex now as
we have all the infrastructure to inline functions as we wish during
SSA, we could just need to delay the inlining of certain functions to
happen after flattening. This PR does exactly what was just mentioned.

For example, the new test `no_predicates_numeric_generic_poseidon` gave
these results when `poseidon_hash` was not marked with
`#[no_predicates_numeric_generic_poseidon]`:
<img width="785" alt="ExistingInlineStrategy"
src="https://github.com/noir-lang/noir/assets/43554004/f2fc1358-c86c-4f02-999e-414056b87a01">

While when `poseidon_hash` was marked with
`#[no_predicates_numeric_generic_poseidon]`:
<img width="788" alt="InlineNeverBench"
src="https://github.com/noir-lang/noir/assets/43554004/21d729f9-32db-4a32-b592-56f76bf5663d">


## Additional Context



## Documentation\*

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [X] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
@TomAFrench
Copy link
Member

@guipublic could you take a look at this? We added the #[no_predicates] attribute in order to address this but Alvaro has reported that it's still an issue to have conditional poseidon calls. We should do some testing to nail down how the performance of this changes based on how it's called.

@guipublic
Copy link
Contributor

I can confirm that 'no_predicates' solves the issue highlighted in the last snippet from the comments.

github-merge-queue bot pushed a commit that referenced this issue Jun 14, 2024
# Description

## Problem\*

Resolves #4688 

## Summary\*
Adding the no_predicates attribute to poseidon2 because hashing
operations have no side-effect.


## Additional Context



## Documentation\*

Check one:
- [X] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
github-merge-queue bot pushed a commit that referenced this issue Jun 17, 2024
# Description

## Problem\*

Related to #4688 

## Summary\*
Adding the no-predicate attribute to the hash implementations of the
stdlib


## Additional Context



## Documentation\*

Check one:
- [X] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

5 participants