diff --git a/constraint/bls12-377/coeff.go b/constraint/bls12-377/coeff.go index 5b4034aaab..d7aa2403b8 100644 --- a/constraint/bls12-377/coeff.go +++ b/constraint/bls12-377/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bls12-377/r1cs_test.go b/constraint/bls12-377/r1cs_test.go index 99c531bf98..f789c5f50b 100644 --- a/constraint/bls12-377/r1cs_test.go +++ b/constraint/bls12-377/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/bls12-381/coeff.go b/constraint/bls12-381/coeff.go index 2d27dc0b02..55328ca99a 100644 --- a/constraint/bls12-381/coeff.go +++ b/constraint/bls12-381/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bls12-381/r1cs_test.go b/constraint/bls12-381/r1cs_test.go index 98830260bc..3a7e10de55 100644 --- a/constraint/bls12-381/r1cs_test.go +++ b/constraint/bls12-381/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/bls24-315/coeff.go b/constraint/bls24-315/coeff.go index 3f117c9b0f..dd3f16bea7 100644 --- a/constraint/bls24-315/coeff.go +++ b/constraint/bls24-315/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bls24-315/r1cs_test.go b/constraint/bls24-315/r1cs_test.go index a1baa6474c..bf09d377f8 100644 --- a/constraint/bls24-315/r1cs_test.go +++ b/constraint/bls24-315/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/bls24-317/coeff.go b/constraint/bls24-317/coeff.go index 20cbf188d7..15c2373224 100644 --- a/constraint/bls24-317/coeff.go +++ b/constraint/bls24-317/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bls24-317/r1cs_test.go b/constraint/bls24-317/r1cs_test.go index ef2a685d08..124f6403f0 100644 --- a/constraint/bls24-317/r1cs_test.go +++ b/constraint/bls24-317/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/blueprint_hint.go b/constraint/blueprint_hint.go index bc171232ac..159cb6042e 100644 --- a/constraint/blueprint_hint.go +++ b/constraint/blueprint_hint.go @@ -43,7 +43,7 @@ func (b *BlueprintGenericHint) CompressHint(h HintMapping) []uint32 { nbInputs += 2 // output range start / end - r := make([]uint32, 0, nbInputs) + r := getBuffer(nbInputs) r = append(r, uint32(nbInputs)) r = append(r, uint32(h.HintID)) r = append(r, uint32(len(h.Inputs))) diff --git a/constraint/blueprint_r1cs.go b/constraint/blueprint_r1cs.go index 936a15cc07..b274397b28 100644 --- a/constraint/blueprint_r1cs.go +++ b/constraint/blueprint_r1cs.go @@ -17,7 +17,7 @@ func (b *BlueprintGenericR1C) NbConstraints() int { func (b *BlueprintGenericR1C) CompressR1C(c *R1C) []uint32 { // we store total nb inputs, len L, len R, len O, and then the "flatten" linear expressions nbInputs := 4 + 2*(len(c.L)+len(c.R)+len(c.O)) - r := make([]uint32, 0, nbInputs) + r := getBuffer(nbInputs) r = append(r, uint32(nbInputs)) r = append(r, uint32(len(c.L)), uint32(len(c.R)), uint32(len(c.O))) for _, t := range c.L { @@ -56,3 +56,17 @@ func (b *BlueprintGenericR1C) DecompressR1C(c *R1C, calldata []uint32) { copySlice(&c.R, lenR, offset+2*lenL) copySlice(&c.O, lenO, offset+2*(lenL+lenR)) } + +// since frontend is single threaded, to avoid allocating slices at each compress call +// we transit the compressed output through here +var bufCalldata []uint32 + +// getBuffer return a slice with at least the given capacity to use in Compress methods +// this is obviously not thread safe, but the frontend is single threaded anyway. +func getBuffer(size int) []uint32 { + if cap(bufCalldata) < size { + bufCalldata = make([]uint32, 0, size*2) + } + bufCalldata = bufCalldata[:0] + return bufCalldata +} diff --git a/constraint/blueprint_scs.go b/constraint/blueprint_scs.go index f12584289b..90ede13fb7 100644 --- a/constraint/blueprint_scs.go +++ b/constraint/blueprint_scs.go @@ -14,7 +14,8 @@ var ( // Encodes // // qL⋅xa + qR⋅xb + qO⋅xc + qM⋅(xaxb) + qC == 0 -type BlueprintGenericSparseR1C struct{} +type BlueprintGenericSparseR1C struct { +} func (b *BlueprintGenericSparseR1C) NbInputs() int { return 9 // number of fields in SparseR1C @@ -24,17 +25,16 @@ func (b *BlueprintGenericSparseR1C) NbConstraints() int { } func (b *BlueprintGenericSparseR1C) CompressSparseR1C(c *SparseR1C) []uint32 { - return []uint32{ - c.XA, - c.XB, - c.XC, - c.QL, - c.QR, - c.QO, - c.QM, - c.QC, - uint32(c.Commitment), - } + bufSCS[0] = c.XA + bufSCS[1] = c.XB + bufSCS[2] = c.XC + bufSCS[3] = c.QL + bufSCS[4] = c.QR + bufSCS[5] = c.QO + bufSCS[6] = c.QM + bufSCS[7] = c.QC + bufSCS[8] = uint32(c.Commitment) + return bufSCS[:] } func (b *BlueprintGenericSparseR1C) DecompressSparseR1C(c *SparseR1C, calldata []uint32) { @@ -167,12 +167,11 @@ func (b *BlueprintSparseR1CMul) NbConstraints() int { } func (b *BlueprintSparseR1CMul) CompressSparseR1C(c *SparseR1C) []uint32 { - return []uint32{ - c.XA, - c.XB, - c.XC, - c.QM, - } + bufSCS[0] = c.XA + bufSCS[1] = c.XB + bufSCS[2] = c.XC + bufSCS[3] = c.QM + return bufSCS[:4] } func (b *BlueprintSparseR1CMul) Solve(s Solver, calldata []uint32) error { @@ -209,14 +208,13 @@ func (b *BlueprintSparseR1CAdd) NbConstraints() int { } func (b *BlueprintSparseR1CAdd) CompressSparseR1C(c *SparseR1C) []uint32 { - return []uint32{ - c.XA, - c.XB, - c.XC, - c.QL, - c.QR, - c.QC, - } + bufSCS[0] = c.XA + bufSCS[1] = c.XB + bufSCS[2] = c.XC + bufSCS[3] = c.QL + bufSCS[4] = c.QR + bufSCS[5] = c.QC + return bufSCS[:6] } func (blueprint *BlueprintSparseR1CAdd) Solve(s Solver, calldata []uint32) error { @@ -258,11 +256,10 @@ func (b *BlueprintSparseR1CBool) NbConstraints() int { } func (b *BlueprintSparseR1CBool) CompressSparseR1C(c *SparseR1C) []uint32 { - return []uint32{ - c.XA, - c.QL, - c.QM, - } + bufSCS[0] = c.XA + bufSCS[1] = c.QL + bufSCS[2] = c.QM + return bufSCS[:3] } func (blueprint *BlueprintSparseR1CBool) Solve(s Solver, calldata []uint32) error { @@ -285,3 +282,7 @@ func (b *BlueprintSparseR1CBool) DecompressSparseR1C(c *SparseR1C, calldata []ui c.QL = calldata[1] c.QM = calldata[2] } + +// since frontend is single threaded, to avoid allocating slices at each compress call +// we transit the compressed output through here +var bufSCS [9]uint32 diff --git a/constraint/bn254/coeff.go b/constraint/bn254/coeff.go index b4d06ad0a6..0ce316660d 100644 --- a/constraint/bn254/coeff.go +++ b/constraint/bn254/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bn254/r1cs_test.go b/constraint/bn254/r1cs_test.go index f872b83a3f..e7ca65cfd8 100644 --- a/constraint/bn254/r1cs_test.go +++ b/constraint/bn254/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/bw6-633/coeff.go b/constraint/bw6-633/coeff.go index 01d84ce575..fd9a96465c 100644 --- a/constraint/bw6-633/coeff.go +++ b/constraint/bw6-633/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bw6-633/r1cs_test.go b/constraint/bw6-633/r1cs_test.go index 954401f685..e63b4d8a5e 100644 --- a/constraint/bw6-633/r1cs_test.go +++ b/constraint/bw6-633/r1cs_test.go @@ -80,7 +80,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/bw6-761/coeff.go b/constraint/bw6-761/coeff.go index 0206740d31..c23fff8258 100644 --- a/constraint/bw6-761/coeff.go +++ b/constraint/bw6-761/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/bw6-761/r1cs_test.go b/constraint/bw6-761/r1cs_test.go index 54309d49aa..32136c08b3 100644 --- a/constraint/bw6-761/r1cs_test.go +++ b/constraint/bw6-761/r1cs_test.go @@ -83,7 +83,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/constraint/core.go b/constraint/core.go index 2bc71f49bb..6efb8d911f 100644 --- a/constraint/core.go +++ b/constraint/core.go @@ -88,9 +88,8 @@ type System struct { bitLen int `cbor:"-"` // level builder - lbWireLevel []int `cbor:"-"` // at which level we solve a wire. init at -1. - lbOutputs []uint32 `cbor:"-"` // wire outputs for current constraint. - lbHints map[int]struct{} `cbor:"-"` // hints we processed in current round + lbWireLevel []int `cbor:"-"` // at which level we solve a wire. init at -1. + lbOutputs []uint32 `cbor:"-"` // wire outputs for current constraint. CommitmentInfo Commitment @@ -108,9 +107,11 @@ func NewSystem(scalarField *big.Int, capacity int, t SystemType) System { MHintsDependencies: make(map[solver.HintID]string), q: new(big.Int).Set(scalarField), bitLen: scalarField.BitLen(), - lbHints: map[int]struct{}{}, Instructions: make([]Instruction, 0, capacity), - CallData: make([]uint32, 0, capacity*2), + CallData: make([]uint32, 0, capacity*8), + lbOutputs: make([]uint32, 0, 256), + lbWireLevel: make([]int, 0, capacity), + Levels: make([][]int, 0, capacity/2), } system.genericHint = system.AddBlueprint(&BlueprintGenericHint{}) return system diff --git a/constraint/hint.go b/constraint/hint.go index e8c9f91738..a79bfea41d 100644 --- a/constraint/hint.go +++ b/constraint/hint.go @@ -21,14 +21,14 @@ func (h *HintMapping) WireIterator() func() int { for i := 0; i < len(h.Inputs); i++ { n += len(h.Inputs[i]) } - inputs := make([]int, 0, n) + inputs := getBuffer(n) for i := 0; i < len(h.Inputs); i++ { for j := 0; j < len(h.Inputs[i]); j++ { term := h.Inputs[i][j] if term.IsConstant() { continue } - inputs = append(inputs, int(term.VID)) + inputs = append(inputs, term.VID) } } lenOutputs := int(h.OutputRange.End - h.OutputRange.Start) @@ -40,7 +40,7 @@ func (h *HintMapping) WireIterator() func() int { } if curr < lenOutputs+len(inputs) { curr++ - return inputs[curr-1-lenOutputs] + return int(inputs[curr-1-lenOutputs]) } return -1 } diff --git a/constraint/level_builder.go b/constraint/level_builder.go index 052d456809..f29c69f769 100644 --- a/constraint/level_builder.go +++ b/constraint/level_builder.go @@ -34,7 +34,6 @@ func (system *System) updateLevel(iID int, c Iterable) { // clean the table. NB! Do not remove or move, this is required to make the // compilation deterministic. system.lbOutputs = system.lbOutputs[:0] - system.lbHints = map[int]struct{}{} } func (system *System) processWire(wireID uint32, maxLevel *int) { diff --git a/constraint/r1cs_test.go b/constraint/r1cs_test.go index 4a82ca79d5..9444b5ef53 100644 --- a/constraint/r1cs_test.go +++ b/constraint/r1cs_test.go @@ -31,26 +31,26 @@ func ExampleR1CS_GetR1Cs() { // X² == X * X r1cs.AddR1C(constraint.R1C{ - L: constraint.LinearExpression{r1cs.MakeTerm(&cOne, X)}, - R: constraint.LinearExpression{r1cs.MakeTerm(&cOne, X)}, - O: constraint.LinearExpression{r1cs.MakeTerm(&cOne, v0)}, + L: constraint.LinearExpression{r1cs.MakeTerm(cOne, X)}, + R: constraint.LinearExpression{r1cs.MakeTerm(cOne, X)}, + O: constraint.LinearExpression{r1cs.MakeTerm(cOne, v0)}, }, blueprint) // X³ == X² * X r1cs.AddR1C(constraint.R1C{ - L: constraint.LinearExpression{r1cs.MakeTerm(&cOne, v0)}, - R: constraint.LinearExpression{r1cs.MakeTerm(&cOne, X)}, - O: constraint.LinearExpression{r1cs.MakeTerm(&cOne, v1)}, + L: constraint.LinearExpression{r1cs.MakeTerm(cOne, v0)}, + R: constraint.LinearExpression{r1cs.MakeTerm(cOne, X)}, + O: constraint.LinearExpression{r1cs.MakeTerm(cOne, v1)}, }, blueprint) // Y == X³ + X + 5 r1cs.AddR1C(constraint.R1C{ - R: constraint.LinearExpression{r1cs.MakeTerm(&cOne, ONE)}, - L: constraint.LinearExpression{r1cs.MakeTerm(&cOne, Y)}, + R: constraint.LinearExpression{r1cs.MakeTerm(cOne, ONE)}, + L: constraint.LinearExpression{r1cs.MakeTerm(cOne, Y)}, O: constraint.LinearExpression{ - r1cs.MakeTerm(&cFive, ONE), - r1cs.MakeTerm(&cOne, X), - r1cs.MakeTerm(&cOne, v1), + r1cs.MakeTerm(cFive, ONE), + r1cs.MakeTerm(cOne, X), + r1cs.MakeTerm(cOne, v1), }, }, blueprint) diff --git a/constraint/system.go b/constraint/system.go index fae9782eb9..964838485d 100644 --- a/constraint/system.go +++ b/constraint/system.go @@ -53,7 +53,7 @@ type ConstraintSystem interface { // MakeTerm returns a new Term. The constraint system may store coefficients in a map, so // calls to this function will grow the memory usage of the constraint system. - MakeTerm(coeff *Element, variableID int) Term + MakeTerm(coeff Element, variableID int) Term // AddCoeff adds a coefficient to the underlying constraint system. The system will not store duplicate, // but is not purging for unused coeff either, so this grows memory usage. diff --git a/constraint/tinyfield/coeff.go b/constraint/tinyfield/coeff.go index f81f7b969f..0cdeb84a93 100644 --- a/constraint/tinyfield/coeff.go +++ b/constraint/tinyfield/coeff.go @@ -72,8 +72,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } diff --git a/constraint/tinyfield/r1cs_test.go b/constraint/tinyfield/r1cs_test.go index 44b9e76772..632222a0fa 100644 --- a/constraint/tinyfield/r1cs_test.go +++ b/constraint/tinyfield/r1cs_test.go @@ -83,7 +83,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs", diff --git a/frontend/cs/r1cs/builder.go b/frontend/cs/r1cs/builder.go index 8fa82209a0..b8036314fe 100644 --- a/frontend/cs/r1cs/builder.go +++ b/frontend/cs/r1cs/builder.go @@ -206,7 +206,7 @@ func (builder *builder) getLinearExpression(_l interface{}) constraint.LinearExp } L = make(constraint.LinearExpression, 0, len(tl)) for _, t := range tl { - L = append(L, builder.cs.MakeTerm(&t.Coeff, t.VID)) + L = append(L, builder.cs.MakeTerm(t.Coeff, t.VID)) } case constraint.LinearExpression: L = tl @@ -381,7 +381,7 @@ func (builder *builder) NewHint(f solver.Hint, nbOutputs int, inputs ...frontend hintInputs[i] = builder.getLinearExpression(t) } else { c := builder.cs.FromInterface(in) - term := builder.cs.MakeTerm(&c, 0) + term := builder.cs.MakeTerm(c, 0) term.MarkConstant() hintInputs[i] = constraint.LinearExpression{term} } diff --git a/frontend/cs/scs/api.go b/frontend/cs/scs/api.go index 1e932a34a2..c542a2752b 100644 --- a/frontend/cs/scs/api.go +++ b/frontend/cs/scs/api.go @@ -23,6 +23,7 @@ import ( "runtime" "strings" + "github.com/consensys/gnark/debug" "github.com/consensys/gnark/frontend/cs" "github.com/consensys/gnark/constraint" @@ -157,16 +158,23 @@ func (builder *builder) Inverse(i1 frontend.Variable) frontend.Variable { return builder.cs.ToBigInt(c) } t := i1.(expr.Term) - debug := builder.newDebugInfo("inverse", "1/", i1, " < ∞") res := builder.newInternalVariable() // res * i1 - 1 == 0 - builder.addPlonkConstraint(sparseR1C{ + constraint := sparseR1C{ xa: res.VID, xb: t.VID, qM: t.Coeff, qC: builder.tMinusOne, - }, debug) + } + + if debug.Debug { + debug := builder.newDebugInfo("inverse", "1/", i1, " < ∞") + builder.addPlonkConstraint(constraint, debug) + } else { + builder.addPlonkConstraint(constraint) + } + return res } @@ -513,7 +521,7 @@ func (builder *builder) Println(a ...frontend.Variable) { sbb.WriteString("%s") // we set limits to the linear expression, so that the log printer // can evaluate it before printing it - log.ToResolve = append(log.ToResolve, constraint.LinearExpression{builder.cs.MakeTerm(&v.Coeff, v.VID)}) + log.ToResolve = append(log.ToResolve, constraint.LinearExpression{builder.cs.MakeTerm(v.Coeff, v.VID)}) } else { builder.printArg(&log, &sbb, arg) } @@ -549,7 +557,7 @@ func (builder *builder) printArg(log *constraint.LogEntry, sbb *strings.Builder, v := tValue.Interface().(expr.Term) // we set limits to the linear expression, so that the log printer // can evaluate it before printing it - log.ToResolve = append(log.ToResolve, constraint.LinearExpression{builder.cs.MakeTerm(&v.Coeff, v.VID)}) + log.ToResolve = append(log.ToResolve, constraint.LinearExpression{builder.cs.MakeTerm(v.Coeff, v.VID)}) return nil } // ignoring error, printer() doesn't return errors diff --git a/frontend/cs/scs/api_assertions.go b/frontend/cs/scs/api_assertions.go index 41b6d51b3a..a4e4447b17 100644 --- a/frontend/cs/scs/api_assertions.go +++ b/frontend/cs/scs/api_assertions.go @@ -47,29 +47,40 @@ func (builder *builder) AssertIsEqual(i1, i2 frontend.Variable) { if i2Constant { xa := i1.(expr.Term) c2 := builder.cs.Neg(c2) - debug := builder.newDebugInfo("assertIsEqual", xa, "==", i2) // xa - i2 == 0 - builder.addPlonkConstraint(sparseR1C{ + toAdd := sparseR1C{ xa: xa.VID, qL: xa.Coeff, qC: c2, - }, debug) + } + + if debug.Debug { + debug := builder.newDebugInfo("assertIsEqual", xa, "==", i2) + builder.addPlonkConstraint(toAdd, debug) + } else { + builder.addPlonkConstraint(toAdd) + } return } xa := i1.(expr.Term) xb := i2.(expr.Term) - debug := builder.newDebugInfo("assertIsEqual", xa, " == ", xb) - xb.Coeff = builder.cs.Neg(xb.Coeff) // xa - xb == 0 - builder.addPlonkConstraint(sparseR1C{ + toAdd := sparseR1C{ xa: xa.VID, xb: xb.VID, qL: xa.Coeff, qR: xb.Coeff, - }, debug) + } + + if debug.Debug { + debug := builder.newDebugInfo("assertIsEqual", xa, " == ", xb) + builder.addPlonkConstraint(toAdd, debug) + } else { + builder.addPlonkConstraint(toAdd) + } } // AssertIsDifferent fails if i1 == i2 diff --git a/frontend/cs/scs/builder.go b/frontend/cs/scs/builder.go index 53604b2db4..eca3f32a4b 100644 --- a/frontend/cs/scs/builder.go +++ b/frontend/cs/scs/builder.go @@ -69,6 +69,10 @@ type builder struct { genericGate constraint.BlueprintID mulGate, addGate, boolGate constraint.BlueprintID + + // used to avoid repeated allocations + bufL expr.LinearExpression + bufH []constraint.LinearExpression } // initialCapacity has quite some impact on frontend performance, especially on large circuits size @@ -80,7 +84,10 @@ func newBuilder(field *big.Int, config frontend.CompileConfig) *builder { mAddInstructions: make(map[uint64]int, config.Capacity/2), config: config, Store: kvstore.New(), + bufL: make(expr.LinearExpression, 20), } + // init hint buffer. + _ = b.hintBuffer(256) curve := utils.FieldToCurve(field) @@ -311,6 +318,17 @@ func (builder *builder) constantValue(v frontend.Variable) (constraint.Element, return builder.cs.FromInterface(v), true } +func (builder *builder) hintBuffer(size int) []constraint.LinearExpression { + if cap(builder.bufH) < size { + builder.bufH = make([]constraint.LinearExpression, 2*size) + for i := 0; i < len(builder.bufH); i++ { + builder.bufH[i] = make(constraint.LinearExpression, 1) + } + } + + return builder.bufH[:size] +} + // NewHint initializes internal variables whose value will be evaluated using // the provided hint function at run time from the inputs. Inputs must be either // variables or convertible to *big.Int. The function returns an error if the @@ -324,18 +342,19 @@ func (builder *builder) constantValue(v frontend.Variable) (constraint.Element, // No new constraints are added to the newly created wire and must be added // manually in the circuit. Failing to do so leads to solver failure. func (builder *builder) NewHint(f solver.Hint, nbOutputs int, inputs ...frontend.Variable) ([]frontend.Variable, error) { - hintInputs := make([]constraint.LinearExpression, len(inputs)) + + hintInputs := builder.hintBuffer(len(inputs)) // ensure inputs are set and pack them in a []uint64 for i, in := range inputs { switch t := in.(type) { case expr.Term: - hintInputs[i] = constraint.LinearExpression{builder.cs.MakeTerm(&t.Coeff, t.VID)} + hintInputs[i][0] = builder.cs.MakeTerm(t.Coeff, t.VID) default: c := builder.cs.FromInterface(in) - term := builder.cs.MakeTerm(&c, 0) + term := builder.cs.MakeTerm(c, 0) term.MarkConstant() - hintInputs[i] = constraint.LinearExpression{term} + hintInputs[i][0] = term } } @@ -355,7 +374,14 @@ func (builder *builder) NewHint(f solver.Hint, nbOutputs int, inputs ...frontend // returns in split into a slice of compiledTerm and the sum of all constants in in as a bigInt func (builder *builder) filterConstantSum(in []frontend.Variable) (expr.LinearExpression, constraint.Element) { - res := make(expr.LinearExpression, 0, len(in)) + var res expr.LinearExpression + if len(in) <= cap(builder.bufL) { + // we can use the temp buffer + res = builder.bufL[:0] + } else { + res = make(expr.LinearExpression, 0, len(in)) + } + b := constraint.Element{} for i := 0; i < len(in); i++ { if c, ok := builder.constantValue(in[i]); ok { @@ -369,7 +395,14 @@ func (builder *builder) filterConstantSum(in []frontend.Variable) (expr.LinearEx // returns in split into a slice of compiledTerm and the product of all constants in in as a coeff func (builder *builder) filterConstantProd(in []frontend.Variable) (expr.LinearExpression, constraint.Element) { - res := make(expr.LinearExpression, 0, len(in)) + var res expr.LinearExpression + if len(in) <= cap(builder.bufL) { + // we can use the temp buffer + res = builder.bufL[:0] + } else { + res = make(expr.LinearExpression, 0, len(in)) + } + b := builder.tOne for i := 0; i < len(in); i++ { if c, ok := builder.constantValue(in[i]); ok { @@ -452,7 +485,7 @@ func (builder *builder) addConstraintExist(a, b expr.Term, k constraint.Element) a, b = b, a // ensure a is in qL } - tk := builder.cs.MakeTerm(&k, 0) + tk := builder.cs.MakeTerm(k, 0) if tk.CoeffID() != int(c.QC) { // the constant part of the addition differs, no point going forward // since we will need to add a new constraint anyway. @@ -462,8 +495,8 @@ func (builder *builder) addConstraintExist(a, b expr.Term, k constraint.Element) // check that the coeff matches qL := a.Coeff qR := b.Coeff - ta := builder.cs.MakeTerm(&qL, 0) - tb := builder.cs.MakeTerm(&qR, 0) + ta := builder.cs.MakeTerm(qL, 0) + tb := builder.cs.MakeTerm(qR, 0) if int(c.QL) != ta.CoeffID() || int(c.QR) != tb.CoeffID() { if !k.IsZero() { // may be for some edge cases we could avoid adding a constraint here. @@ -543,7 +576,7 @@ func (builder *builder) mulConstraintExist(a, b expr.Term) (expr.Term, bool) { // recompute the qM coeff and check that it matches; qM := builder.cs.Mul(a.Coeff, b.Coeff) - tm := builder.cs.MakeTerm(&qM, 0) + tm := builder.cs.MakeTerm(qM, 0) if int(c.QM) != tm.CoeffID() { // so we wanted to compute // N * xC == qM*xA*xB @@ -602,9 +635,9 @@ func (builder *builder) newDebugInfo(errName string, in ...interface{}) constrai case *expr.LinearExpression, expr.LinearExpression: // shouldn't happen case expr.Term: - in[i] = builder.cs.MakeTerm(&t.Coeff, t.VID) + in[i] = builder.cs.MakeTerm(t.Coeff, t.VID) case *expr.Term: - in[i] = builder.cs.MakeTerm(&t.Coeff, t.VID) + in[i] = builder.cs.MakeTerm(t.Coeff, t.VID) case constraint.Element: in[i] = builder.cs.String(t) case *constraint.Element: diff --git a/internal/generator/backend/template/representations/coeff.go.tmpl b/internal/generator/backend/template/representations/coeff.go.tmpl index 3275becce4..637fd7cf29 100644 --- a/internal/generator/backend/template/representations/coeff.go.tmpl +++ b/internal/generator/backend/template/representations/coeff.go.tmpl @@ -53,9 +53,8 @@ func (ct *CoeffTable) AddCoeff(coeff constraint.Element) uint32 { return cID } - -func (ct *CoeffTable) MakeTerm(coeff *constraint.Element, variableID int) constraint.Term { - cID := ct.AddCoeff(*coeff) +func (ct *CoeffTable) MakeTerm(coeff constraint.Element, variableID int) constraint.Term { + cID := ct.AddCoeff(coeff) return constraint.Term{VID: uint32(variableID), CID: cID} } @@ -64,9 +63,6 @@ func (ct *CoeffTable) CoeffToString(cID int) string { return ct.Coefficients[cID].String() } - - - // implements constraint.Field type field struct{} diff --git a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl index 5b583e6d80..3c5c9a8be4 100644 --- a/internal/generator/backend/template/representations/tests/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/tests/r1cs.go.tmpl @@ -71,7 +71,6 @@ func TestSerialization(t *testing.T) { "field", "CoeffTable.mCoeffs", "System.lbWireLevel", - "System.lbHints", "System.genericHint", "System.SymbolTable", "System.lbOutputs",