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

optimize oct and quad insertion #1125

Merged
merged 1 commit into from
May 29, 2020
Merged

Conversation

porcuquine
Copy link
Collaborator

@porcuquine porcuquine commented May 27, 2020

WARNING: this PR reduces constraints and is therefore a breaking circuit change. Only merge once last v26 release has been cut. cc: @cryptonemo

This PR updates insertion.rs to use hand-optimized versions of insertion when the size of the output array is either 4 or 8. These are the sizes used for our merkle trees and for which optimization will help. By taking advantage of redundancy in the output for each position, we can avoid the full generality otherwise required. This allows us to avoid the quadratic cost of size * (size - 1) constraints.

In order to aid in legibility and for consistency with internal spec audit (the motivation for this change), I have introduced a macro, witness! which allows declarative assignment of constraints to allocated numbers based on the value of an allocated boolean value.

Previous behavior is preserved and verified in tests — so bit input for insertion is still little endian. Bit and position indices are 0-indexed, and witness names rely on these conventions systematically.

For reference, here are the constraint definitions used to compute the oct insertion result:

    witness!(p1_x00 <== if b0 { a } else { b });
    witness!(p1_xx0 <== if b1 { c } else { &p1_x00 });
    witness!(p1 <== if b2 { c } else { &p1_xx0 });

    witness!(p2_x10 <== if b0 { d } else { a });
    witness!(p2_xx0 <== if b1 { &p2_x10 } else { c });
    witness!(p2 <== if b2 { d } else { &p2_xx0 });

    witness!(p3_xx0 <== if &b0_and_b1 { a } else { d });
    witness!(p3 <== if b2 { e } else { &p3_xx0 });

    witness!(p4_xx1 <== if &b0_nor_b1 { a } else { f });
    witness!(p4 <== if b2 { &p4_xx1 } else { e });

    witness!(p5_x01 <== if b0 { a } else { f });
    witness!(p5_xx1 <== if b1 { g } else { &p5_x01 });
    witness!(p5 <== if b2 { &p5_xx1 } else { f });

    witness!(p6_x11 <== if b0 { h } else { a });
    witness!(p6_xx1 <== if b1 { &p6_x11 } else { g });
    witness!(p6 <== if b2 { &p6_xx1 } else { g });

    witness!(p7_xx1 <== if &b0_and_b1 { a } else { h });
    witness!(p7 <== if b2 { &p7_xx1 } else { h });

    Ok(vec![p0, p1, p2, p3, p4, p5, p6, p7])

@porcuquine porcuquine changed the title Feat/optimized oct insert optimize oct and quad insertion May 27, 2020
@@ -69,6 +86,172 @@ pub fn insert<E: Engine, CS: ConstraintSystem<E>>(
Ok(result)
}

pub fn insert_4<E: Engine, CS: ConstraintSystem<E>>(
Copy link
Contributor

Choose a reason for hiding this comment

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

when do we use quads?

Copy link
Collaborator Author

@porcuquine porcuquine May 27, 2020

Choose a reason for hiding this comment

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

Quads are needed any time we need to build a tree which cannot be constructed either with:

  • only oct trees
  • only oct and binary trees

Put differently, we need them whenever log2 N mod 3 = 2, where N is the number of 32-byte leaves. Although not currently supported, this would include either 16 or 128GiB sectors. Since we have only currently generated parameters for 32 and 64GiB sectors, quad hashing is dormant. But it's important that we preserve the freedom to support any power of two sector size — so the audit needs to cover quad hashes.

let (b0, b1) = (&bits[0], &bits[1]);
let (a, b, c, d) = (&element, &elements[0], &elements[1], &elements[2]);

/// Define constrain macro to allow legible definition of positional constraints.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that this is a nice use of macros, however (imho) this adds code without expressing new ideas or clarifying preexisting ones. The "assign and constrain" operator looks (imo) out of place with respect to bellman's view of how gadgets should be defined and values allocated. Can the condition bit ever not be a reference?

cryptonemo
cryptonemo previously approved these changes May 28, 2020
Copy link
Collaborator

@cryptonemo cryptonemo left a comment

Choose a reason for hiding this comment

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

Looks good after a rebase

@porcuquine porcuquine force-pushed the feat/optimized-oct-insert branch 3 times, most recently from 625e0d8 to eab7af5 Compare May 28, 2020 21:59
@porcuquine porcuquine merged commit 3fa6901 into master May 29, 2020
@porcuquine porcuquine deleted the feat/optimized-oct-insert branch May 29, 2020 00:49
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.

4 participants