Skip to content

Commit

Permalink
feat: Add separate report for public function bytecode sizes (#8750)
Browse files Browse the repository at this point in the history
Re-use https://github.com/noir-lang/noir-gates-diff for computing
bytecode sizes of all public functions. I made a new script
`noir-projects/noir-contracts/publicFunctionsSizeReport.js`. It runs
through all the artifacts in the `target` directory of `noir-contracts`
and collects all public functions. We then compare a report of bytecode
sizes based upon the package name of
`{contract_name}::{public_function_name}`.

To see an example of the sticky comment after a public function change
you can look at
#8755.
  • Loading branch information
vezenovm authored Sep 25, 2024
1 parent 9321cbc commit d3c102f
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,47 @@ jobs:
delete: ${{ !steps.gates_diff.outputs.markdown }}
message: ${{ steps.gates_diff.outputs.markdown }}

public-functions-size-report:
needs: [build-images, changes]
if: needs.changes.outputs.non-docs == 'true' && needs.changes.outputs.non-misc-ci == 'true'
runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
with: { ref: "${{ env.GIT_COMMIT }}" }
# Only allow one memory-hunger prover test to use this runner
- uses: ./.github/ci-setup-action
with:
concurrency_key: public-functions-size-report-x86
- name: "Aztec Public Functions Bytecode Size Report"
working-directory: ./noir-projects/
timeout-minutes: 40
run: |
earthly-ci \
--artifact +public-functions-report/public_functions_report.json
mv public_functions_report.json ../public_functions_report.json
- name: Compare public functions bytecode size reports
id: public_functions_sizes_diff
uses: noir-lang/noir-gates-diff@ef8aaf48fb833f3b6e3f91665bb23afb7e68c6e3
with:
report: public_functions_report.json
header: |
# Changes to public function bytecode sizes
brillig_report: true
brillig_report_bytes: true
summaryQuantile: 0 # Display any diff in bytecode size count

- name: Add bytecode size diff to sticky comment
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: public_functions_size
# delete the comment in case changes no longer impact circuit sizes
delete: ${{ !steps.public_functions_sizes_diff.outputs.markdown }}
message: ${{ steps.public_functions_sizes_diff.outputs.markdown }}

merge-check:
runs-on: ubuntu-20.04
needs:
Expand Down
9 changes: 9 additions & 0 deletions noir-projects/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,12 @@ gates-report:
RUN ./gates_report.sh

SAVE ARTIFACT ./noir-protocol-circuits/gates_report.json gates_report.json

public-functions-report:
FROM +build-contracts
WORKDIR /usr/src/noir-projects

RUN cd noir-contracts && node publicFunctionsSizeReport.js ./target/

SAVE ARTIFACT ./noir-contracts/public_functions_report.json public_functions_report.json

73 changes: 73 additions & 0 deletions noir-projects/noir-contracts/publicFunctionsSizeReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const path = require("path");
const fs = require("fs");
const fsp = require("fs").promises;

// Simple script to extract the exact bytecode size of a contracts public functions.
// The output of this script is meant to be used with the noir-lang/noir-gates-diff project.
// The noir-gates-diff was made for comparing ACIR/Brillig opcodes.
// However, this script was made to re-use the noir-gates-diff for bytecode sizes as
// to minimize both the amount of changes needed in noir-gates-diff and in nargo.
async function main() {

let [targetDir] = process.argv.slice(2);
if (!targetDir) {
console.log(
"Usage: node extractPublicFunctionsAsNoirArtifacts.js <targetDir>"
);
return;
}

let artifactPaths = [];
fs.readdirSync(targetDir).forEach(file => {
// We want to exclude any backups that may be generated by the avm-transpiler
if (path.extname(file) === ".json") {
artifactPaths.push(path.join(targetDir, file));
}
});

let workspaceReport = {
programs: []
}
for (var i = 0; i < artifactPaths.length; i++) {
let contractArtifactPath = artifactPaths[i];
const contractArtifact = JSON.parse(
await fsp.readFile(contractArtifactPath, "utf8")
);
contractArtifact.functions.forEach(async func => {
if (func.custom_attributes.includes("public")) {
if (func.brillig_names.length != 1) {
console.log(
"Expected only a single Brillig function"
);
return;
}
let func_with_contract_name = contractArtifact.name + "::" + func.brillig_names[0];
let program_report = {
package_name: "",
functions: [{ name: "main", opcodes: 1 }],
unconstrained_functions: [],
}
// Programs are compared by package name, so we make a unique one for each function here
program_report.package_name = func_with_contract_name;
let bytecode_bytes = Buffer.from(func.bytecode, 'base64');
let func_report = {
name: "main",
opcodes: bytecode_bytes,
};
func_report.opcodes = bytecode_bytes.length;
program_report.unconstrained_functions.push(func_report);
workspaceReport.programs.push(program_report);
}
});
}

const outPath = path.join("public_functions_report.json");

console.log(`Writing to ${outPath}`);
await fsp.writeFile(outPath, JSON.stringify(workspaceReport, null, 2));
}

main().catch((err) => {
console.error(err);
process.exit(1);
});

0 comments on commit d3c102f

Please sign in to comment.