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

[WIP] Restructure the memory pipeline #118

Closed
wants to merge 4 commits into from

Conversation

winston-h-zhang
Copy link
Member

@winston-h-zhang winston-h-zhang commented Nov 14, 2023

This PR restructures the way arecibo approaches memory allocation and restructures the entire memory pipeline. See the notion design doc for more info.

Notable improvements

  • The critical sections in prove_step and R1CSShape::commit_T no longer clone the large witness.
  • Downstream in lurk-rs, this PR fixes the strange regression we observed when using loaded public parameters. This is a strong indicator that inefficient memory allocations in arecibo was creating (and could've created) very unpredictable performance regressions.
  • Generally, the memory consumption of Nova is now very predictable, and this new memory pipeline is much safer to scale.

To-dos and other outstanding issues

  • We only refactor the Nova side of arecibo, and left the SuperNova untouched. This contains the scope of the PR. In the future, we should be interested in converting SuperNova to the same memory strategy as well.
  • The ResourceSink structure was temporarily created to manage the extra buffers prove_step needs. This should be integrated into the RecursiveSNARK API. Maybe with some sort of RecursiveSNARKEngine / RecursiveSNARKEngineTrait to manage folding.
  • There is one last inefficiency, which is that we recompute R1CS multiplication against the running witness in commit_T. The Z vectors should be moved into the ResourceSink to de-duplicate this.
  • Audit the security of these changes. @gabriel-barrett brought up possible security concerns when I re-introduced l_w_primary and l_u_primary into RecursiveSNARK, pointing out the revisiting Nova paper. I think ResourceSink fixes this, but we should make sure.
  • Refactor WitnessCS in upstream bellpepper to unify with WitnessViewCS, which is redundant.

@winston-h-zhang winston-h-zhang changed the title Restructure the memory pipeline [WIP] Restructure the memory pipeline Nov 14, 2023
@adr1anh
Copy link
Contributor

adr1anh commented Nov 14, 2023

Nice work getting rid of these clones!

Why does RecursiveSNARK::new need to return a sink, instead of keeping it as a member of the struct?

@winston-h-zhang
Copy link
Member Author

@adr1anh I originally did that, but then I didn't want to pollute the RecursiveSNARK type, so I moved it out. There should be a nice way to manage everything together, like how CommitmentEngineTrait manages generating CommitmentKeys -- some sort of RecursiveSNARKEngineTrait.

Comment on lines -46 to +50
let W = R1CSWitness::<G>::new(shape, self.aux_assignment())?;
let X = &self.input_assignment()[1..];
let W = R1CSWitness::<G>::new(shape, self.aux_assignment().to_vec())?;

let comm_W = W.commit(ck);

let instance = R1CSInstance::<G>::new(shape, &comm_W, X)?;
let instance = R1CSInstance::<G>::new(shape, comm_W, self.input_assignment().to_vec())?;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since two people have asked me about why there are still clones here, I want to clarify. This function, r1cs_instance_and_witness, is no longer being called in prove_step. This change is purely atheistic -- it moves the .to_vec() call inside R1CSWitness::new/R1CSInstance::new to be explicit on construction -- so the compiler is happy.

We do not call this function, there are no extra clones.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good change, and follows C-CALLER-CONTROL

Comment on lines +355 to +360
let (u_primary, w_primary) = r1cs::instance_and_witness(
r1cs_primary,
&pp.ck_primary,
input_assignment,
aux_assignment,
)?;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new r1cs::instance_and_witness function eats the inputs instead of cloning.

/// `setup = true`. After the initial step, every next Nova step has a fixed shape, so the buffers in
/// `R1CSWitness` and `R1CSInstance` have the exact capacity they need. To be memory efficient,
/// [`WitnessViewCS`] is flagged as `setup = false` and we no longer allow the buffers to resize.
pub struct WitnessViewCS<'a, Scalar>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines -46 to +50
let W = R1CSWitness::<G>::new(shape, self.aux_assignment())?;
let X = &self.input_assignment()[1..];
let W = R1CSWitness::<G>::new(shape, self.aux_assignment().to_vec())?;

let comm_W = W.commit(ck);

let instance = R1CSInstance::<G>::new(shape, &comm_W, X)?;
let instance = R1CSInstance::<G>::new(shape, comm_W, self.input_assignment().to_vec())?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good change, and follows C-CALLER-CONTROL

self.multiply_witness_unchecked(W, u_and_X)
}

/// Multiply by a witness representing a dense vector; uses rayon/gpu.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this uses the GPU yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a mis-comment; it indeed doesn't use GPU

Comment on lines +513 to +516
let mut W = vec![G::Scalar::ZERO; S.num_vars];
W.shrink_to_fit();
let mut E = vec![G::Scalar::ZERO; S.num_cons];
E.shrink_to_fit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the capacity allocated by the vec! macro? how does that compare to the length of the vector created by the vec! macro?

Given the answers to these questions, what is the effect of the call to shrink_to_fit()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I couldn't comfirm from the vec! documentation that vec![x; n] initializes with_capacity(n). So I just redundantly called shrink_to_fit

Copy link
Member

@huitseeker huitseeker Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I couldn't comfirm from the vec! documentation that vec![x; n] initializes with_capacity(n)

Why? This is the part of the documentation where this is confirmed.

Alternatively, a quick test might have helped answer your question using the capcity method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I just tested and indeed there's no need for shrink_to_fit

Comment on lines +715 to +726
pub fn instance_and_witness<G: Group>(
shape: &R1CSShape<G>,
ck: &CommitmentKey<G>,
input_assignment: Vec<G::Scalar>,
aux_assignment: Vec<G::Scalar>,
) -> Result<(R1CSInstance<G>, R1CSWitness<G>), NovaError> {
let W = R1CSWitness::<G>::new(shape, aux_assignment)?;
let comm_W = W.commit(ck);
let instance = R1CSInstance::<G>::new(shape, comm_W, input_assignment)?;

Ok((instance, W))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactor of r1cs_instance_and_witness in #121 should supersede this.

huitseeker added a commit that referenced this pull request Nov 15, 2023
#121)"

This reverts commit fdf8296.

This should allow us to investigate the effects of components of #118 without noise.
We can re-apply this later if we see it as an improvement.
github-merge-queue bot pushed a commit that referenced this pull request Nov 15, 2023
#121)" (#125)

This reverts commit fdf8296.

This should allow us to investigate the effects of components of #118 without noise.
We can re-apply this later if we see it as an improvement.
@winston-h-zhang
Copy link
Member Author

Closed after #137

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants