Skip to content

Commit 3176007

Browse files
authored
Merge pull request #239 from Peter554/better-benchmark-for-find-shortest-chains
Add more useful benchmark for find_shortest_chains
2 parents cb524b1 + 099dbe3 commit 3176007

5 files changed

Lines changed: 45 additions & 11 deletions

File tree

.github/workflows/main.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ jobs:
4646
- uses: actions-rs/toolchain@v1
4747
with:
4848
toolchain: stable
49+
- name: Print versions
50+
run: cargo version --verbose && cargo clippy --version
4951
- name: Run tests
5052
run: cargo test --no-default-features
5153
working-directory: ./rust
@@ -81,4 +83,4 @@ jobs:
8183
with:
8284
token: ${{ secrets.CODSPEED_TOKEN }}
8385
run: |
84-
uv run pytest tests/benchmarking/ --codspeed
86+
uv run pytest tests/benchmarking/ --codspeed

rust/src/filesystem.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,11 @@ impl FileSystem for RealBasicFileSystem {
8787

8888
// Coding specification needs to be in the first two lines, or it's ignored.
8989
for line in s.lines().take(2) {
90-
if let Some(captures) = encoding_re.captures(line) {
91-
if let Some(encoding_name) = captures.get(1) {
90+
if let Some(captures) = encoding_re.captures(line)
91+
&& let Some(encoding_name) = captures.get(1) {
9292
detected_encoding = Some(encoding_name.as_str().to_string());
9393
break;
9494
}
95-
}
9695
}
9796

9897
if let Some(enc_name) = detected_encoding {

rust/src/graph/hierarchy_queries.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl Graph {
1717
}
1818

1919
// TODO(peter) Guarantee order?
20-
pub fn all_modules(&self) -> impl ModuleIterator {
20+
pub fn all_modules(&self) -> impl ModuleIterator<'_> {
2121
self.modules.values()
2222
}
2323

@@ -28,7 +28,7 @@ impl Graph {
2828
}
2929
}
3030

31-
pub fn get_module_children(&self, module: ModuleToken) -> impl ModuleIterator {
31+
pub fn get_module_children(&self, module: ModuleToken) -> impl ModuleIterator<'_> {
3232
let children = match self.module_children.get(module) {
3333
Some(children) => children
3434
.iter()
@@ -42,7 +42,7 @@ impl Graph {
4242
/// Returns an iterator over the passed modules descendants.
4343
///
4444
/// Parent modules will be yielded before their child modules.
45-
pub fn get_module_descendants(&self, module: ModuleToken) -> impl ModuleIterator {
45+
pub fn get_module_descendants(&self, module: ModuleToken) -> impl ModuleIterator<'_> {
4646
let mut descendants = self.get_module_children(module).collect::<Vec<_>>();
4747
for child in descendants.clone() {
4848
descendants.extend(self.get_module_descendants(child.token).collect::<Vec<_>>())
@@ -53,7 +53,7 @@ impl Graph {
5353
pub fn find_matching_modules(
5454
&self,
5555
expression: &ModuleExpression,
56-
) -> impl ModuleIterator + use<'_> {
56+
) -> impl ModuleIterator<'_> + use<'_> {
5757
let interner = MODULE_NAMES.read().unwrap();
5858
let modules: FxHashSet<_> = self
5959
.modules

rust/src/import_scanning.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ fn scan_for_imports_no_py_single_module(
140140
}
141141
None => {
142142
// It's an external import.
143-
if include_external_packages {
144-
if let Some(imported_module) =
143+
if include_external_packages
144+
&& let Some(imported_module) =
145145
_distill_external_module(&imported_object_name, found_packages)
146146
{
147147
imports.insert(DirectImport {
@@ -151,7 +151,6 @@ fn scan_for_imports_no_py_single_module(
151151
line_contents: imported_object.line_contents,
152152
});
153153
}
154-
}
155154
}
156155
}
157156
}

tests/benchmarking/test_benchmarking.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,40 @@ def test_no_chains(self, large_graph, benchmark):
464464
)
465465
assert result == set()
466466

467+
def test_chains_found_sparse_imports(self, benchmark):
468+
"""
469+
A test case where the number of import chains is significantly less
470+
than the number of module combinations. In this case package `a` has
471+
100 modules and package `c` has 100 modules, however the number of import
472+
chains between them is only 10.
473+
474+
This case of "sparse" imports is typical of realistic scenarios, where there are
475+
large packages and only a small handful of contract violations.
476+
477+
This test case is designed to expose weaknesses of naive O(N^2) algorithms
478+
which compute chains by pathfinding between every pair of modules.
479+
"""
480+
graph = ImportGraph()
481+
graph.add_module("a")
482+
graph.add_module("b")
483+
graph.add_module("c")
484+
for i in range(100):
485+
graph.add_module(f"a.m{i}")
486+
graph.add_module(f"b.m{i}")
487+
graph.add_module(f"c.m{i}")
488+
if i % 10 == 0:
489+
graph.add_import(importer=f"a.m{i}", imported=f"b.m{i}")
490+
graph.add_import(importer=f"b.m{i}", imported=f"c.m{i}")
491+
492+
result = _run_benchmark(
493+
benchmark,
494+
graph.find_shortest_chains,
495+
"a",
496+
"c",
497+
as_packages=True,
498+
)
499+
assert len(result) == 10
500+
467501

468502
def test_copy_graph(large_graph, benchmark):
469503
_run_benchmark(benchmark, lambda: deepcopy(large_graph))

0 commit comments

Comments
 (0)