Skip to content

Commit

Permalink
feat: Add gate profiler for noir circuits (#7004)
Browse files Browse the repository at this point in the history
This PR:

- Modifies acir format to track constraint => original opcode and to
store gates per opcode (only gates, no lookups ATM) when building a
circuit
- Modifies the gates command to return this information along with the
totals
- Adds a new profiler binary in noir tooling with a command generate a
gates flamegraph
- Adds a small script in noir-projects to extract a private function as
a regular noir artifact, to make it usable by the profiler. This is
needed because transpiling makes our artifacts unreadable by noir
tooling.
 
 
Example result: (altough github breaks the zoom/search functionality
when uploading the svg)

![transfer](https://github.com/AztecProtocol/aztec-packages/assets/5372114/1b489182-bd4b-445d-81bd-45651753300f)

 
Usage:
  ```
./target/release/noir_profiler gates-flamegraph --artifact-path
PATH_TO_THE_ARTIFACT_JSON --backend-path BB_PATH --output OUTPUT_FOLDER
  ```
Example:
```
./target/release/noir_profiler gates-flamegraph --artifact-path ~/aztec-packages/noir-projects/noir-contracts/target/token_contract-Token-transfer.json --backend-path ~/aztec-packages/barretenberg/cpp/build/bin/bb  --output .
```

Or you can run it from the code in `noir/noir-repo/tooling/profiler` via
`cargo run` instead of directly calling the built binary.

To use the small tool to extract private functions:
```
node extractFunctionAsNoirArtifact.js PATH_TO_CONTRACT_ARTIFACT FUNCTION_NAME
```
Example: 
```
node extractFunctionAsNoirArtifact.js ./target/token_contract-Token.json transfer
```

---------

Co-authored-by: Leila Wang <LeilaWang@users.noreply.github.com>
Co-authored-by: Michael Connor <iAmMichaelConnor@users.noreply.github.com>
Co-authored-by: iAmMichaelConnor <mike@aztecprotocol.com>
Co-authored-by: ludamad <adam.domurad@gmail.com>
  • Loading branch information
5 people authored Jun 13, 2024
1 parent 12af650 commit a2f6876
Show file tree
Hide file tree
Showing 29 changed files with 1,574 additions and 358 deletions.
14 changes: 12 additions & 2 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,15 +446,25 @@ void gateCount(const std::string& bytecodePath, bool honk_recursion)
size_t i = 0;
for (auto constraint_system : constraint_systems) {
acir_proofs::AcirComposer acir_composer(0, verbose_logging);
acir_composer.create_circuit(constraint_system);
acir_composer.create_circuit(constraint_system, {}, true);
auto circuit_size = acir_composer.get_total_circuit_size();

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

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

// Attach a comma if we still circuit reports to generate
if (i != (constraint_systems.size() - 1)) {
Expand Down
191 changes: 153 additions & 38 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp

Large diffs are not rendered by default.

64 changes: 56 additions & 8 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,44 @@

namespace acir_format {

/**
* @brief Indices of the original opcode that originated each constraint in AcirFormat.
* @details Contains one array of indices per opcode type. The length of each array is equal to the number of
* constraints of that type. The relationship between the opcodes and constraints is assumed to be one to one, except
* for block constraints.
*/
struct AcirFormatOriginalOpcodeIndices {
std::vector<size_t> logic_constraints;
std::vector<size_t> range_constraints;
std::vector<size_t> aes128_constraints;
std::vector<size_t> sha256_constraints;
std::vector<size_t> sha256_compression;
std::vector<size_t> schnorr_constraints;
std::vector<size_t> ecdsa_k1_constraints;
std::vector<size_t> ecdsa_r1_constraints;
std::vector<size_t> blake2s_constraints;
std::vector<size_t> blake3_constraints;
std::vector<size_t> keccak_constraints;
std::vector<size_t> keccak_permutations;
std::vector<size_t> pedersen_constraints;
std::vector<size_t> pedersen_hash_constraints;
std::vector<size_t> poseidon2_constraints;
std::vector<size_t> multi_scalar_mul_constraints;
std::vector<size_t> ec_add_constraints;
std::vector<size_t> recursion_constraints;
std::vector<size_t> honk_recursion_constraints;
std::vector<size_t> bigint_from_le_bytes_constraints;
std::vector<size_t> bigint_to_le_bytes_constraints;
std::vector<size_t> bigint_operations;
std::vector<size_t> poly_triple_constraints;
std::vector<size_t> quad_constraints;
// Multiple opcode indices per block:
std::vector<std::vector<size_t>> block_constraints;

friend bool operator==(AcirFormatOriginalOpcodeIndices const& lhs,
AcirFormatOriginalOpcodeIndices const& rhs) = default;
};

struct AcirFormat {
// The number of witnesses in the circuit
uint32_t varnum;
Expand Down Expand Up @@ -72,6 +110,13 @@ struct AcirFormat {
quad_constraints;
std::vector<BlockConstraint> block_constraints;

// Number of gates added to the circuit per original opcode.
// Has length equal to num_acir_opcodes.
std::vector<size_t> gates_per_opcode = {};

// Indices of the original opcode that originated each constraint in AcirFormat.
AcirFormatOriginalOpcodeIndices original_opcode_indices;

// For serialization, update with any new fields
MSGPACK_FIELDS(varnum,
public_inputs,
Expand Down Expand Up @@ -142,18 +187,21 @@ struct AcirProgramStack {
};

template <typename Builder = bb::UltraCircuitBuilder>
Builder create_circuit(const AcirFormat& constraint_system,
Builder create_circuit(AcirFormat& constraint_system,
size_t size_hint = 0,
WitnessVector const& witness = {},
bool honk_recursion = false,
std::shared_ptr<bb::ECCOpQueue> op_queue = std::make_shared<bb::ECCOpQueue>());
std::shared_ptr<bb::ECCOpQueue> op_queue = std::make_shared<bb::ECCOpQueue>(),
bool collect_gates_per_opcode = false);

template <typename Builder>
void build_constraints(Builder& builder,
AcirFormat const& constraint_system,
bool has_valid_witness_assignments,
bool honk_recursion = false); // honk_recursion means we will honk to recursively verify this
// circuit. This distinction is needed to not add the default
// aggregation object when we're not using the honk RV.
void build_constraints(
Builder& builder,
AcirFormat& constraint_system,
bool has_valid_witness_assignments,
bool honk_recursion = false,
bool collect_gates_per_opcode = false); // honk_recursion means we will honk to recursively verify this
// circuit. This distinction is needed to not add the default
// aggregation object when we're not using the honk RV.

} // namespace acir_format
Loading

0 comments on commit a2f6876

Please sign in to comment.