diff --git a/.gitignore b/.gitignore index 8f4eafd88b..9082f5d7cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Local cache .cache +resources/test +!resources/test/src ### # Rust.gitignore diff --git a/Cargo.lock b/Cargo.lock index 8fda27dc82..39858cc62d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1529,6 +1529,7 @@ dependencies = [ "clearscreen", "colored", "fern", + "lazy_static", "log", "notify", "pyo3", diff --git a/Cargo.toml b/Cargo.toml index eed3799363..d99fa044e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ clap = { version = "3.2.16", features = ["derive"] } clearscreen = { version = "1.0.10" } colored = { version = "2.0.0" } fern = { version = "0.6.1" } +lazy_static = { version = "1.4.0" } log = { version = "0.4.17" } notify = { version = "4.0.17" } pyo3 = { version = "0.16.5", features = ["extension-module", "abi3-py37"] } @@ -25,3 +26,7 @@ rustpython-parser = { git = "https://github.com/RustPython/RustPython.git", rev serde = { version = "1.0.143", features = ["derive"] } serde_json = { version = "1.0.83" } walkdir = { version = "2.3.2" } + +[profile.release] +lto = true +panic = "abort" diff --git a/README.md b/README.md index a474af6fe5..1d8edf57d8 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,31 @@ cargo run resources/test/src maturin publish --skip-existing --target x86_64-apple-darwin maturin publish --skip-existing --target aarch64-apple-darwin ``` + +## Benchmarking + +First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase, +which makes it a good target for benchmarking. Note that we clone v3.9, as `RustPython` doesn't yet +support pattern matching, which was introduced in v3.10. + +```shell +git clone --branch 3.9 https://github.com/python/cpython.git resources/test/cpython +``` + +Next, to benchmark the release build: + +```shell +cargo build --release + +hyperfine --warmup 5 \ + "./target/release/rust_python_linter ./resources/test/cpython/ --no-cache" \ + "./target/release/rust_python_linter ./resources/test/cpython/" + +Benchmark 1: ./target/release/rust_python_linter ./resources/test/cpython/ --no-cache + Time (mean ± σ): 353.6 ms ± 7.6 ms [User: 2868.8 ms, System: 171.5 ms] + Range (min … max): 344.4 ms … 367.3 ms 10 runs + +Benchmark 2: ./target/release/rust_python_linter ./resources/test/cpython/ + Time (mean ± σ): 59.6 ms ± 2.5 ms [User: 36.4 ms, System: 345.6 ms] + Range (min … max): 55.9 ms … 67.0 ms 48 runs +``` diff --git a/src/fs.rs b/src/fs.rs index 09d5eb0769..8407e40a58 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,10 +1,63 @@ +use anyhow::Result; +use lazy_static::lazy_static; +use std::collections::HashSet; use std::fs::File; use std::io::{BufRead, BufReader, Read}; +use std::ops::Deref; use std::path::{Path, PathBuf}; - -use anyhow::Result; use walkdir::{DirEntry, WalkDir}; +lazy_static! { + // TODO(charlie): Make these configurable. + static ref EXCLUDES: HashSet<&'static str> = vec![ + "resources/test/cpython/Lib/ctypes/test/test_numbers.py", + "resources/test/cpython/Lib/dataclasses.py", + "resources/test/cpython/Lib/lib2to3/tests/data/bom.py", + "resources/test/cpython/Lib/lib2to3/tests/data/crlf.py", + "resources/test/cpython/Lib/lib2to3/tests/data/different_encoding.py", + "resources/test/cpython/Lib/lib2to3/tests/data/false_encoding.py", + "resources/test/cpython/Lib/lib2to3/tests/data/py2_test_grammar.py", + "resources/test/cpython/Lib/sqlite3/test/factory.py", + "resources/test/cpython/Lib/sqlite3/test/hooks.py", + "resources/test/cpython/Lib/sqlite3/test/regression.py", + "resources/test/cpython/Lib/sqlite3/test/transactions.py", + "resources/test/cpython/Lib/sqlite3/test/types.py", + "resources/test/cpython/Lib/test/bad_coding2.py", + "resources/test/cpython/Lib/test/badsyntax_3131.py", + "resources/test/cpython/Lib/test/badsyntax_pep3120.py", + "resources/test/cpython/Lib/test/encoded_modules/module_iso_8859_1.py", + "resources/test/cpython/Lib/test/encoded_modules/module_koi8_r.py", + "resources/test/cpython/Lib/test/sortperf.py", + "resources/test/cpython/Lib/test/test_email/torture_test.py", + "resources/test/cpython/Lib/test/test_fstring.py", + "resources/test/cpython/Lib/test/test_genericpath.py", + "resources/test/cpython/Lib/test/test_getopt.py", + "resources/test/cpython/Lib/test/test_htmlparser.py", + "resources/test/cpython/Lib/test/test_importlib/stubs.py", + "resources/test/cpython/Lib/test/test_importlib/test_files.py", + "resources/test/cpython/Lib/test/test_importlib/test_metadata_api.py", + "resources/test/cpython/Lib/test/test_importlib/test_open.py", + "resources/test/cpython/Lib/test/test_importlib/test_util.py", + "resources/test/cpython/Lib/test/test_named_expressions.py", + "resources/test/cpython/Lib/test/test_peg_generator/__main__.py", + "resources/test/cpython/Lib/test/test_pipes.py", + "resources/test/cpython/Lib/test/test_source_encoding.py", + "resources/test/cpython/Lib/test/test_weakref.py", + "resources/test/cpython/Lib/test/test_webbrowser.py", + "resources/test/cpython/Lib/tkinter/__main__.py", + "resources/test/cpython/Lib/tkinter/test/test_tkinter/test_variables.py", + "resources/test/cpython/Modules/_decimal/libmpdec/literature/fnt.py", + "resources/test/cpython/Modules/_decimal/tests/deccheck.py", + "resources/test/cpython/Tools/i18n/pygettext.py", + "resources/test/cpython/Tools/test2to3/maintest.py", + "resources/test/cpython/Tools/test2to3/setup.py", + "resources/test/cpython/Tools/test2to3/test/test_foo.py", + "resources/test/cpython/Tools/test2to3/test2to3/hello.py", + ] + .into_iter() + .collect(); +} + fn is_not_hidden(entry: &DirEntry) -> bool { entry .file_name() @@ -20,6 +73,7 @@ pub fn iter_python_files(path: &PathBuf) -> impl Iterator { .filter_entry(is_not_hidden) .filter_map(|entry| entry.ok()) .filter(|entry| entry.path().to_string_lossy().ends_with(".py")) + .filter(|entry| !EXCLUDES.contains(entry.path().to_string_lossy().deref())) } pub fn read_line(path: &Path, row: &usize) -> Result {