Skip to content

Commit f0ae53b

Browse files
beta-zilianigithub-actionsggiraldez
authored
Benchmarking TS (#1325)
(This is #1287 but in a local branch to test bencher, and with some commits's author fixed) This PR introduces the following infra commands: - `infra perf cargo`: this is the `infra perf` command from before (flamegraphs). - ~`infra perf fetch`: fetches a series of projects from solcify and stores them in a specified local folder.~ - `infra perf npm`: runs comparisons of our TS library with solc and (currently) solc-typed-ast, and ships them to bencher. The comparisons are run twice, one cold and one hot. Details: This is a revamp of https://github.com/NomicFoundation/slang/tree/perf-ts with several improvements: - [x] ~Uses Benny instead of Jest.~ Removed Benny - [x] Count only the hot path. - [x] Moved to `solidity/testing/perf` - [x] Improved solc interface to be capable of executing the test in any file. - [x] Integration with `infra` script. - [x] ~Add more examples by running on every file in certain projects (e.g., openzeppelin's)~ - [x] Collect specific measures for slang (time for setup, time to resolve each definition, etc). - [x] ~Add other examples specific for slang.~ Won't fix. - [x] ~Add memory test.~ Won't do now. - [x] Load to Bencher. The current architecture for the tests is as follow: ```mermaid sequenceDiagram box sienna Rust participant infra participant fetch participant perf/npm-benches end box olive External participant sourcify participant bencher end box midnightblue TS participant npm/main end infra->>perf/npm-benches: run perf/npm-benches->>fetch: for every project fetch->>sourcify: get project sourcify->>fetch: persist in FS (JSON) perf/npm-benches->>npm/main: for every project npm/main-->>perf/npm-benches: ouptut comparison in console (JSON) perf/npm-benches-->>infra: BMF infra->>bencher: send results ``` --------- Co-authored-by: github-actions <github-actions@users.noreply.github.com> Co-authored-by: Gustavo Giráldez <ggiraldez@manas.tech>
1 parent fcd423f commit f0ae53b

File tree

25 files changed

+3225
-896
lines changed

25 files changed

+3225
-896
lines changed
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: "benchmark"
1+
name: "benchmark_cargo"
22

33
on:
44
# Run using manual triggers from GitHub UI:
@@ -11,6 +11,11 @@ on:
1111
required: true
1212
default: true
1313

14+
bencherProject:
15+
description: "Bencher project:"
16+
type: "string"
17+
default: "slang-dashboard-cargo"
18+
1419
# Run on pushes to 'main' branch:
1520
push:
1621
branches:
@@ -35,11 +40,15 @@ jobs:
3540
- name: "Restore Cache"
3641
uses: "./.github/actions/cache/restore"
3742

43+
- name: "Set bencher project"
44+
run: |
45+
echo "SLANG_BENCHER_PROJECT=${{ inputs.bencherProject }}" >> $GITHUB_ENV
46+
3847
- name: "infra setup"
3948
run: "./scripts/bin/infra setup"
4049

41-
- name: "infra perf benchmark"
42-
run: "./scripts/bin/infra perf benchmark ${{ inputs.dryRun == true && '--dry-run' || '' }}"
50+
- name: "infra perf cargo"
51+
run: "./scripts/bin/infra perf cargo ${{ inputs.dryRun == true && '--dry-run' || '' }}"
4352
env:
4453
BENCHER_API_TOKEN: "${{ secrets.BENCHER_API_TOKEN }}"
4554

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: "benchmark_npm"
2+
3+
on:
4+
# Run using manual triggers from GitHub UI:
5+
# https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow
6+
workflow_dispatch:
7+
inputs:
8+
dryRun:
9+
description: "Attempt a local run and report results here, without updating the bencher dashboard."
10+
type: "boolean"
11+
required: true
12+
default: true
13+
14+
bencherProject:
15+
description: "Bencher project:"
16+
type: "string"
17+
default: "slang-dashboard-npm"
18+
19+
# Run on pushes to 'main' branch:
20+
push:
21+
branches:
22+
- "main"
23+
24+
# Queue up benchmark workflows for the same branch, so that results are reported in order:
25+
concurrency:
26+
group: "${{ github.workflow }}-${{ github.ref_name }}"
27+
cancel-in-progress: false
28+
29+
jobs:
30+
benchmark:
31+
runs-on: "ubuntu-22.04" # _SLANG_DEV_CONTAINER_BASE_IMAGE_ (keep in sync)
32+
33+
# Only run on the main repo (not forks), unless it is a dry run:
34+
if: "${{ github.repository == 'NomicFoundation/slang' || inputs.dryRun == true }}"
35+
36+
steps:
37+
- name: "Checkout Repository"
38+
uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683"
39+
40+
- name: "Restore Cache"
41+
uses: "./.github/actions/cache/restore"
42+
43+
- name: "Set bencher project"
44+
run: |
45+
echo "SLANG_BENCHER_PROJECT=${{ inputs.bencherProject }}" >> $GITHUB_ENV
46+
47+
- name: "infra setup"
48+
run: "./scripts/bin/infra setup"
49+
50+
- name: "infra check npm"
51+
run: "./scripts/bin/infra check npm"
52+
53+
- name: "infra perf npm"
54+
run: "./scripts/bin/infra perf npm ${{ inputs.dryRun == true && '--dry-run' || '' }}"
55+
env:
56+
BENCHER_API_TOKEN: "${{ secrets.BENCHER_API_TOKEN }}"

Cargo.toml

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,23 @@ metaslang_bindings = { path = "crates/metaslang/bindings", version = "1.2.1" }
6464
metaslang_graph_builder = { path = "crates/metaslang/graph_builder", version = "1.2.1" }
6565
metaslang_cst = { path = "crates/metaslang/cst", version = "1.2.1" }
6666

67-
slang_solidity = { path = "crates/solidity/outputs/cargo/crate", version = "1.2.1" }
68-
slang_solidity_cli = { path = "crates/solidity/outputs/cargo/cli", version = "1.2.1" }
69-
solidity_cargo_tests = { path = "crates/solidity/outputs/cargo/tests", version = "1.2.1" }
70-
solidity_cargo_wasm = { path = "crates/solidity/outputs/cargo/wasm", version = "1.2.1" }
71-
solidity_language = { path = "crates/solidity/inputs/language", version = "1.2.1" }
72-
solidity_npm_package = { path = "crates/solidity/outputs/npm/package", version = "1.2.1" }
73-
solidity_spec = { path = "crates/solidity/outputs/spec", version = "1.2.1" }
74-
solidity_testing_perf = { path = "crates/solidity/testing/perf", version = "1.2.1" }
75-
solidity_testing_snapshots = { path = "crates/solidity/testing/snapshots", version = "1.2.1" }
76-
solidity_testing_solc = { path = "crates/solidity/testing/solc", version = "1.2.1" }
77-
solidity_testing_sourcify = { path = "crates/solidity/testing/sourcify", version = "1.2.1" }
67+
slang_solidity = { path = "crates/solidity/outputs/cargo/crate", version = "1.2.0" }
68+
slang_solidity_cli = { path = "crates/solidity/outputs/cargo/cli", version = "1.2.0" }
69+
solidity_cargo_tests = { path = "crates/solidity/outputs/cargo/tests", version = "1.2.0" }
70+
solidity_cargo_wasm = { path = "crates/solidity/outputs/cargo/wasm", version = "1.2.0" }
71+
solidity_language = { path = "crates/solidity/inputs/language", version = "1.2.0" }
72+
solidity_npm_package = { path = "crates/solidity/outputs/npm/package", version = "1.2.0" }
73+
solidity_spec = { path = "crates/solidity/outputs/spec", version = "1.2.0" }
74+
solidity_testing_perf = { path = "crates/solidity/testing/perf", version = "1.2.0" }
75+
solidity_testing_snapshots = { path = "crates/solidity/testing/snapshots", version = "1.2.0" }
76+
solidity_testing_solc = { path = "crates/solidity/testing/solc", version = "1.2.0" }
77+
solidity_testing_sourcify = { path = "crates/solidity/testing/sourcify", version = "1.2.0" }
78+
79+
slang_testlang = { path = "crates/testlang/outputs/cargo/crate", version = "1.2.0" }
80+
testlang_cargo_tests = { path = "crates/testlang/outputs/cargo/tests", version = "1.2.0" }
81+
testlang_cargo_wasm = { path = "crates/testlang/outputs/cargo/wasm", version = "1.2.0" }
82+
testlang_language = { path = "crates/testlang/inputs/language", version = "1.2.0" }
83+
testlang_npm_package = { path = "crates/testlang/outputs/npm/package", version = "1.2.0" }
7884

7985
#
8086
# External

crates/infra/cli/src/commands/perf/benchmark/mod.rs renamed to crates/infra/cli/src/commands/perf/cargo/mod.rs

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,16 @@ use infra_utils::commands::Command;
77
use infra_utils::github::GitHub;
88
use infra_utils::paths::PathExtensions;
99

10+
use crate::toolchains::bencher::run_bench;
1011
use crate::utils::DryRun;
1112

12-
// Source: https://github.com/bencherdev/bencher/blob/aa31a002842cfb0da9d6c60569396fc5261f5111/tasks/test_api/src/task/test/smoke_test.rs#L20
13-
const BENCHER_TEST_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhcGlfa2V5IiwiZXhwIjo1OTkzNjQyMTU2LCJpYXQiOjE2OTg2NzQ4NjEsImlzcyI6Imh0dHBzOi8vZGV2ZWwtLWJlbmNoZXIubmV0bGlmeS5hcHAvIiwic3ViIjoibXVyaWVsLmJhZ2dlQG5vd2hlcmUuY29tIiwib3JnIjpudWxsfQ.9z7jmM53TcVzc1inDxTeX9_OR0PQPpZAsKsCE7lWHfo";
14-
1513
#[derive(Clone, Debug, Parser)]
16-
pub struct BenchmarkController {
14+
pub struct CargoController {
1715
#[command(flatten)]
1816
dry_run: DryRun,
1917
}
2018

21-
impl BenchmarkController {
19+
impl CargoController {
2220
pub fn execute(&self) -> Result<()> {
2321
Self::install_valgrind();
2422

@@ -70,49 +68,16 @@ impl BenchmarkController {
7068
}
7169

7270
fn run_iai_bench(&self, package_name: &str, bench_name: &str) {
73-
let dry_run = self.dry_run.get();
74-
75-
let token = if dry_run {
76-
// Use a dummy test token for dry runs:
77-
// https://github.com/bencherdev/bencher/issues/468
78-
BENCHER_TEST_TOKEN.to_string()
79-
} else {
80-
std::env::var("BENCHER_API_TOKEN").expect(
81-
"BENCHER_API_TOKEN is not set. Either perform a '--dry-run', or set it to your Bencher API token: https://bencher.dev/console"
82-
)
83-
};
84-
8571
let test_runner = format!("cargo bench --package {package_name} --bench {bench_name}");
8672

87-
let testbed = if GitHub::is_running_in_ci() {
88-
"ci"
89-
} else {
90-
"dev"
91-
};
92-
93-
let mut command = Command::new("bencher")
94-
.arg("run")
95-
.property("--project", "slang")
96-
.property("--adapter", "rust_iai_callgrind")
97-
.property("--testbed", testbed)
98-
.secret("BENCHER_API_TOKEN", token);
99-
100-
if dry_run {
101-
command = command.flag("--dry-run");
102-
}
103-
104-
// Has to be the last argument:
105-
command.arg(test_runner).run();
73+
run_bench(self.dry_run.get(), "rust_iai_callgrind", &test_runner);
10674

10775
let reports_dir = Path::repo_path("target/iai")
10876
.join(package_name)
10977
.join(bench_name);
11078

11179
println!("
11280
113-
Bencher Run is complete...
114-
Test Results: [https://bencher.dev/console/projects/slang/reports]
115-
11681
Reports/Logs: {reports_dir:?}
11782
- Callgrind flamegraphs (callgrind.*.svg) can be viewed directly in the browser.
11883
- DHAT traces (dhat.*.out) can be viewed using the [dhat/dh_view.html] tool from the Valgrind release [https://valgrind.org/downloads/].
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
mod benchmark;
1+
mod cargo;
2+
mod npm;
23

34
use anyhow::Result;
45
use clap::{Parser, Subcommand};
56

6-
use crate::commands::perf::benchmark::BenchmarkController;
7+
use crate::commands::perf::cargo::CargoController;
8+
use crate::commands::perf::npm::NpmController;
79

810
#[derive(Clone, Debug, Parser)]
911
pub struct PerfController {
@@ -13,14 +15,17 @@ pub struct PerfController {
1315

1416
#[derive(Clone, Debug, Subcommand)]
1517
enum PerfCommand {
16-
/// Run benchmark tests, and report the results to <https://bencher.dev/console>
17-
Benchmark(BenchmarkController),
18+
/// Run benchmark tests for the rust api, and report the results to <https://bencher.dev/console>
19+
Cargo(CargoController),
20+
/// Run benchmark tests for the typescript api, and report the results to <https://bencher.dev/console>
21+
Npm(NpmController),
1822
}
1923

2024
impl PerfController {
2125
pub fn execute(&self) -> Result<()> {
2226
match &self.command {
23-
PerfCommand::Benchmark(controller) => controller.execute(),
27+
PerfCommand::Cargo(controller) => controller.execute(),
28+
PerfCommand::Npm(controller) => controller.execute(),
2429
}
2530
}
2631
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use anyhow::Result;
2+
use clap::Parser;
3+
use infra_utils::cargo::CargoWorkspace;
4+
5+
use crate::toolchains::bencher::run_bench;
6+
use crate::utils::DryRun;
7+
8+
#[derive(Clone, Debug, Parser)]
9+
pub struct NpmController {
10+
#[arg(short, long, default_value_t = String::from(".*"))]
11+
/// A regex pattern to select which project(s) to run
12+
pattern: String,
13+
14+
#[command(flatten)]
15+
dry_run: DryRun,
16+
17+
#[arg(long, default_value_t = 2)]
18+
/// The number of cold runs
19+
cold: usize,
20+
21+
#[arg(long, default_value_t = 5)]
22+
/// The number of hot runs
23+
hot: usize,
24+
}
25+
26+
impl NpmController {
27+
fn install_deps() -> Result<()> {
28+
CargoWorkspace::install_binary("bencher_cli")?;
29+
Ok(())
30+
}
31+
32+
fn execute_npm_benchmarks(&self) {
33+
let test_runner = format!(
34+
"cargo run --package {package} -- --pattern=\"{pattern}\" --cold={cold} --hot={hot}",
35+
package = "solidity_testing_perf_npm",
36+
pattern = &self.pattern,
37+
cold = &self.cold,
38+
hot = &self.hot,
39+
);
40+
41+
run_bench(self.dry_run.get(), "json", &test_runner);
42+
}
43+
44+
#[allow(clippy::unnecessary_wraps)]
45+
pub fn execute(&self) -> Result<()> {
46+
Self::install_deps()?;
47+
48+
self.execute_npm_benchmarks();
49+
Ok(())
50+
}
51+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use infra_utils::commands::Command;
2+
use infra_utils::github::GitHub;
3+
4+
// Use a dummy test token for dry runs:
5+
// https://github.com/bencherdev/bencher/issues/468
6+
// Source: https://github.com/bencherdev/bencher/blob/aa31a002842cfb0da9d6c60569396fc5261f5111/tasks/test_api/src/task/test/smoke_test.rs#L20
7+
const BENCHER_TEST_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhcGlfa2V5IiwiZXhwIjo1OTkzNjQyMTU2LCJpYXQiOjE2OTg2NzQ4NjEsImlzcyI6Imh0dHBzOi8vZGV2ZWwtLWJlbmNoZXIubmV0bGlmeS5hcHAvIiwic3ViIjoibXVyaWVsLmJhZ2dlQG5vd2hlcmUuY29tIiwib3JnIjpudWxsfQ.9z7jmM53TcVzc1inDxTeX9_OR0PQPpZAsKsCE7lWHfo";
8+
9+
// Default bencher project. Can be overriden with the env var SLANG_BENCHER_PROJECT
10+
const BENCHER_PROJECT: &str = "slang-extended-dashboard";
11+
12+
fn get_bencher_project() -> String {
13+
std::env::var("SLANG_BENCHER_PROJECT").unwrap_or(String::from(BENCHER_PROJECT))
14+
}
15+
16+
pub(crate) fn run_bench(dry_run: bool, adapter: &str, test_runner: &str) {
17+
let token = if dry_run {
18+
BENCHER_TEST_TOKEN.to_string()
19+
} else {
20+
std::env::var("BENCHER_API_TOKEN").expect(
21+
"BENCHER_API_TOKEN is not set. Either perform a '--dry-run', or set it to your Bencher API token: https://bencher.dev/console"
22+
)
23+
};
24+
25+
let testbed = if GitHub::is_running_in_ci() {
26+
"ci"
27+
} else {
28+
"dev"
29+
};
30+
31+
let project = get_bencher_project();
32+
33+
let mut command = Command::new("bencher")
34+
.arg("run")
35+
.property("--project", &project)
36+
.property("--adapter", adapter)
37+
.property("--testbed", testbed)
38+
.secret("BENCHER_API_TOKEN", token);
39+
40+
if dry_run {
41+
command = command.flag("--dry-run");
42+
}
43+
44+
// Has to be the last argument:
45+
command.arg(test_runner).run();
46+
47+
println!(
48+
"
49+
50+
Bencher Run is complete...
51+
Test Results: [https://bencher.dev/console/projects/{project}/reports]
52+
"
53+
);
54+
}

crates/infra/cli/src/toolchains/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod bencher;
12
pub mod mkdocs;
23
pub mod npm;
34
pub mod pipenv;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "solidity_testing_perf_npm"
3+
version.workspace = true
4+
rust-version.workspace = true
5+
edition.workspace = true
6+
publish = false
7+
8+
[dependencies]
9+
serde = { workspace = true }
10+
serde_json = { workspace = true }
11+
infra_utils = { workspace = true }
12+
anyhow = { workspace = true }
13+
regex = { workspace = true }
14+
clap = { workspace = true }
15+
reqwest = { workspace = true, features = ["json"] }
16+
strum = { workspace = true }
17+
strum_macros = { workspace = true }
18+
19+
[lints]
20+
workspace = true
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@slang-private/solidity-testing-perf-npm",
3+
"private": true,
4+
"type": "module",
5+
"dependencies": {
6+
"@nomicfoundation/slang": "file:../../../outputs/npm/package/",
7+
"@openzeppelin/contracts": "^5.2.0",
8+
"@solidity-parser/parser": "^0.20.1",
9+
"command-line-args": "^6.0.1",
10+
"command-line-usage": "^7.0.3",
11+
"solc": "^0.8.29"
12+
},
13+
"devDependencies": {
14+
"@types/command-line-args": "^5.2.3",
15+
"@types/command-line-usage": "^5.0.4",
16+
"@types/node": "^22.15.17"
17+
}
18+
}

0 commit comments

Comments
 (0)