mirror of
https://github.com/nushell/nushell.git
synced 2026-10-29 01:15:05 +08:00
<!-- Thanks for contributing to Nushell! Before submitting, please read the contributing guide: https://github.com/nushell/nushell/blob/main/CONTRIBUTING.md This template helps reviewers understand your changes and allows us to generate high-quality release notes. --> ## Description This PR adds some parser benchmarks. See how to run them below. You should get an output similar this below. Hopefully my math is right. ```nushell Throughput summary (derived from benchmark timing, unit: micro): ╭────┬─────────────────────────────────────────┬───────┬───────┬─────────┬───────────┬────────────────┬────────────────╮ │ # │ benchmark │ chars │ bytes │ seconds │ time_unit │ chars_per_unit │ bytes_per_unit │ ├────┼─────────────────────────────────────────┼───────┼───────┼─────────┼───────────┼────────────────┼────────────────┤ │ 0 │ parser_lex_large_97332b_97098c │ 97098 │ 97332 │ 0.00 │ micro │ 246.38 │ 246.97 │ │ 1 │ parser_lex_medium_4315b_4315c │ 4315 │ 4315 │ 0.00 │ micro │ 169.22 │ 169.22 │ │ 2 │ parser_lex_small_82b_82c │ 82 │ 82 │ 0.00 │ micro │ 106.11 │ 106.11 │ │ 3 │ parser_lite_medium_4315b_4315c │ 4315 │ 4315 │ 0.00 │ micro │ 95.46 │ 95.46 │ │ 4 │ parser_lite_real_world_620b_620c │ 620 │ 620 │ 0.00 │ micro │ 38.51 │ 38.51 │ │ 5 │ parser_lite_small_82b_82c │ 82 │ 82 │ 0.00 │ micro │ 5.99 │ 5.99 │ │ 6 │ parser_parse_block_medium_4315b_4315c │ 4315 │ 4315 │ 0.00 │ micro │ 37.52 │ 37.52 │ │ 7 │ parser_parse_block_real_world_620b_620c │ 620 │ 620 │ 0.00 │ micro │ 24.31 │ 24.31 │ │ 8 │ parser_parse_block_small_82b_82c │ 82 │ 82 │ 0.00 │ micro │ 4.77 │ 4.77 │ │ 9 │ parser_parse_large_97332b_97098c │ 97098 │ 97332 │ 0.00 │ micro │ 97.10 │ 97.33 │ │ 10 │ parser_parse_medium_4315b_4315c │ 4315 │ 4315 │ 0.00 │ micro │ 44.62 │ 44.62 │ │ 11 │ parser_parse_small_82b_82c │ 82 │ 82 │ 0.00 │ micro │ 4.27 │ 4.27 │ ╰────┴─────────────────────────────────────────┴───────┴───────┴─────────┴───────────┴────────────────┴────────────────╯ Headline metric candidates (chars/micro): ╭───┬──────────────────────────────────┬────────────────╮ │ # │ benchmark │ chars_per_unit │ ├───┼──────────────────────────────────┼────────────────┤ │ 0 │ parser_lex_large_97332b_97098c │ 246.38 │ │ 1 │ parser_lex_medium_4315b_4315c │ 169.22 │ │ 2 │ parser_lex_small_82b_82c │ 106.11 │ │ 3 │ parser_parse_large_97332b_97098c │ 97.10 │ │ 4 │ parser_lite_medium_4315b_4315c │ 95.46 │ ╰───┴──────────────────────────────────┴────────────────╯ Benchmark logfile: target/parser-bench/benchmarks-parser__-20260519-125213.log parser_lex_small_82b_82c [ 758.0 ns ... 772.8 ns ... 829.7 ns ] stddev: 14.8 ns parser_lex_medium_4315b_4315c [ 25.2 us ... 25.5 us ... 26.1 us ] stddev: 248.3 ns parser_lex_large_97332b_97098c [ 389.0 us ... 394.1 us ... 410.0 us ] stddev: 4.7 us parser_lite_small_82b_82c [ 13.2 us ... 13.7 us ... 17.7 us ] stddev: 670.6 ns parser_lite_medium_4315b_4315c [ 45.0 us ... 45.2 us ... 46.0 us ] stddev: 183.7 ns parser_lite_real_world_620b_620c [ 15.9 us ... 16.1 us ... 18.1 us ] stddev: 374.2 ns parser_parse_block_small_82b_82c [ 16.7 us ... 17.2 us ... 19.0 us ] stddev: 402.0 ns parser_parse_block_medium_4315b_4315c [ 111.0 us ... 115.0 us ... 124.3 us ] stddev: 3.1 us parser_parse_block_real_world_620b_620c [ 25.0 us ... 25.5 us ... 26.6 us ] stddev: 411.2 ns parser_parse_small_82b_82c [ 18.7 us ... 19.2 us ... 20.8 us ] stddev: 508.7 ns parser_parse_medium_4315b_4315c [ 95.2 us ... 96.7 us ... 113.4 us ] stddev: 3.2 us parser_parse_large_97332b_97098c [ 1.0 ms ... 1.0 ms ... 1.0 ms ] stddev: 3.8 us Compiling nu-cmd-lang v0.112.3 (/Users/fdncred/src/nushell/crates/nu-cmd-lang) Compiling nu-test-support v0.112.3 (/Users/fdncred/src/nushell/crates/nu-test-support) Compiling nu v0.112.3 (/Users/fdncred/src/nushell) Finished `bench` profile [optimized] target(s) in 1m 12s Running benches/benchmarks.rs (target/release/deps/benchmarks-5a8a0256451b1c57) ``` <!-- Explain what this PR does and why. This section is intentionally flexible: - Describe the problem - Explain your approach - Include technical details if relevant Good examples: - "In this PR, I fixed..." - "In this PR, I added support for..." - "This change improves X by..." Write as much or as little as needed for reviewers to understand your changes. --> ## User-facing changes (Release notes) Run the new parser benchmarks like this: ```nushell nu scripts/parser-bench-run.nu ``` The `parser-bench-run.nu` script has these arguments. ```nushell --filter: string = "parser_*" # benchmark name glob filter (tango uses glob matching) --bench: string = "benchmarks" # bench target name --out-dir: path = "target/parser-bench" # directory where logs are written ``` Analyze the new parser benchmarks like this: ```nushell nu scripts/parser-bench-throughput.nu target/parser-bench/benchmarks-parser__-20260519-125213.log ``` The `parser-bench-throughput.nu` script has these arguments ```nushell log_file?: path # output file produced by parser-bench-0.112.2-run.nu --name-prefix: string = "parser_" # benchmark name glob filter --time-unit: string = "micro" # throughput time base: sec|milli|micro|nano ``` <!-- This section is used (mostly as-is) for https://www.nushell.sh/blog/ Describe how Nushell behavior changes from a user's perspective. Do NOT describe internal Rust changes here. Write in a release note style, for example: - "Added support for..." - "Fixed an issue where..." - "Nushell now supports..." - "Improved performance of..." If your changes do NOT affect users (internal refactors, cleanup, etc.), just write: - "n/a" - "nan" - or similar This tells us the change should not appear in the changelog. Tips: - Focus on observable behavior - Include examples if helpful - Keep it concise You can: - Write a short paragraph (will appear as a bullet point), OR - Use headings (###) if your change needs more structure Avoid writing things like: - "In this PR, I refactored..." - "This updates internal code..." You may leave this blank until the PR is ready. --> ## Additional notes <!-- Optional. Examples: - fixes #123 - closes #456 - related #789 Anything else reviewers should know. Remove this section if not needed. -->
131 lines
4.1 KiB
Nu
131 lines
4.1 KiB
Nu
#!/usr/bin/env nu
|
|
# Parse parser benchmark output and report throughput (chars/sec and bytes/sec).
|
|
# Compatible with Nushell 0.112.2.
|
|
|
|
def unit-to-seconds [value: float, unit: string] {
|
|
match $unit {
|
|
"ns" => { $value / 1_000_000_000.0 }
|
|
"us" => { $value / 1_000_000.0 }
|
|
"µs" => { $value / 1_000_000.0 }
|
|
"ms" => { $value / 1_000.0 }
|
|
"s" => { $value }
|
|
_ => { null }
|
|
}
|
|
}
|
|
|
|
def normalize-time-unit [unit: string] {
|
|
let normalized = ($unit | str downcase)
|
|
|
|
match $normalized {
|
|
"sec" | "s" | "second" | "seconds" => { "sec" }
|
|
"milli" | "ms" | "millisecond" | "milliseconds" => { "milli" }
|
|
"micro" | "us" | "µs" | "microsecond" | "microseconds" => { "micro" }
|
|
"nano" | "ns" | "nanosecond" | "nanoseconds" => { "nano" }
|
|
_ => { null }
|
|
}
|
|
}
|
|
|
|
def selected-unit-to-seconds [unit: string] {
|
|
match $unit {
|
|
"sec" => { 1.0 }
|
|
"milli" => { 0.001 }
|
|
"micro" => { 0.000001 }
|
|
"nano" => { 0.000000001 }
|
|
_ => { null }
|
|
}
|
|
}
|
|
|
|
def extract-sizes [name: string] {
|
|
let capture = ($name | parse -r '_(?<bytes>[0-9]+)b_(?<chars>[0-9]+)c$')
|
|
|
|
if ($capture | is-empty) {
|
|
null
|
|
} else {
|
|
{
|
|
bytes: ($capture.0.bytes | into int)
|
|
chars: ($capture.0.chars | into int)
|
|
}
|
|
}
|
|
}
|
|
|
|
def main [
|
|
log_file?: path # output file produced by parser-bench-0.112.2-run.nu
|
|
--name-prefix: string = "parser_"
|
|
--time-unit: string = "micro" # throughput time base: sec|milli|micro|nano
|
|
] {
|
|
if $log_file == null {
|
|
error make {
|
|
msg: "Missing required argument: log_file"
|
|
help: "Usage: parser-bench-throughput.nu <log-file>\n\nExample:\n ./target/debug/nu scripts/parser-bench-throughput.nu target/parser-bench/benchmarks-parser__-20260519-125213.log"
|
|
}
|
|
}
|
|
|
|
let normalized_time_unit = (normalize-time-unit $time_unit)
|
|
if $normalized_time_unit == null {
|
|
error make {
|
|
msg: $"unsupported time unit: ($time_unit)"
|
|
help: "supported values: sec, milli, micro, nano"
|
|
}
|
|
}
|
|
|
|
let selected_unit_seconds = (selected-unit-to-seconds $normalized_time_unit)
|
|
|
|
let rows = (
|
|
open $log_file
|
|
| lines
|
|
| parse -r '^(?<name>parser_[A-Za-z0-9_]+)\s+\[.*?\.\.\.\s*(?<value>[0-9]+(?:\.[0-9]+)?)\s*(?<unit>ns|us|µs|ms|s)\s*\.\.\.'
|
|
| where {|row| $row.name | str starts-with $name_prefix }
|
|
| each {|row|
|
|
let seconds = (unit-to-seconds ($row.value | into float) $row.unit)
|
|
let sizes = (extract-sizes $row.name)
|
|
let unit_time = if $seconds == null {
|
|
null
|
|
} else {
|
|
$seconds / $selected_unit_seconds
|
|
}
|
|
|
|
if ($seconds == null or $sizes == null or $unit_time == null or $unit_time == 0.0) {
|
|
null
|
|
} else {
|
|
{
|
|
benchmark: $row.name
|
|
seconds: ($seconds | into float)
|
|
chars: $sizes.chars
|
|
bytes: $sizes.bytes
|
|
time_unit: $normalized_time_unit
|
|
chars_per_unit: (($sizes.chars | into float) / $unit_time)
|
|
bytes_per_unit: (($sizes.bytes | into float) / $unit_time)
|
|
}
|
|
}
|
|
}
|
|
| compact
|
|
| uniq-by benchmark
|
|
| sort-by benchmark
|
|
)
|
|
|
|
if ($rows | is-empty) {
|
|
error make {
|
|
msg: "no parser benchmark rows were parsed"
|
|
help: "ensure the log contains parser benchmark output lines with timing units"
|
|
}
|
|
}
|
|
|
|
print $"Throughput summary \(derived from benchmark timing, unit: ($normalized_time_unit)\):"
|
|
print ($rows
|
|
| select benchmark chars bytes seconds time_unit chars_per_unit bytes_per_unit
|
|
| table)
|
|
|
|
print $"\nHeadline metric candidates \(chars/($normalized_time_unit)\):"
|
|
print ($rows
|
|
| sort-by chars_per_unit --reverse
|
|
| first 5
|
|
| select benchmark chars_per_unit
|
|
| table)
|
|
|
|
# print "\nFull table of all parsed benchmarks with throughput metrics:"
|
|
# print $rows
|
|
|
|
print $"\nBenchmark logfile: ($log_file)"
|
|
open $log_file
|
|
}
|