mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:43 +00:00
[red-knot] Add very basic benchmark (#12182)
This commit is contained in:
parent
497fd4c505
commit
e2e0889a30
7 changed files with 197 additions and 3 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2053,6 +2053,9 @@ dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"red_knot",
|
||||||
|
"red_knot_module_resolver",
|
||||||
|
"ruff_db",
|
||||||
"ruff_linter",
|
"ruff_linter",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
"ruff_python_formatter",
|
"ruff_python_formatter",
|
||||||
|
|
|
@ -35,6 +35,7 @@ ruff_source_file = { path = "crates/ruff_source_file" }
|
||||||
ruff_text_size = { path = "crates/ruff_text_size" }
|
ruff_text_size = { path = "crates/ruff_text_size" }
|
||||||
ruff_workspace = { path = "crates/ruff_workspace" }
|
ruff_workspace = { path = "crates/ruff_workspace" }
|
||||||
|
|
||||||
|
red_knot = { path = "crates/red_knot" }
|
||||||
red_knot_module_resolver = { path = "crates/red_knot_module_resolver" }
|
red_knot_module_resolver = { path = "crates/red_knot_module_resolver" }
|
||||||
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
|
red_knot_python_semantic = { path = "crates/red_knot_python_semantic" }
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ impl Program {
|
||||||
self.with_db(|db| {
|
self.with_db(|db| {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for open_file in db.workspace.open_files() {
|
for open_file in db.workspace.open_files() {
|
||||||
result.extend_from_slice(&db.check_file(open_file));
|
result.extend_from_slice(&db.check_file_impl(open_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -19,7 +19,11 @@ impl Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
fn check_file(&self, file: VfsFile) -> Diagnostics {
|
pub fn check_file(&self, file: VfsFile) -> Result<Diagnostics, Cancelled> {
|
||||||
|
self.with_db(|db| db.check_file_impl(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_file_impl(&self, file: VfsFile) -> Diagnostics {
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
diagnostics.extend_from_slice(lint_syntax(self, file));
|
diagnostics.extend_from_slice(lint_syntax(self, file));
|
||||||
diagnostics.extend_from_slice(lint_semantic(self, file));
|
diagnostics.extend_from_slice(lint_semantic(self, file));
|
||||||
|
|
|
@ -31,6 +31,10 @@ harness = false
|
||||||
name = "formatter"
|
name = "formatter"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "red_knot"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
@ -41,11 +45,14 @@ criterion = { workspace = true, default-features = false }
|
||||||
codspeed-criterion-compat = { workspace = true, default-features = false, optional = true }
|
codspeed-criterion-compat = { workspace = true, default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
ruff_db = { workspace = true }
|
||||||
ruff_linter = { workspace = true }
|
ruff_linter = { workspace = true }
|
||||||
ruff_python_ast = { workspace = true }
|
ruff_python_ast = { workspace = true }
|
||||||
ruff_python_formatter = { workspace = true }
|
ruff_python_formatter = { workspace = true }
|
||||||
ruff_python_parser = { workspace = true }
|
ruff_python_parser = { workspace = true }
|
||||||
ruff_python_trivia = { workspace = true }
|
ruff_python_trivia = { workspace = true }
|
||||||
|
red_knot = { workspace = true }
|
||||||
|
red_knot_module_resolver = { workspace = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
178
crates/ruff_benchmark/benches/red_knot.rs
Normal file
178
crates/ruff_benchmark/benches/red_knot.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#![allow(clippy::disallowed_names)]
|
||||||
|
|
||||||
|
use red_knot::program::Program;
|
||||||
|
use red_knot::Workspace;
|
||||||
|
use red_knot_module_resolver::{set_module_resolution_settings, ModuleResolutionSettings};
|
||||||
|
use ruff_benchmark::criterion::{
|
||||||
|
criterion_group, criterion_main, BatchSize, Criterion, Throughput,
|
||||||
|
};
|
||||||
|
use ruff_db::file_system::{FileSystemPath, MemoryFileSystem};
|
||||||
|
use ruff_db::parsed::parsed_module;
|
||||||
|
use ruff_db::vfs::{system_path_to_file, VfsFile};
|
||||||
|
use ruff_db::Upcast;
|
||||||
|
|
||||||
|
static FOO_CODE: &str = r#"
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from bar import Bar
|
||||||
|
|
||||||
|
class Foo(Bar):
|
||||||
|
def foo() -> str:
|
||||||
|
return "foo"
|
||||||
|
|
||||||
|
@typing.override
|
||||||
|
def bar() -> str:
|
||||||
|
return "foo_bar"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
static BAR_CODE: &str = r#"
|
||||||
|
class Bar:
|
||||||
|
def bar() -> str:
|
||||||
|
return "bar"
|
||||||
|
|
||||||
|
def random(arg: int) -> int:
|
||||||
|
if arg == 1:
|
||||||
|
return 48472783
|
||||||
|
if arg < 10:
|
||||||
|
return 20
|
||||||
|
return 36673
|
||||||
|
"#;
|
||||||
|
|
||||||
|
static TYPING_CODE: &str = r#"
|
||||||
|
def override(): ...
|
||||||
|
"#;
|
||||||
|
|
||||||
|
struct Case {
|
||||||
|
program: Program,
|
||||||
|
fs: MemoryFileSystem,
|
||||||
|
foo: VfsFile,
|
||||||
|
bar: VfsFile,
|
||||||
|
typing: VfsFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_case() -> Case {
|
||||||
|
let fs = MemoryFileSystem::new();
|
||||||
|
let foo_path = FileSystemPath::new("/src/foo.py");
|
||||||
|
let bar_path = FileSystemPath::new("/src/bar.py");
|
||||||
|
let typing_path = FileSystemPath::new("/src/typing.pyi");
|
||||||
|
fs.write_files([
|
||||||
|
(foo_path, FOO_CODE),
|
||||||
|
(bar_path, BAR_CODE),
|
||||||
|
(typing_path, TYPING_CODE),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let workspace_root = FileSystemPath::new("/src");
|
||||||
|
let workspace = Workspace::new(workspace_root.to_path_buf());
|
||||||
|
|
||||||
|
let mut program = Program::new(workspace, fs.clone());
|
||||||
|
let foo = system_path_to_file(&program, foo_path).unwrap();
|
||||||
|
|
||||||
|
set_module_resolution_settings(
|
||||||
|
&mut program,
|
||||||
|
ModuleResolutionSettings {
|
||||||
|
extra_paths: vec![],
|
||||||
|
workspace_root: workspace_root.to_path_buf(),
|
||||||
|
site_packages: None,
|
||||||
|
custom_typeshed: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
program.workspace_mut().open_file(foo);
|
||||||
|
|
||||||
|
let bar = system_path_to_file(&program, bar_path).unwrap();
|
||||||
|
let typing = system_path_to_file(&program, typing_path).unwrap();
|
||||||
|
|
||||||
|
Case {
|
||||||
|
program,
|
||||||
|
fs,
|
||||||
|
foo,
|
||||||
|
bar,
|
||||||
|
typing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn benchmark_without_parse(criterion: &mut Criterion) {
|
||||||
|
let mut group = criterion.benchmark_group("red_knot/check_file");
|
||||||
|
group.throughput(Throughput::Bytes(FOO_CODE.len() as u64));
|
||||||
|
|
||||||
|
group.bench_function("red_knot_check_file[without_parse]", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
let case = setup_case();
|
||||||
|
// Pre-parse the module to only measure the semantic time.
|
||||||
|
parsed_module(case.program.upcast(), case.foo);
|
||||||
|
parsed_module(case.program.upcast(), case.bar);
|
||||||
|
parsed_module(case.program.upcast(), case.typing);
|
||||||
|
case
|
||||||
|
},
|
||||||
|
|case| {
|
||||||
|
let Case { program, foo, .. } = case;
|
||||||
|
let result = program.check_file(foo).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.as_slice(), [] as [String; 0]);
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn benchmark_incremental(criterion: &mut Criterion) {
|
||||||
|
let mut group = criterion.benchmark_group("red_knot/check_file");
|
||||||
|
group.throughput(Throughput::Bytes(FOO_CODE.len() as u64));
|
||||||
|
|
||||||
|
group.bench_function("red_knot_check_file[incremental]", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
let mut case = setup_case();
|
||||||
|
case.program.check_file(case.foo).unwrap();
|
||||||
|
|
||||||
|
case.fs
|
||||||
|
.write_file(
|
||||||
|
FileSystemPath::new("/src/foo.py"),
|
||||||
|
format!("{BAR_CODE}\n# A comment\n"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
case.bar.touch(&mut case.program);
|
||||||
|
case
|
||||||
|
},
|
||||||
|
|case| {
|
||||||
|
let Case { program, foo, .. } = case;
|
||||||
|
let result = program.check_file(foo).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.as_slice(), [] as [String; 0]);
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn benchmark_cold(criterion: &mut Criterion) {
|
||||||
|
let mut group = criterion.benchmark_group("red_knot/check_file");
|
||||||
|
group.throughput(Throughput::Bytes(FOO_CODE.len() as u64));
|
||||||
|
|
||||||
|
group.bench_function("red_knot_check_file[cold]", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
setup_case,
|
||||||
|
|case| {
|
||||||
|
let Case { program, foo, .. } = case;
|
||||||
|
let result = program.check_file(foo).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.as_slice(), [] as [String; 0]);
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(cold, benchmark_without_parse);
|
||||||
|
criterion_group!(without_parse, benchmark_cold);
|
||||||
|
criterion_group!(incremental, benchmark_incremental);
|
||||||
|
criterion_main!(without_parse, cold, incremental);
|
|
@ -19,6 +19,7 @@ use crate::file_system::{FileSystem, FileSystemPath, FileType, Metadata, Result}
|
||||||
/// Use a tempdir with the real file system to test these advanced file system features and complex file system behavior.
|
/// Use a tempdir with the real file system to test these advanced file system features and complex file system behavior.
|
||||||
///
|
///
|
||||||
/// Only intended for testing purposes.
|
/// Only intended for testing purposes.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct MemoryFileSystem {
|
pub struct MemoryFileSystem {
|
||||||
inner: Arc<MemoryFileSystemInner>,
|
inner: Arc<MemoryFileSystemInner>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::parsed::parsed_module;
|
||||||
use crate::source::{line_index, source_text};
|
use crate::source::{line_index, source_text};
|
||||||
use crate::vfs::{Vfs, VfsFile};
|
use crate::vfs::{Vfs, VfsFile};
|
||||||
|
|
||||||
mod file_revision;
|
pub mod file_revision;
|
||||||
pub mod file_system;
|
pub mod file_system;
|
||||||
pub mod parsed;
|
pub mod parsed;
|
||||||
pub mod source;
|
pub mod source;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue