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

feat: add gate count tracking for ivc constraints #10772

Merged
merged 9 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,76 @@ void gateCount(const std::string& bytecodePath, bool recursive, bool honk_recurs
gates_per_opcode_str,
"]\n }");

// Attach a comma if we still circuit reports to generate
// Attach a comma if there are more circuit reports to generate
if (i != (constraint_systems.size() - 1)) {
result_string = format(result_string, ",");
}

functions_string = format(functions_string, result_string);

i++;
}
functions_string = format(functions_string, "\n]}");

const char* jsonData = functions_string.c_str();
size_t length = strlen(jsonData);
std::vector<uint8_t> data(jsonData, jsonData + length);
writeRawBytesToStdout(data);
}

/**
* @brief Constructs a barretenberg circuit from program bytecode and reports the resulting gate counts
* @details IVC circuits utilize the Mega arithmetization and a structured execution trace. This method reports the
* number of each gate type present in the circuit vs the fixed max number allowed by the structured trace.
*
* @param bytecodePath Path to the file containing the serialized circuit
*/
void gate_count_for_ivc(const std::string& bytecodePath)
{
// All circuit reports will be built into the string below
std::string functions_string = "{\"functions\": [\n ";
auto constraint_systems = get_constraint_systems(bytecodePath, /*honk_recursion=*/false);

// Initialize an SRS to make the ClientIVC constructor happy
init_bn254_crs(1 << 20);
init_grumpkin_crs(1 << 15);
TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE };

size_t i = 0;
for (const auto& constraint_system : constraint_systems) {
acir_format::AcirProgram program{ constraint_system };
const auto& ivc_constraints = constraint_system.ivc_recursion_constraints;
acir_format::ProgramMetadata metadata{ .ivc = ivc_constraints.empty() ? nullptr
: create_mock_ivc_from_constraints(
ivc_constraints, trace_settings),
.collect_gates_per_opcode = true };

auto builder = acir_format::create_circuit<MegaCircuitBuilder>(program, metadata);
builder.finalize_circuit(/*ensure_nonzero=*/true);
size_t circuit_size = builder.num_gates;

// Print the details of the gate types within the structured execution trace
builder.blocks.set_fixed_block_sizes(trace_settings);
builder.blocks.summarize();

// Build individual circuit report
std::string gates_per_opcode_str;
for (size_t j = 0; j < program.constraints.gates_per_opcode.size(); j++) {
gates_per_opcode_str += std::to_string(program.constraints.gates_per_opcode[j]);
if (j != program.constraints.gates_per_opcode.size() - 1) {
gates_per_opcode_str += ",";
}
}

auto result_string = format("{\n \"acir_opcodes\": ",
program.constraints.num_acir_opcodes,
",\n \"circuit_size\": ",
circuit_size,
",\n \"gates_per_opcode\": [",
gates_per_opcode_str,
"]\n }");

// Attach a comma if there are more circuit reports to generate
if (i != (constraint_systems.size() - 1)) {
result_string = format(result_string, ",");
}
Expand Down Expand Up @@ -1210,6 +1279,8 @@ int main(int argc, char* argv[])
gateCount<UltraCircuitBuilder>(bytecode_path, recursive, honk_recursion);
} else if (command == "gates_mega_honk") {
gateCount<MegaCircuitBuilder>(bytecode_path, recursive, honk_recursion);
} else if (command == "gates_for_ivc") {
gate_count_for_ivc(bytecode_path);
} else if (command == "verify") {
return verify(proof_path, vk_path) ? 0 : 1;
} else if (command == "contract") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ void build_constraints(Builder& builder, AcirProgram& program, const ProgramMeta
info("WARNING: this circuit contains unhandled avm_recursion_constraints!");
}
if (!constraint_system.ivc_recursion_constraints.empty()) {
process_ivc_recursion_constraints(builder, constraint_system, metadata.ivc, has_valid_witness_assignments);
process_ivc_recursion_constraints(
builder, constraint_system, metadata.ivc, has_valid_witness_assignments, gate_counter);
}
} else {
process_plonk_recursion_constraints(builder, constraint_system, has_valid_witness_assignments, gate_counter);
Expand Down Expand Up @@ -398,7 +399,8 @@ PairingPointAccumulatorIndices process_honk_recursion_constraints(
void process_ivc_recursion_constraints(MegaCircuitBuilder& builder,
AcirFormat& constraints,
const std::shared_ptr<ClientIVC>& ivc,
bool has_valid_witness_assignments)
bool has_valid_witness_assignments,
GateCounter<MegaCircuitBuilder>& gate_counter)
{
using StdlibVerificationKey = ClientIVC::RecursiveVerificationKey;

Expand Down Expand Up @@ -446,6 +448,11 @@ void process_ivc_recursion_constraints(MegaCircuitBuilder& builder,

// Complete the kernel circuit with all required recursive verifications, databus consistency checks etc.
ivc->complete_kernel_circuit_logic(builder);

// Note: we can't easily track the gate contribution from each individual ivc_recursion_constraint since they are
// handled simultaneously in the above function call; instead we track the total contribution
gate_counter.track_diff(constraints.gates_per_opcode,
constraints.original_opcode_indices.ivc_recursion_constraints.at(0));
}

#ifndef DISABLE_AZTEC_VM
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ void process_honk_recursion_constraints(Builder& builder,
void process_ivc_recursion_constraints(MegaCircuitBuilder& builder,
AcirFormat& constraints,
ClientIVC* ivc,
bool has_valid_witness_assignments);
bool has_valid_witness_assignments,
GateCounter<MegaCircuitBuilder>& gate_counter);

#ifndef DISABLE_AZTEC_VM
void process_avm_recursion_constraints(Builder& builder,
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/gates_report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ for pathname in "$PROTOCOL_CIRCUITS_DIR/target"/*.json; do
fi
done

# If it's mega honk, we need to use the gates_mega_honk command
# If it's mega honk, we need to use the gates_for_ivc command
if [ "$IS_MEGA_HONK_CIRCUIT" = "true" ]; then
GATES_INFO=$($BB_BIN gates_mega_honk -h -b "$pathname")
GATES_INFO=$($BB_BIN gates_for_ivc -h -b "$pathname")
else
GATES_INFO=$($BB_BIN gates -h -b "$pathname")
fi
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/noir-contracts/scripts/flamegraph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ FUNCTION_ARTIFACT="${ARTIFACT_NAME}-${FUNCTION}.json"
mkdir -p "$SCRIPT_DIR/../dest"

# At last, generate the flamegraph
$PROFILER gates --artifact-path "$SCRIPT_DIR/../target/$FUNCTION_ARTIFACT" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --backend-gates-command "gates_mega_honk" --output "$SCRIPT_DIR/../dest"
$PROFILER gates --artifact-path "$SCRIPT_DIR/../target/$FUNCTION_ARTIFACT" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --backend-gates-command "gates_for_ivc" --output "$SCRIPT_DIR/../dest"

# serve the file over http
echo "Serving flamegraph at http://0.0.0.0:8000/main::gates.svg"
Expand Down
4 changes: 2 additions & 2 deletions noir-projects/noir-protocol-circuits/crates/blob/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ Current count: 428,240.
Install bbup: `curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash`
The version of bb compatible with nargo 0.33.0 is `bbup --version 0.48.0`

`time bb gates_mega_honk -b ./target/blob.json`
`time bb gates_for_ivc -b ./target/blob.json`

## Generate, then serve a new flamegraph, after compiling:

<!-- `~/packages/noir/noir-repo/target/release/noir-profiler gates-flamegraph --artifact-path ./target/blob.json --backend-path ~/.bb/bb --output ./flamegraph -- -h && python3 -m http.server --directory "./flamegraph" 3000` -->

`~/packages/noir/noir-repo/target/release/noir-profiler gates-flamegraph --artifact-path ./target/blob.json --backend-path ~/.bb/bb --output ./flamegraph --backend-gates-command "gates_mega_honk" -- -h && python3 -m http.server --directory "./flamegraph" 3000`
`~/packages/noir/noir-repo/target/release/noir-profiler gates-flamegraph --artifact-path ./target/blob.json --backend-path ~/.bb/bb --output ./flamegraph --backend-gates-command "gates_for_ivc" -- -h && python3 -m http.server --directory "./flamegraph" 3000`

## To serve an existing flamegraph:

Expand Down
2 changes: 1 addition & 1 deletion noir-projects/noir-protocol-circuits/scripts/flamegraph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ for CIRCUIT_NAME in "${CIRCUIT_NAMES[@]}"; do

# Generate the flamegraph.
if [ "$IS_MEGA_HONK_CIRCUIT" = "true" ]; then
$PROFILER gates --artifact-path "${ARTIFACT}" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --output "$DEST" --output-filename "$CIRCUIT_NAME" --backend-gates-command "gates_mega_honk" -- -h
$PROFILER gates --artifact-path "${ARTIFACT}" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --output "$DEST" --output-filename "$CIRCUIT_NAME" --backend-gates-command "gates_for_ivc" -- -h
else
$PROFILER gates --artifact-path "${ARTIFACT}" --backend-path "$SCRIPT_DIR/../../../barretenberg/cpp/build/bin/bb" --output "$DEST" --output-filename "$CIRCUIT_NAME" -- -h
fi
Expand Down
4 changes: 3 additions & 1 deletion yarn-project/bb-prover/src/bb/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@ export async function computeGateCountForCircuit(
circuitName: string,
bytecode: Buffer,
flavor: UltraHonkFlavor | 'mega_honk',
log: LogFn,
): Promise<BBFailure | BBSuccess> {
// Check that the working directory exists
try {
Expand All @@ -912,6 +913,7 @@ export async function computeGateCountForCircuit(
let stdout = '';
const logHandler = (message: string) => {
stdout += message;
log(message);
};

try {
Expand All @@ -921,7 +923,7 @@ export async function computeGateCountForCircuit(

const result = await executeBB(
pathToBB,
flavor === 'mega_honk' ? `gates_mega_honk` : `gates`,
flavor === 'mega_honk' ? `gates_for_ivc` : `gates`,
['-b', bytecodePath, '-v'],
logHandler,
);
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,17 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {
}

public async computeGateCountForCircuit(bytecode: Buffer, circuitName: string): Promise<number> {
const logFunction = (message: string) => {
this.log.debug(`$bb gates ${circuitName} - ${message}`);
};

const result = await computeGateCountForCircuit(
this.bbBinaryPath,
this.bbWorkingDirectory,
circuitName,
bytecode,
'mega_honk',
logFunction,
);
if (result.status === BB_RESULT.FAILURE) {
throw new Error(result.reason);
Expand Down
Loading