-
Notifications
You must be signed in to change notification settings - Fork 161
Setup basic perf CI #417
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
Setup basic perf CI #417
Changes from all commits
e8cc677
460aade
ce8233d
4dbe150
9808df1
e3c1fa2
0060001
58fb5dc
d16e389
927f23a
5460d2b
430769d
baf562a
ee56a32
1f141cb
91490e8
199d257
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# CI for performance benchmarking | ||
# Domains of interest: | ||
# * startup speed (== to parse and load all rules) | ||
# * network filter matching (== the avg time to check a request) | ||
# * first request matching delay (== time to check the first request) | ||
# * memory usage after loading rules and after a few requests | ||
name: Performance CI | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
|
||
permissions: | ||
contents: write | ||
pages: write | ||
pull-requests: write | ||
|
||
jobs: | ||
benchmark: | ||
name: Performance benchmarking | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||
|
||
- name: Bench network filter matching | ||
run: cargo bench --bench bench_matching rule-match-browserlike/brave-list -- --output-format bencher | tee -a output.txt | ||
|
||
- name: Bench first request matching delay | ||
run: cargo bench --bench bench_matching rule-match-first-request -- --output-format bencher | tee -a output.txt | ||
|
||
- name: Bench startup speed | ||
run: cargo bench --bench bench_rules blocker_new/brave-list -- --output-format bencher | tee -a output.txt | ||
|
||
- name: Bench memory usage | ||
run: cargo bench --bench bench_memory -- --output-format bencher | tee -a output.txt | ||
|
||
- name: Store benchmark result | ||
uses: benchmark-action/github-action-benchmark@d48d326b4ca9ba73ca0cd0d59f108f9e02a381c7 # v1.20.4 | ||
with: | ||
name: Rust Benchmark | ||
tool: 'cargo' | ||
output-file-path: output.txt | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
alert-threshold: '130%' # fails on +30% regression | ||
comment-on-alert: true | ||
fail-on-alert: true | ||
comment-always: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/* Copyright (c) 2025 The Brave Authors. All rights reserved. | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
* You can obtain one at https://mozilla.org/MPL/2.0/. */ | ||
|
||
use criterion::*; | ||
use std::alloc::{GlobalAlloc, Layout, System}; | ||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use adblock::Engine; | ||
use adblock::request::Request; | ||
|
||
#[path = "../tests/test_utils.rs"] | ||
mod test_utils; | ||
use test_utils::rules_from_lists; | ||
|
||
// Custom allocator to track memory usage | ||
#[global_allocator] | ||
static ALLOCATOR: MemoryTracker = MemoryTracker::new(); | ||
|
||
struct MemoryTracker { | ||
allocated: AtomicUsize, | ||
internal: System, | ||
} | ||
|
||
impl MemoryTracker { | ||
const fn new() -> Self { | ||
Self { | ||
allocated: AtomicUsize::new(0), | ||
internal: System, | ||
} | ||
} | ||
|
||
fn current_usage(&self) -> usize { | ||
self.allocated.load(Ordering::SeqCst) | ||
} | ||
|
||
fn reset(&self) { | ||
self.allocated.store(0, Ordering::SeqCst); | ||
} | ||
} | ||
|
||
unsafe impl GlobalAlloc for MemoryTracker { | ||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||
let ret = self.internal.alloc(layout); | ||
if !ret.is_null() { | ||
self.allocated.fetch_add(layout.size(), Ordering::SeqCst); | ||
} | ||
ret | ||
} | ||
|
||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { | ||
self.internal.dealloc(ptr, layout); | ||
self.allocated.fetch_sub(layout.size(), Ordering::SeqCst); | ||
} | ||
|
||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { | ||
let ret = self.internal.realloc(ptr, layout, new_size); | ||
if !ret.is_null() { | ||
self.allocated.fetch_sub(layout.size(), Ordering::SeqCst); | ||
self.allocated.fetch_add(new_size, Ordering::SeqCst); | ||
} | ||
ret | ||
} | ||
|
||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { | ||
let ret = self.internal.alloc_zeroed(layout); | ||
if !ret.is_null() { | ||
self.allocated.fetch_add(layout.size(), Ordering::SeqCst); | ||
} | ||
ret | ||
} | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[derive(Serialize, Deserialize, Clone)] | ||
struct TestRequest { | ||
frameUrl: String, | ||
url: String, | ||
cpt: String, | ||
} | ||
|
||
impl From<&TestRequest> for Request { | ||
fn from(v: &TestRequest) -> Self { | ||
Request::new(&v.url, &v.frameUrl, &v.cpt).unwrap() | ||
} | ||
} | ||
|
||
fn load_requests() -> Vec<TestRequest> { | ||
let requests_str = rules_from_lists(&["data/requests.json"]); | ||
let reqs: Vec<TestRequest> = requests_str | ||
.into_iter() | ||
.map(|r| serde_json::from_str(&r)) | ||
.filter_map(Result::ok) | ||
.collect(); | ||
reqs | ||
} | ||
|
||
fn bench_memory_usage(c: &mut Criterion) { | ||
let mut group = c.benchmark_group("memory-usage"); | ||
group.sample_size(10); | ||
group.measurement_time(std::time::Duration::from_secs(1)); | ||
|
||
let mut noise = 0; | ||
let all_requests = load_requests(); | ||
let first_1000_requests: Vec<_> = all_requests.iter().take(1000).collect(); | ||
|
||
group.bench_function("brave-list-initial", |b| { | ||
let mut result = 0; | ||
b.iter_custom(|iters| { | ||
for _ in 0..iters { | ||
ALLOCATOR.reset(); | ||
let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]); | ||
let engine = Engine::from_rules(rules, Default::default()); | ||
|
||
noise += 1; // add some noise to make criterion happy | ||
result += ALLOCATOR.current_usage() + noise; | ||
|
||
// Prevent engine from being optimized | ||
criterion::black_box(&engine); | ||
} | ||
|
||
// Return the memory usage as a Duration | ||
std::time::Duration::from_nanos(result as u64) | ||
}); | ||
}); | ||
|
||
group.bench_function("brave-list-after-1000-requests", |b| { | ||
b.iter_custom(|iters| { | ||
let mut result = 0; | ||
for _ in 0..iters { | ||
ALLOCATOR.reset(); | ||
let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]); | ||
let engine = Engine::from_rules(rules, Default::default()); | ||
|
||
for request in first_1000_requests.clone() { | ||
criterion::black_box(engine.check_network_request(&request.into())); | ||
} | ||
|
||
noise += 1; // add some noise to make criterion happy | ||
result += ALLOCATOR.current_usage() + noise; | ||
|
||
// Prevent engine from being optimized | ||
criterion::black_box(&engine); | ||
} | ||
|
||
// Return the memory usage as a Duration | ||
std::time::Duration::from_nanos(result as u64) | ||
}) | ||
}); | ||
|
||
group.finish(); | ||
} | ||
|
||
criterion_group!(benches, bench_memory_usage); | ||
criterion_main!(benches); |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
const { execSync } = require("child_process"); | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
|
||
// Remove readline and use command line arguments | ||
const args = process.argv.slice(2); | ||
|
||
if (args.length < 2) { | ||
console.error( | ||
"Usage: node update-lists.js <Brave Services Key> <target version for brave list (i.e. 1.0.10268)>" | ||
); | ||
process.exit(1); | ||
} | ||
|
||
const apiKey = args[0]; | ||
const version = args[1]; | ||
|
||
const versionNumber = version.replace(/\./g, "_"); | ||
const extensionId = "iodkpdagapdfkphljnddpjlldadblomo"; | ||
|
||
execSync( | ||
"curl -o data/easylist.to/easylist/easylist.txt https://easylist.to/easylist/easylist.txt" | ||
); | ||
execSync( | ||
"curl -o data/easylist.to/easylist/easyprivacy.txt https://easylist.to/easylist/easyprivacy.txt" | ||
); | ||
execSync( | ||
"curl -o data/easylist.to/easylistgermany/easylistgermany.txt https://easylist.to/easylistgermany/easylistgermany.txt" | ||
); | ||
|
||
execSync( | ||
`curl -o extension.zip -H "BraveServiceKey: ${apiKey}" ` + | ||
`https://brave-core-ext.s3.brave.com/release/${extensionId}/extension_${versionNumber}.crx` | ||
); | ||
|
||
const tempDir = fs.mkdtempSync("temp-brave-list"); | ||
const listPath = path.join(tempDir, "list.txt"); | ||
try { | ||
execSync("unzip extension.zip -d " + tempDir); | ||
} catch (e) { | ||
if (!fs.existsSync(listPath)) { | ||
console.error("Failed to find list.txt in extension.zip"); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
execSync(`mv -f ${listPath} data/brave/brave-main-list.txt`); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,3 +14,6 @@ serde = { version = "1.0", features = ["derive", "rc"] } | |
serde_json = "1.0" | ||
adblock = { path = "../", features = ["css-validation", "content-blocking", "resource-assembler"] } | ||
neon = { version = "^0.10.1", default-features = false, features = ["napi-1"] } | ||
|
||
[features] | ||
default-panic-hook = [] | ||
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this does anything? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without it the build got red from time to time: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One of the changes in Rust 1.84 is rust-lang/rust#132577, so this is now a lint warning (and thus an error since we've configured lint warnings as errors). I think it's resolved in newer versions of Neon, but we can leave it here as-is for now. |
Uh oh!
There was an error while loading. Please reload this page.