hyperfine-1.18.0/.cargo_vcs_info.json0000644000000001360000000000100131140ustar { "git": { "sha1": "24a0d5da1bff11567bbf307315d11cb0e10733ec" }, "path_in_vcs": "" }hyperfine-1.18.0/.github/dependabot.yml000064400000000000000000000004351046102023000160760ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: monthly time: "04:00" timezone: Europe/Berlin open-pull-requests-limit: 2 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" hyperfine-1.18.0/.github/workflows/CICD.yml000064400000000000000000000347161046102023000165410ustar 00000000000000name: CICD env: CICD_INTERMEDIATES_DIR: "_cicd-intermediates" MSRV_FEATURES: "" on: workflow_dispatch: pull_request: push: branches: - master tags: - '*' jobs: crate_metadata: name: Extract crate metadata runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Extract crate information id: crate_metadata run: | cargo metadata --no-deps --format-version 1 | jq -r '"name=" + .packages[0].name' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"version=" + .packages[0].version' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"maintainer=" + .packages[0].authors[0]' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"homepage=" + .packages[0].homepage' | tee -a $GITHUB_OUTPUT cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + .packages[0].rust_version' | tee -a $GITHUB_OUTPUT outputs: name: ${{ steps.crate_metadata.outputs.name }} version: ${{ steps.crate_metadata.outputs.version }} maintainer: ${{ steps.crate_metadata.outputs.maintainer }} homepage: ${{ steps.crate_metadata.outputs.homepage }} msrv: ${{ steps.crate_metadata.outputs.msrv }} ensure_cargo_fmt: name: Ensure 'cargo fmt' has been run runs-on: ubuntu-20.04 steps: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - uses: actions/checkout@v4 - run: cargo fmt -- --check min_version: name: Minimum supported rust version runs-on: ubuntu-20.04 needs: crate_metadata steps: - name: Checkout source code uses: actions/checkout@v4 - name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }}) uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.crate_metadata.outputs.msrv }} components: clippy - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }} - name: Run tests run: cargo test --locked ${{ env.MSRV_FEATURES }} build: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} needs: crate_metadata strategy: fail-fast: false matrix: job: - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true } - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - { target: i686-pc-windows-msvc , os: windows-2019 } - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - { target: x86_64-apple-darwin , os: macos-12 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - { target: x86_64-pc-windows-msvc , os: windows-2019 } - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } env: BUILD_CMD: cargo steps: - name: Checkout source code uses: actions/checkout@v4 - name: Install prerequisites shell: bash run: | case ${{ matrix.job.target }} in arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; esac - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.job.target }} - name: Install cross if: matrix.job.use-cross uses: taiki-e/install-action@v2 with: tool: cross - name: Overwrite build command env variable if: matrix.job.use-cross shell: bash run: echo "BUILD_CMD=cross" >> $GITHUB_ENV - name: Show version information (Rust, cargo, GCC) shell: bash run: | gcc --version || true rustup -V rustup toolchain list rustup default cargo -V rustc -V - name: Build shell: bash run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }} - name: Set binary name & path id: bin shell: bash run: | # Figure out suffix of binary EXE_suffix="" case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac; # Setup paths BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}" BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}" # Let subsequent steps know where to find the binary echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT - name: Set testing options id: test-options shell: bash run: | # test only library unit tests and binary for arm-type targets unset CARGO_TEST_OPTIONS unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${{ needs.crate_metadata.outputs.name }}" ;; esac; echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT - name: Run tests shell: bash run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} - name: Create tarball id: package shell: bash run: | PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }} PKG_NAME=${PKG_BASENAME}${PKG_suffix} echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package" ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/" mkdir -p "${ARCHIVE_DIR}" mkdir -p "${ARCHIVE_DIR}/autocomplete" # Binary cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR" # README, LICENSE and CHANGELOG files cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR" # Man page cp 'doc/${{ needs.crate_metadata.outputs.name }}.1' "$ARCHIVE_DIR" # Autocompletion files cp 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'${{ needs.crate_metadata.outputs.name }}.bash' "$ARCHIVE_DIR/autocomplete/" cp 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'${{ needs.crate_metadata.outputs.name }}.fish' "$ARCHIVE_DIR/autocomplete/" cp 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'_${{ needs.crate_metadata.outputs.name }}.ps1' "$ARCHIVE_DIR/autocomplete/" cp 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'_${{ needs.crate_metadata.outputs.name }}' "$ARCHIVE_DIR/autocomplete/" # base compressed package pushd "${PKG_STAGING}/" >/dev/null case ${{ matrix.job.target }} in *-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;; *) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;; esac; popd >/dev/null # Let subsequent steps know where to find the compressed package echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT - name: Create Debian package id: debian-package shell: bash if: startsWith(matrix.job.os, 'ubuntu') run: | COPYRIGHT_YEARS="2018 - "$(date "+%Y") DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package" DPKG_DIR="${DPKG_STAGING}/dpkg" mkdir -p "${DPKG_DIR}" DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }} DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac; DPKG_VERSION=${{ needs.crate_metadata.outputs.version }} unset DPKG_ARCH case ${{ matrix.job.target }} in aarch64-*-linux-*) DPKG_ARCH=arm64 ;; arm-*-linux-*hf) DPKG_ARCH=armhf ;; i686-*-linux-*) DPKG_ARCH=i686 ;; x86_64-*-linux-*) DPKG_ARCH=amd64 ;; *) DPKG_ARCH=notset ;; esac; DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT # Binary install -Dm755 "${{ steps.bin.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.bin.outputs.BIN_NAME }}" # Man page install -Dm644 'doc/${{ needs.crate_metadata.outputs.name }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" # Autocompletion files install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'${{ needs.crate_metadata.outputs.name }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ needs.crate_metadata.outputs.name }}" install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'${{ needs.crate_metadata.outputs.name }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ needs.crate_metadata.outputs.name }}.fish" install -Dm644 'target/${{ matrix.job.target }}/release/build/${{ needs.crate_metadata.outputs.name }}'*/out/'_${{ needs.crate_metadata.outputs.name }}' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ needs.crate_metadata.outputs.name }}" # README and LICENSE install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md" install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT" install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE" install -Dm644 "CHANGELOG.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" gzip -n --best "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog" cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" < "${DPKG_DIR}/DEBIAN/control" <> $GITHUB_OUTPUT # build dpkg fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}" - name: "Artifact upload: tarball" uses: actions/upload-artifact@master with: name: ${{ steps.package.outputs.PKG_NAME }} path: ${{ steps.package.outputs.PKG_PATH }} - name: "Artifact upload: Debian package" uses: actions/upload-artifact@master if: steps.debian-package.outputs.DPKG_NAME with: name: ${{ steps.debian-package.outputs.DPKG_NAME }} path: ${{ steps.debian-package.outputs.DPKG_PATH }} - name: Check for release id: is-release shell: bash run: | unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT - name: Publish archives and packages uses: softprops/action-gh-release@v1 if: steps.is-release.outputs.IS_RELEASE with: files: | ${{ steps.package.outputs.PKG_PATH }} ${{ steps.debian-package.outputs.DPKG_PATH }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} winget: name: Publish to Winget runs-on: ubuntu-latest needs: build if: startsWith(github.ref, 'refs/tags/v') steps: - uses: vedantmgoyal2009/winget-releaser@v2 with: identifier: sharkdp.hyperfine installers-regex: '-pc-windows-msvc\.zip$' token: ${{ secrets.WINGET_TOKEN }} hyperfine-1.18.0/.gitignore000064400000000000000000000000251046102023000136710ustar 00000000000000 /target/ **/*.rs.bk hyperfine-1.18.0/CHANGELOG.md000064400000000000000000000313351046102023000135220ustar 00000000000000# unreleased ## Features ## Changes ## Bugfixes ## Other # v1.18.0 ## Features - Add support for microseconds via `--time-unit microsecond`, see #684 (@sharkdp) ## Bugfixes - Proper argument quoting on Windows CMD, see #296 and #678 (@PedroWitzel) # v1.17.0 ## Features - Add new `--sort` option to control the order in the rel. speed comparison and in markup export formats, see #601, #614, #655 (@sharkdp) - Parameters which are unused in the command line are now displayed in parentheses, see #600 and #644 (@sharkdp). - Added `--log-count` option for histogram plots, see `scripts/plot_histogram.py` (@sharkdp) ## Changes - Updated hyperfine to use `windows-sys` instead of the unmaintained `winapi`, see #624, #639, #636, #641 (@clemenswasser) - Silenced deprecation warning in Python scripts, see #633 (@nicovank) - Major update of the man page, see 0ce6578, #647 (@sharkdp) ## Bugfixes - Do not export intermediate results to stdout when using `-` as a file name, see #640 and #643 (@sharkdp) - Markup exporting does not fail if benchmark results are zero, see #642 (@sharkdp) # v1.16.1 ## Bugfixes - Fix line-wrapping of `--help` text (@sharkdp) - Fix `--input=null` (@sharkdp) # v1.16.0 ## Features - Added new `--input` option, see #541 and #563 (@snease) - Added possibility to specify `-` as the filename in the `--export-*` options, see #615 and #623 (@humblepenguinn) ## Changes - Improve hints for outlier warnings if `--warmup` or `--prepare` are in use already, see #570 (@sharkdp) ## Bugfixes - Fix uncolored output on Windows if `TERM` is not set, see #583 (@nabijaczleweli) - On Windows, only run `cmd.exe` with the `/C` option. Use `-c` for all other shells. See #568 and #582 (@FilipAndersson245) ## Other - Thanks to @berombau for working on dependency upgrades, see #584 - Fixed installationm on Windows, see #595 and #596 (@AntoniosBarotsis) # v1.15.0 ## Features - Disable colorized output in case of `TERM=dumb` or `NO_COLOR=1`, see #542 and #555 (@nabijaczleweli) - Add new (experimental) `--min-benchmarking-time ` option, see #527 (@sharkdp) ## Bugfixes - Fix user and kernel times on Windows, see #368 and #538 (@clemenswasser) ## Other - Improve `--help` texts of `--export-*` options, see #506 and #522 (@Engineer-of-Efficiency) # v1.14.0 ## Features - Add a new `--output={null,pipe,inherit,}` option to control where the output of the benchmarked program is redirected (if at all), see #377 and #509 (@tavianator, originally suggested by @BurntSushi) - Add Emacs org-mode as a new export format, see #491 (@ppaulweber) # v1.13.0 ## Features - Added a new `--shell=none`/`-N` option to disable the intermediate shell for executing the benchmarked commands. Hyperfine normally measures and subtracts the shell spawning time, but the intermediate shell always introduces a certain level of measurement noise. Using `--shell=none`/`-N` allows users to benchmark very fast commands (with a runtime on the order of a few milliseconds). See #336, #429, and #487 (@cipriancraciun and @sharkdp) - Added `--setup`/`-s` option that can be used to run `make all` or similar. It runs once per set of tests, like `--cleanup`/`-c` (@avar) - Added new `plot_progression.py` script to debug background interference effects. ## Changes - Breaking change: the `-s` short option for `--style` is now used for the new `--setup` option. - The environment offset randomization is now also available on Windows, see #484 ## Other - Improved documentation and test coverage, cleaned up code base for future improvements. # v1.12.0 ## Features - `--command-name` can now take parameter names from `--parameter-*` options, see #351 and #391 (@silathdiir) - Exit codes (or signals) are now printed in cases of command failures, see #342 (@KaindlJulian) - Exit codes are now part of the JSON output, see #371 (@JordiChauzi) - Colorized output should now be enabled on Windows by default, see #427 ## Changes - When `--export-*` commands are used, result files are created before benchmark execution to fail early in case of, e.g., wrong permissions. See #306 (@s1ck). - When `--export-*` options are used, result files are written after each individual benchmark command instead of writing after all benchmarks have finished. See #306 (@s1ck). - Reduce number of shell startup time measurements from 200 to 50, generally speeding up benchmarks. See #378 - User and system time are now in consistent time units, see #408 and #409 (@film42) # v1.11.0 ## Features - The `-L`/`--parameter-list` option can now be specified multiple times to evaluate all possible combinations of the listed parameters: ``` bash hyperfine -L number 1,2 -L letter a,b,c \ "echo {number}{letter}" \ "printf '%s\n' {number}{letter}" # runs 12 benchmarks: 2 commands (echo and printf) times 6 combinations of # the "letter" and "number" parameters ``` See: #253, #318 (@wchargin) - Add CLI option to identify a command with a custom name, see #326 (@scampi) ## Changes - When parameters are used with `--parameter-list` or `--parameter-scan`, the JSON export format now contains a dictionary `parameters` instead of a single key `parameter`. See #253, #318. - The `plot_parametrized.py` script now infers the parameter name, and its `--parameter-name` argument has been deprecated. See #253, #318. ## Bugfixes - Fix a bug in the outlier detection which would only detect "slow outliers" but not the fast ones (runs that are much faster than the rest of the benchmarking runs), see #329 - Better error messages for very fast commands that would lead to inf/nan results in the relative speed comparison, see #319 - Show error message if `--warmup` or `--*runs` arguments can not be parsed, see #337 - Keep output colorized when the output is not interactive and `--style=full` or `--style=color` is used. # v1.10.0 ## Features - Hyperfine now comes with shell completion files for Bash, Zsh, Fish and PowerShell, see #290 (@four0000four). - Hyperfine now comes with a basic man page, see #257 (@cadeef) - During execution of benchmarks, hyperfine will now set a `HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET` environment variable in order to randomize the memory layout. See #235 and #241 for references and details. - A few enhancements for the histogram plotting scripts and the advanced statistics script - Updates for the `plot_whisker.py` script, see #275 (@ghaiklor) ## Bugfixes - Fix Spin Icon on Windows, see #229 - A few typos have been fixed, see #292 (@McMartin) ## Packaging - `hyperfine` is now available on MacPorts for macOS, see #281 (@herbygillot) - `hyperfine` is now available on OpenBSD, see #289 (@minusf) Package authors: note that Hyperfine now comes with a set of shell completion files and a man page (see above) # v1.9.0 ## Features - The new `--parameter-list ` option can be used to run a parametrized benchmark on a user-specified list of values. This is similar to `--parameter-scan `, but doesn't necessarily required numeric arguments. ``` bash hyperfine --parameter-list compiler "gcc,clang" \ "{compiler} -O2 main.cpp" ``` See: #227, #234 (@JuanPotato) - Added `none` as a possible choice for the `--style` option to run `hyperfine` without any output, see #193 (@knidarkness) - Added a few new scripts for plotting various types of benchmark results (https://github.com/sharkdp/hyperfine/tree/master/scripts) ## Changes - The `--prepare` command is now also run during the warmup phase, see #182 (@sseemayer) - Better estimation of the remaining benchmark time due to an update of the `indicatif` crate. ## Other - `hyperfine` is now available on NixOS, see #240 (@tuxinaut) # v1.8.0 ## Features - The `--prepare ` option can now be specified multiple times to run specific preparation commands for each of the benchmarked programs: ``` bash hyperfine --prepare "make clean; git checkout master" "make" \ --prepare "make clean; git checkout feature" "make" ``` See: #216, #218 (@iamsauravsharma) - Added a new [`welch_ttest.py`](https://github.com/sharkdp/hyperfine/blob/master/scripts/welch_ttest.py) script to test whether or not the two benchmark results are the same, see #222 (@uetchy) - The Markdown export has been improved. The relative speed is now exported with a higher precision (see #208) and includes the standard deviation (see #225). ## Other - Improved documentation for [`scripts`](https://github.com/sharkdp/hyperfine/tree/master/scripts) folder (@matthieusb) # v1.7.0 ## Features - Added a new `-D`,`--parameter-step-size` option that can be used to control the step size for `--parameter-scan` benchmarks. In addition, decimal numbers are now allowed for parameter scans. For example, the following command runs `sleep 0.3`, `sleep 0.5` and `sleep 0.7`: ``` bash hyperfine --parameter-scan delay 0.3 0.7 -D 0.2 'sleep {delay}' ``` For more details, see #184 (@piyushrungta25) ## Other - hyperfine is now in the official Alpine repositories, see #177 (@maxice8, @5paceToast) - hyperfine is now in the official Fedora repositories, see #196 (@ignatenkobrain) - hyperfine is now in the official Arch Linux repositories - hyperfine can be installed on FreeBSD, see #204 (@0mp) - Enabled LTO for slightly smaller binary sizes, see #179 (@Calinou) - Various small improvements all over the code base, see #194 (@phimuemue) # v1.6.0 ## Features - Added a `-c, --cleanup ` option to execute `CMD` after the completion of all benchmarking runs for a given command. This is useful if the commands to be benchmarked produce artifacts that need to be cleaned up. See #91 (@RalfJung and @colinwahl) - Add parameter values (for `--parameter-scan` benchmarks) to exported CSV and JSON files. See #131 (@bbannier) - Added AsciiDoc export option, see #137 (@5paceToast) - The relative speed is now part of the Markdown export, see #127 (@mathiasrw and @sharkdp). - The *median* run time is now exported via CSV and JSON, see #171 (@hosewiejacke and @sharkdp). ## Other - Hyperfine has been updated to Rust 2018 (@AnderEnder). The minimum supported Rust version is now 1.31. # v1.5.0 ## Features - Show the number of runs in `hyperfine`s output (@tcmal) - Added two Python scripts to post-process exported benchmark results (see [`scripts/`](https://github.com/sharkdp/hyperfine/tree/master/scripts) folder) ## Other - Refined `--help` text for the `--export-*` flags (@psteinb) - Added Snapcraft file (@popey) - Small improvements in the progress bar "experience". # v1.4.0 ## Features - Added `-S`/`--shell` option to override the default shell, see #61 (@mqudsi and @jasonpeacock) - Added `-u`/`--time-unit` option to change the unit of time (`second` or `millisecond`), see #80 (@jasonpeacock) - Markdown export auto-selects time unit, see #71 (@jasonpeacock) # v1.3.0 ## Feature - Compute and print standard deviation of the speed ratio, see #83 (@Shnatsel) - More compact output format, see #70 (@jasonpeacock) - Added `--style=color`, see #70 (@jasonpeacock) - Added options to specify the max/exact numbers of runs, see #77 (@orium) ## Bugfixes - Change Windows `cmd` interpreter to `cmd.exe` to prevent accidentally calling other programs, see #74 (@tathanhdinh) ## Other - Binary releases for Windows are now available, see #87 # v1.2.0 - Support parameters in preparation commands, see #68 (@siiptuo) - Updated dependencies, see #69. The minimum required Rust version is now 1.24. # v1.1.0 * Added `--show-output` option (@chrisduerr and @sevagh) * Refactoring work (@stevepentland) # v1.0.0 ## Features * Support for various export-formats like CSV, JSON and Markdown - see #38, #44, #49, #42 (@stevepentland) * Summary output that compares the different benchmarks, see #6 (@stevepentland) * Parameterized benchmarks via `-P`, `--parameter-scan `, see #19 ## Thanks I'd like to say a big THANK YOU to @stevepentland for implementing new features, for reviewing pull requests and for giving very valuable feedback. # v0.5.0 * Proper Windows support (@stevepentland) * Added `--style auto/basic/nocolor/full` option (@stevepentland) * Correctly estimate the full execution time, see #27 (@rleungx) * Added Void Linux install instructions (@wpbirney) # v0.4.0 - New `--style` option to disable output coloring and interactive CLI features, see #24 (@stevepentland) - Statistical outlier detection, see #23 #18 # v0.3.0 ## Features - In addition to 'real' (wall clock) time, Hyperfine can now also measure 'user' and 'system' time (see #5). - Added `--prepare` option that can be used to clear up disk caches before timing runs, for example (see #8). ## Other - [Arch Linux package](https://aur.archlinux.org/packages/hyperfine) for Hyperfine (@jD91mZM2). - Ubuntu/Debian packages are now are available. # v0.2.0 Initial public release hyperfine-1.18.0/CITATION.cff000064400000000000000000000007361046102023000136040ustar 00000000000000cff-version: 1.2.0 title: hyperfine message: >- If you use this software in scientific publications, please consider citing it using the metadata from this file. type: software authors: - given-names: David family-names: Peter email: mail@david-peter.de orcid: 'https://orcid.org/0000-0001-7950-9915' repository-code: 'https://github.com/sharkdp/hyperfine' abstract: A command-line benchmarking tool. license: MIT version: 1.16.1 date-released: '2023-03-21' hyperfine-1.18.0/Cargo.lock0000644000001105260000000000100110740ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom", "once_cell", "version_check", ] [[package]] name = "ahash" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", "once_cell", "version_check", ] [[package]] name = "aho-corasick" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_cmd" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", "bstr", "doc-comment", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "borsh" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", "hashbrown 0.13.2", ] [[package]] name = "borsh-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate", "proc-macro2", "syn 1.0.109", ] [[package]] name = "borsh-derive-internal" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "borsh-schema-derive-internal" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "bstr" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "regex-automata", "serde", ] [[package]] name = "bytecheck" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", "simdutf8", ] [[package]] name = "bytecheck_derive" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_complete" version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7" dependencies = [ "clap", ] [[package]] name = "clap_lex" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", "windows-sys 0.48.0", ] [[package]] name = "console" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", "windows-sys 0.45.0", ] [[package]] name = "csv" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "errno" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" dependencies = [ "errno-dragonfly", "libc", "windows-sys 0.48.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash 0.7.6", ] [[package]] name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash 0.8.3", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hyperfine" version = "1.18.0" dependencies = [ "anyhow", "approx", "assert_cmd", "atty", "clap", "clap_complete", "colored", "csv", "indicatif", "libc", "nix", "once_cell", "predicates", "rand 0.8.5", "rust_decimal", "serde", "serde_json", "shell-words", "statistical", "tempfile", "thiserror", "windows-sys 0.48.0", ] [[package]] name = "indicatif" version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db45317f37ef454e6519b6c3ed7d377e5f23346f0823f86e65ca36912d1d0ef8" dependencies = [ "console", "instant", "number_prefix", "portable-atomic", "unicode-width", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.3", "rustix", "windows-sys 0.48.0", ] [[package]] name = "itertools" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "nix" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", "memoffset", "pin-utils", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ "num-bigint", "num-complex", "num-integer", "num-iter", "num-rational", "num-traits", ] [[package]] name = "num-bigint" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg 1.1.0", "num-integer", "num-traits", ] [[package]] name = "num-complex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg 1.1.0", "num-traits", ] [[package]] name = "num-integer" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg 1.1.0", "num-traits", ] [[package]] name = "num-iter" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg 1.1.0", "num-integer", "num-traits", ] [[package]] name = "num-rational" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.1.0", "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ "anstyle", "difflib", "float-cmp", "itertools", "normalize-line-endings", "predicates-core", "regex", ] [[package]] name = "predicates-core" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "proc-macro-crate" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ "toml", ] [[package]] name = "proc-macro2" version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "ptr_meta" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ "autocfg 0.1.8", "libc", "rand_chacha 0.1.1", "rand_core 0.4.2", "rand_hc", "rand_isaac", "rand_jitter", "rand_os", "rand_pcg", "rand_xorshift", "winapi", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] [[package]] name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ "autocfg 0.1.8", "rand_core 0.3.1", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core 0.6.4", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ "libc", "rand_core 0.4.2", "winapi", ] [[package]] name = "rand_os" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ "cloudabi", "fuchsia-cprng", "libc", "rand_core 0.4.2", "rdrand", "winapi", ] [[package]] name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ "autocfg 0.1.8", "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rend" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ "bitvec", "bytecheck", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", "tinyvec", "uuid", ] [[package]] name = "rkyv_derive" version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "rust_decimal" version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ "arrayvec", "borsh", "bytes", "num-traits", "rand 0.8.5", "rkyv", "serde", "serde_json", ] [[package]] name = "rustix" version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ "bitflags 2.4.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "serde" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", "syn 2.0.37", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "shell-words" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "simdutf8" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] name = "statistical" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49d57902bb128e5e38b5218d3681215ae3e322d99f65d5420e9849730d2ea372" dependencies = [ "num", "rand 0.6.5", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys 0.48.0", ] [[package]] name = "terminal_size" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ "rustix", "windows-sys 0.48.0", ] [[package]] name = "termtree" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", "syn 2.0.37", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets 0.42.2", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] hyperfine-1.18.0/Cargo.toml0000644000000047250000000000100111220ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.70.0" name = "hyperfine" version = "1.18.0" authors = ["David Peter "] build = "build.rs" description = "A command-line benchmarking tool" homepage = "https://github.com/sharkdp/hyperfine" readme = "README.md" categories = ["command-line-utilities"] license = "MIT OR Apache-2.0" repository = "https://github.com/sharkdp/hyperfine" [profile.release] lto = true codegen-units = 1 strip = true [dependencies.anyhow] version = "1.0" [dependencies.atty] version = "0.2" [dependencies.clap] version = "4" features = [ "suggestions", "color", "wrap_help", "cargo", "help", "usage", "error-context", ] default-features = false [dependencies.colored] version = "2.0" [dependencies.csv] version = "1.1" [dependencies.indicatif] version = "=0.17.4" [dependencies.rand] version = "0.8" [dependencies.rust_decimal] version = "1.31" [dependencies.serde] version = "1.0" features = ["derive"] [dependencies.serde_json] version = "1.0" [dependencies.shell-words] version = "1.0" [dependencies.statistical] version = "1.0" [dependencies.thiserror] version = "1.0" [dev-dependencies.approx] version = "0.5" [dev-dependencies.assert_cmd] version = "2.0" [dev-dependencies.predicates] version = "3.0" [dev-dependencies.tempfile] version = "3.3" [build-dependencies.atty] version = "0.2" [build-dependencies.clap] version = "4.0.18" [build-dependencies.clap_complete] version = "4.2.1" [features] windows_process_extensions_main_thread_handle = [] [target."cfg(all(windows, not(windows_process_extensions_main_thread_handle)))".dependencies.once_cell] version = "1.17" [target."cfg(not(windows))".dependencies.libc] version = "0.2" [target."cfg(target_os=\"linux\")".dependencies.nix] version = "0.26.2" features = ["zerocopy"] [target."cfg(windows)".dependencies.windows-sys] version = "0.48" features = [ "Win32_Foundation", "Win32_Security", "Win32_System_JobObjects", "Win32_System_LibraryLoader", "Win32_System_Threading", ] hyperfine-1.18.0/Cargo.toml.orig000064400000000000000000000032061046102023000145740ustar 00000000000000[package] authors = ["David Peter "] categories = ["command-line-utilities"] description = "A command-line benchmarking tool" homepage = "https://github.com/sharkdp/hyperfine" license = "MIT OR Apache-2.0" name = "hyperfine" readme = "README.md" repository = "https://github.com/sharkdp/hyperfine" version = "1.18.0" edition = "2018" build = "build.rs" rust-version = "1.70.0" [features] # Use the nightly feature windows_process_extensions_main_thread_handle windows_process_extensions_main_thread_handle = [] [dependencies] colored = "2.0" indicatif = "=0.17.4" statistical = "1.0" atty = "0.2" csv = "1.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rust_decimal = "1.31" rand = "0.8" shell-words = "1.0" thiserror = "1.0" anyhow = "1.0" [target.'cfg(not(windows))'.dependencies] libc = "0.2" [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.48", features = [ "Win32_Foundation", "Win32_Security", "Win32_System_JobObjects", "Win32_System_LibraryLoader", "Win32_System_Threading", ] } [target.'cfg(all(windows, not(windows_process_extensions_main_thread_handle)))'.dependencies] once_cell = "1.17" [target.'cfg(target_os="linux")'.dependencies] nix = { version = "0.26.2", features = ["zerocopy"] } [dependencies.clap] version = "4" default-features = false features = ["suggestions", "color", "wrap_help", "cargo", "help", "usage", "error-context"] [dev-dependencies] approx = "0.5" assert_cmd = "2.0" predicates = "3.0" tempfile = "3.3" [build-dependencies] clap = "4.0.18" atty = "0.2" clap_complete = "4.2.1" [profile.release] lto = true strip = true codegen-units = 1 hyperfine-1.18.0/LICENSE-APACHE000064400000000000000000000261351046102023000136370ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hyperfine-1.18.0/LICENSE-MIT000064400000000000000000000021211046102023000133340ustar 00000000000000MIT License Copyright (c) 2018-2022 David Peter, and all hyperfine contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hyperfine-1.18.0/README.md000064400000000000000000000233361046102023000131720ustar 00000000000000# hyperfine [![CICD](https://github.com/sharkdp/hyperfine/actions/workflows/CICD.yml/badge.svg)](https://github.com/sharkdp/hyperfine/actions/workflows/CICD.yml) [![Version info](https://img.shields.io/crates/v/hyperfine.svg)](https://crates.io/crates/hyperfine) [中文](https://github.com/chinanf-boy/hyperfine-zh) A command-line benchmarking tool. **Demo**: Benchmarking [`fd`](https://github.com/sharkdp/fd) and [`find`](https://www.gnu.org/software/findutils/): ![hyperfine](https://i.imgur.com/z19OYxE.gif) ## Features * Statistical analysis across multiple runs. * Support for arbitrary shell commands. * Constant feedback about the benchmark progress and current estimates. * Warmup runs can be executed before the actual benchmark. * Cache-clearing commands can be set up before each timing run. * Statistical outlier detection to detect interference from other programs and caching effects. * Export results to various formats: CSV, JSON, Markdown, AsciiDoc. * Parameterized benchmarks (e.g. vary the number of threads). * Cross-platform ## Usage ### Basic benchmarks To run a benchmark, you can simply call `hyperfine ...`. The argument(s) can be any shell command. For example: ```sh hyperfine 'sleep 0.3' ``` Hyperfine will automatically determine the number of runs to perform for each command. By default, it will perform *at least* 10 benchmarking runs and measure for at least 3 seconds. To change this, you can use the `-r`/`--runs` option: ```sh hyperfine --runs 5 'sleep 0.3' ``` If you want to compare the runtimes of different programs, you can pass multiple commands: ```sh hyperfine 'hexdump file' 'xxd file' ``` ### Warmup runs and preparation commands For programs that perform a lot of disk I/O, the benchmarking results can be heavily influenced by disk caches and whether they are cold or warm. If you want to run the benchmark on a warm cache, you can use the `-w`/`--warmup` option to perform a certain number of program executions before the actual benchmark: ```sh hyperfine --warmup 3 'grep -R TODO *' ``` Conversely, if you want to run the benchmark for a cold cache, you can use the `-p`/`--prepare` option to run a special command before *each* timing run. For example, to clear harddisk caches on Linux, you can run ```sh sync; echo 3 | sudo tee /proc/sys/vm/drop_caches ``` To use this specific command with hyperfine, call `sudo -v` to temporarily gain sudo permissions and then call: ```sh hyperfine --prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches' 'grep -R TODO *' ``` ### Parameterized benchmarks If you want to run a series of benchmarks where a single parameter is varied (say, the number of threads), you can use the `-P`/`--parameter-scan` option and call: ```sh hyperfine --prepare 'make clean' --parameter-scan num_threads 1 12 'make -j {num_threads}' ``` This also works with decimal numbers. The `-D`/`--parameter-step-size` option can be used to control the step size: ```sh hyperfine --parameter-scan delay 0.3 0.7 -D 0.2 'sleep {delay}' ``` This runs `sleep 0.3`, `sleep 0.5` and `sleep 0.7`. For non-numeric parameters, you can also supply a list of values with the `-L`/`--parameter-list` option: ``` hyperfine -L compiler gcc,clang '{compiler} -O2 main.cpp' ``` ### Intermediate shell By default, commands are executed using a predefined shell (`/bin/sh` on Unix, `cmd.exe` on Windows). If you want to use a different shell, you can use the `-S, --shell ` option: ```sh hyperfine --shell zsh 'for i in {1..10000}; do echo test; done' ``` Note that hyperfine always *corrects for the shell spawning time*. To do this, it performs a calibration procedure where it runs the shell with an empty command (multiple times), to measure the startup time of the shell. It will then subtract this time from the total to show the actual time used by the command in question. If you want to run a benchmark *without an intermediate shell*, you can use the `-N` or `--shell=none` option. This is helpful for very fast commands (< 5 ms) where the shell startup overhead correction would produce a significant amount of noise. Note that you cannot use shell syntax like `*` or `~` in this case. ``` hyperfine -N 'grep TODO /home/user' ``` ### Shell functions and aliases If you are using bash, you can export shell functions to directly benchmark them with hyperfine: ```bash my_function() { sleep 1; } export -f my_function hyperfine --shell=bash my_function ``` Otherwise, inline them into or source them from the benchmarked program: ```sh hyperfine 'my_function() { sleep 1; }; my_function' echo 'alias my_alias="sleep 1"' > /tmp/my_alias.sh hyperfine '. /tmp/my_alias.sh; my_alias' ``` ### Exporting results Hyperfine has multiple options for exporting benchmark results to CSV, JSON, Markdown and other formats (see `--help` text for details). #### Markdown You can use the `--export-markdown ` option to create tables like the following: | Command | Mean [s] | Min [s] | Max [s] | Relative | |:---|---:|---:|---:|---:| | `find . -iregex '.*[0-9]\.jpg$'` | 2.275 ± 0.046 | 2.243 | 2.397 | 9.79 ± 0.22 | | `find . -iname '*[0-9].jpg'` | 1.427 ± 0.026 | 1.405 | 1.468 | 6.14 ± 0.13 | | `fd -HI '.*[0-9]\.jpg$'` | 0.232 ± 0.002 | 0.230 | 0.236 | 1.00 | #### JSON The JSON output is useful if you want to analyze the benchmark results in more detail. The [`scripts/`](https://github.com/sharkdp/hyperfine/tree/master/scripts) folder includes a lot of helpful Python programs to further analyze benchmark results and create helpful visualizations, like a histogram of runtimes or a whisker plot to compare multiple benchmarks: | ![](doc/histogram.png) | ![](doc/whisker.png) | |---:|---:| ### Detailed benchmark flowchart The following chart explains the execution order of various timing runs when using options like `--warmup`, `--prepare `, `--setup ` or `--cleanup `: ![](doc/execution-order.png) ## Installation [![Packaging status](https://repology.org/badge/vertical-allrepos/hyperfine.svg)](https://repology.org/project/hyperfine/versions) ### On Ubuntu Download the appropriate `.deb` package from the [Release page](https://github.com/sharkdp/hyperfine/releases) and install it via `dpkg`: ``` wget https://github.com/sharkdp/hyperfine/releases/download/v1.16.1/hyperfine_1.16.1_amd64.deb sudo dpkg -i hyperfine_1.16.1_amd64.deb ``` ### On Fedora On Fedora, hyperfine can be installed from the official repositories: ```sh dnf install hyperfine ``` ### On Alpine Linux On Alpine Linux, hyperfine can be installed [from the official repositories](https://pkgs.alpinelinux.org/packages?name=hyperfine): ``` apk add hyperfine ``` ### On Arch Linux On Arch Linux, hyperfine can be installed [from the official repositories](https://www.archlinux.org/packages/community/x86_64/hyperfine/): ``` pacman -S hyperfine ``` ### On Debian Linux On Debian Linux, hyperfine can be installed [from the testing repositories](https://packages.debian.org/testing/main/hyperfine) ``` apt install hyperfine ``` ### On Funtoo Linux On Funtoo Linux, hyperfine can be installed [from core-kit](https://github.com/funtoo/core-kit/tree/1.4-release/app-benchmarks/hyperfine): ``` emerge app-benchmarks/hyperfine ``` ### On NixOS On NixOS, hyperfine can be installed [from the official repositories](https://nixos.org/nixos/packages.html?query=hyperfine): ``` nix-env -i hyperfine ``` ### On Void Linux Hyperfine can be installed via xbps ``` xbps-install -S hyperfine ``` ### On macOS Hyperfine can be installed via [Homebrew](https://brew.sh): ``` brew install hyperfine ``` Or you can install using [MacPorts](https://www.macports.org): ``` sudo port selfupdate sudo port install hyperfine ``` ### On FreeBSD Hyperfine can be installed via pkg: ``` pkg install hyperfine ``` ### On OpenBSD ``` doas pkg_add hyperfine ``` ### On Windows Hyperfine can be installed via [Chocolatey](https://community.chocolatey.org/packages/hyperfine), [Scoop](https://scoop.sh/#/apps?q=hyperfine&s=0&d=1&o=true&id=8f7c10f75ecf5f9e42a862c615257328e2f70f61), or [Winget](https://github.com/microsoft/winget-pkgs/tree/master/manifests/s/sharkdp/hyperfine): ``` choco install hyperfine ``` ``` scoop install hyperfine ``` ``` winget install hyperfine ``` ### With conda Hyperfine can be installed via [`conda`](https://conda.io/en/latest/) from the [`conda-forge`](https://anaconda.org/conda-forge/hyperfine) channel: ``` conda install -c conda-forge hyperfine ``` ### With cargo (Linux, macOS, Windows) Hyperfine can be installed from source via [cargo](https://doc.rust-lang.org/cargo/): ``` cargo install --locked hyperfine ``` Make sure that you use Rust 1.70 or newer. ### From binaries (Linux, macOS, Windows) Download the corresponding archive from the [Release page](https://github.com/sharkdp/hyperfine/releases). ## Alternative tools Hyperfine is inspired by [bench](https://github.com/Gabriella439/bench). ## Integration with other tools [Chronologer](https://github.com/dandavison/chronologer) is a tool that uses `hyperfine` to visualize changes in benchmark timings across your Git history. Make sure to check out the [`scripts` folder](https://github.com/sharkdp/hyperfine/tree/master/scripts) in this repository for a set of tools to work with `hyperfine` benchmark results. ## Origin of the name The name *hyperfine* was chosen in reference to the hyperfine levels of caesium 133 which play a crucial role in the [definition of our base unit of time](https://en.wikipedia.org/wiki/Second#History_of_definition) — the second. ## Citing hyperfine Thank you for considering to cite hyperfine in your research work. Please see the information in the sidebar on how to properly cite hyperfine. ## License `hyperfine` is dual-licensed under the terms of the MIT License and the Apache License 2.0. See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files for details. hyperfine-1.18.0/build.rs000064400000000000000000000011101046102023000133420ustar 00000000000000use std::fs; use clap_complete::{generate_to, Shell}; include!("src/cli.rs"); fn main() { let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or_else(|| std::env::var_os("OUT_DIR")); let outdir = match var { None => return, Some(outdir) => outdir, }; fs::create_dir_all(&outdir).unwrap(); let mut command = build_command(); for shell in [ Shell::Bash, Shell::Fish, Shell::Zsh, Shell::PowerShell, Shell::Elvish, ] { generate_to(shell, &mut command, "hyperfine", &outdir).unwrap(); } } hyperfine-1.18.0/doc/execution-order.png000064400000000000000000002163131046102023000163010ustar 00000000000000PNG  IHDRXQ> pHYs QtEXtSoftwarewww.inkscape.org< IDATxyxLgd{ bWUW[[U][TKK- %EH(jO&H3L DrW3<{&<}Ga4!Bb,B!6` !B3IB!$XB!L,!Bb& B!D1K!I%BQ$B!(f` !B3IB!Ei Ull,PBiR( bcc7o*سB [aYIII4hЀ v(B  !D)h4BV.0x(` !SFQaQI%OXyCrM܅ :t/bccCZhڴ) &**ݻwc0hѢu1۳gjݺu4hЀիpM.\s=gކ ޽;666sa7o֭[h4tؑjժ- =XBQ,^m۶QV-<<<ضm.\0|2ƍ f͚ő#GLmll{{{kׯyfk^O?DFFiii,^9sPbEF#ƍ#66qu!*҃%e_Ei׮]v5ۿrJzŀ`ٲei֭[?{g+:uܹs9r}>;|K!ʐ~wF6mhӦ vvvΝF>|tn޼^/\\\L?Wl<K!ʐmҰaCOǪU6mi,NGLL ZtL>}xST*.A"K!:uDN?~<{eR^=tRyFѬ8>oɘ`o|8ϟg֬Yb007G_1dKwȑ#ٺu+?SNE:V !Dyf4 \nݺ=AAA,_g޽{DFF ׯ_Obb":tޞxZ-_~_f̙$$$p)S㭷2Yt)۷o' s=ĉ0`ٓWquuo5t-X777^}U<==iԨm۶Ɔj*ڵ… |t%_ƍq4mڔjժ1zh$ݻ  '''vJ@@r( FQVB`U%s4uٶz?ЦMZ-jhLOٴi| "AF^%}BZԬTT-[+&Di,!(%S+%͛7@׳o>y\\\߸z*UVgϞ.N֭[q+W[n888kwAyÛԩSj 1: G###z*:tl߾(jժEn ]Xܹs:t,5kF6m-&!Dt"ڵ+^^^OE:hu#e k׎3g˅ &,,lݻȚ5k̎C5ʔ`effҹsg&O̺u눋CTGٿYw^ @JJ ŶmhذY%iii 2իWlҔ`m߾pصkiiixyy1ds:tl믿/aX~=cƌa͚51F# :%K|̛7{{{,--ILLo߾ϲbjР͛77KJ%o&C DDDbBG\p!]t!11gϲn:Xb?<YYYfoڴ ^{ԩ 4Xbcc:u*|fnܸA޽]67o͛\zWWWV5k/Faر/%C'O$88ݻws|1?޴ܹ?B^_\~^xe˖kv̝;3fBBBVbÆ ̘1k!DYS~}9B޽iҤYOGJ '|b:{vYAn}Ӳe|~饗8qi &u͙3L/_7+W櫯ʕ+رGn?o~wؤI8q)a7n'Nĉ̝;7nzݻ-[P(ر#Ft:K:w̘1cL$((>}0|R!+S#~믿lذa rʼ{3 6ЪU+f͚Epp0*T@TW_ѯ_?nJ`` [lYf@ѣYn۶mcƌM̙C*Uزe 'O6+`ĉo̚5+@^^^̞=.]<'DH ya^}T^R^zaggMےٷo,쵅͛7뤧ڵjժQjUZ()OPPkr+WT㔗qvvahh(nZ"664)xuЁ;wrifΜIJJ =z0ט9s&K,᧟~bܸq;xxx矓ιsغu+!!!ddd0|p >M6eݺuZ +++9¸q۷o3}tF%ڵk 믿wޡ_~_|AAA|7;vk.֮]neBsc2ydl#_~%}QQQ,X^z'OvZTbTT|g̙3y 4]4JHHH8njcԨQkkkz-^|EVĦi4hk֬atޝ_gyz=9233d(5NNNL>cj֬Y {"O>b>)x4o޼3[[[8siۙ3g3}swـh7R`0~{ǎ,9qvZu뚶KlBcǎTPl]R~}~7S)OJT*&T(LSΝc̘1ưa0 >y-[oPJŎ;2e +WdX[[Ӯ];Νoy摐?[la˖-3GG{|aV\izφ Lǎk}srr?_|Yf:tO>I&}eСKڵ+Æ Yf>}˗/[lz쉍 }ixӜ~W\a۶mhd…t:RSSM獊bƍ[y3f uԡRJkҥ$$$bڿQF1n8~mBBB8pY:ur֮]ڵkgISCa,>KQiii8::[A=+/S'ysyJRg?̄`DOwG[|25 ~B:2UK|@4iR‘ q%V?[ LyI:B!D%`Dvhݺ5qI\V!ʴZ

Y]LjLkp7KPڡPJK!ʋ`G5X9yd%D,a]Tn?P"(,!4 _D^&IMѧĔvh)# Bröa>]!X6$/˰(\HEGs(-Zw>mE%Ku߾S+N6qsU$EO /b.z+lZGRvhBQ$iaQ66zjkYT'(վϠAiʥ*J2 U޵F4탺Jc,|j`U#9)RǨƺ X7.2v*g컾ozKuge%(d؀wuVv})uJ$XH2.$e{(lPǐ@yXv/dq`)=$꣋_XxU»:Sb~k.F IX,PejCRCov0^8iOPai1NWno!eXҶ~unu|]: EZ2Ůp|cne4CQ!H$nTǫqUaQ^F훻._ cN,La;\vCro]EuzϑypFM&6-w'r IضʽYQٻaYMk*찪޴Mw*4. Y'6`fcUVu;==Tx=]8AId%Ie[$~7wyD_}ҶBs8ʍPU"}P(~3{ef=P-Lי^Z0{Y,Z>x׿ǢO5% IDAT3,O$F:x r`eKꪏq6ۊш!3JK1dhGiCzbn_}>!cصS(mPWFa;n?}^_4Yn]EP}(wM&)K*0:+֍z`Y Y!k\?U TIQ'D`iQ9{GڛgJڶokVz<Jm^)yJZ"k.2 DJ;h0Sڻr$&5 >eRm^0IYcs55dos86>5uǾS>#納r_4Vڑ{f>9 uFz^!(nKGQ9zsI 0WE:ˀV5c^KP[cU K8}E¡D #uǸ'gwΕ=rCdkv-+*ܩ%\+laC1(,~Ӄ:`j V3;ed\Fm?bYy։ x OW _EnMjF̻~q /J;q*wTN^wo1;=`r )Vnz~QڻaHOD7ꊵ;72!I4î w# בaB4{f/ms?ʽ2~~PKMMRV|@桟 g󸾳ecumuu䝙y}&Xk'߇FM.cݠ[sSa4PYwR:LOXjOI١P*!L:?PQw\;IyunD!(le]K[پqZ(%Dqur\+:i Ed4(mQ0f $~u&8X)*ʌAqaT5 +$^64`HO̝$:fCImBb?v#Edi04(l\ћfBQ:GҶD C桮XEy,> }rU:.͇~ĉȑ#RW;vΝѣb}6~-VbСDGGOw-ɉp̙cv#GRIOOaÆfO"̘1vڱtR 7twABaW?mڴy,1mڴoƬGyēLzRgYL=VJhU+TBI ShLMfc[xwNQH 9e_xo6MCqB\҃kvkc>Sh*/g(DYPǑ.[21~cfƍiܸ'=]J; !JKVe0bT>V荰6.i: 2cTu ᮡ3:i%ٴpRSVŖ hqPÒg RuM5l.Gz}=mk碦!G:tnXM{|6ҷlNgkQBi"C)w ּYDdHKbR7Ҕ`+tvU`2cO.fW[ VX[-i23M5ϥ4:*|j LI'5{8aQRiz#UeTXw. Ů%X!͝YKgxB::#g[p%SO۸Wlکk8k 8l=nHG~B!ktFe%[:2\:ˢ niɯy!gȬ%Ip|Į#wP`lc]-hdܥ N˒,-i _ņ5 zts,k!"2`ceVt7>Vr*MuqӛVUPVp33#XڸT ^bZ5[,V]䍊LjKǓ :ƍfEB!(^e6j묦}:Oܕv5GS,Ǩlh9ҥXT aɡ-Su<*CB!RfDvRI`䧘NhZHg:%jl2`wA9gVѶNe\lJ;!$XOU!Mo$8W|63Kgbo#ieJY.DYz%WEV5R*pQ)wi]6D/YVNE"w~I!*bbs[Gf0d-5tuC>Ic`l>XQނX19DY#J,~̲xUXڡQd. Oד7򜫚$$ y80Z/gRFT[;XYW5 H:jc^񵪞nZ6.IZ@C?+%Z8ݍ,6kXBAg >s^ Qf cAnj3| 'Umӊ~,=Sm[<-i~6+' T5K!J͛77fooɓ2d-[˥Gi?H'yg9s Rɓ7# B1СC\p 6(J&NHxx8'N`XZ9ԨQ3g΀ظq#ԪU RIpp0~!ΝweΜ9;w^xB9s8u.\`ŷi&Zh?̹sXr%wzYhϟgƍTZd [on  !DY6`ϟСC ]vO8~1p@t:+WD駟ȨQ … ̛7nݺDPPSL_d;l߾Aʛoɶm0lٲٳgh0a+VUV:v숋 o6III=zd͛G*U֭,_tSJ QJ$B2dܸq̜9-[zj#F`ќ;wiӦh">Sn޼Ν;qvvӜ|8XXUjM?gfޙjJ*XZZX QI%ehDam]T-t:SUSzXb߿?SLM6DDDPF .\šVr\|K#GᅮSڡޞJ*Sȑ#NGXX͛7?e˖={b~F8~8@9o0W\!33M9~KWrIx͛73fȣɄ ؽ{7vvv9sVZqSէ~ʕ+h42vX Rl߳gF^CT2x`RSSygj ܹs! =zW΋/hzk }v!JjɓK;tسg(j~ʣI3B9ɞkfbbbXf +VˋÇ`ۥKطojޞ?`0HJJɓz믿tDDDp%9|0zBBB0 ܺu0]iѢm۶jq;Tc;wHGRRk|wDGGܨ]6Wu o[+5Aퟡ{8tM "DIPW{, .4>|Yʙ3g8z(մ… }iھ|rׯ?˖-ٳx{{3d徔4-[Ʈ]}>@oN8Arr2ԬY6m8`:33ʕ+өS'YN>Mll,/^NӦMdggӮ];ұcGӶ;RSSٻw/iiiҹs|OڵÇӢE FGxYg{kRҳK;2ITzBAnpΝ7nLZZgϞ%++_]_z5Օzq9YzYoƧ~`ԩSxzz No>Ξ=ѣGsիgھh"x =J˖-M=<<ҥ ǎ7779Rdp!B޽{Yt)YYYߟ}{{lܸ*UIbb"7nٙ9s昞hz={닓.]ɉ/,qYlaaaGŊ]hiӦfqL8$>fʕmϟgرۗm۶QV-Ҹ|2̘1,2޽Kj4hz*4,kdo`_5h( Vj5jQ{mdPeO'z1?~<*T0-4 W}fm/]İa֭W֖,6liݻӽ{w222g̙3Xc_jgСCtЁqƱgO٭q ,̙34i҄#F[ƍڵ+F2mOMM%<<,ؾ};{7ޠgϞDGG3vX,X_Sۼ8_ȑ#iРA;v0}tU&uׯgΝNPйsgZhҥKYl퓺rct_ OrWu-t:|.jW_}ԣg0|S2fcc×_~Ijj*k޹sgSrжm[ڶmˁmYb.111?ɓ':s7'''ڶmM^)P=zĉ㻹W^yŔ\ ?^`{GGG{=O``ܸq̝;Ÿ:Bd( ֈ#P(ףGZhoIڵ -4RR%lmmINN6WbET*gϞ},0+hذ!z˗/h,"33kҠAϟO6mپZjԬYݻw3vXnJRR+PZ5h4&J,)y3jqpp͍7ou2gtΝ;M !xr!W^yqEEl 4iueҥ̚5/u2qD޺u7n-o5T\P.]9R?g۶mqqqٓW^yuB<9lUyWiҤ cf VYnjz 4Ù~.^YOYbaaA~ 5k{aȑMx饗P*,_pɭo& )ʜ:gMBBuɷ/))ŋsA5kVއx2Nh{,ʬ2[8F֭[oDgjZ$*B*kd쌨T YdǾ/d- eMc-[5*[Id)SҢ~|]F>c9}M,;?$55)SMKK)}p/^;w|2̥ηy5^s ۷oחƍĄ bl߾]湾~`k??\Jƍ ʕ+2_~aE'r]²͛2رcˈ#t ONzDu?-wK`ӳgOʕ+Gǎl߾"EȔhٲ%Çg億дiS^ző#G޽{ ̂ ٳ'kKm)Tcƌ֭[$$$aÆO N:Ջ$mۆ*sv;kԨƸs>===vԮ]"## J*reCXX3gΤQF.]'OpyLMM?~WR+ΣG(\0wܡA2CMLL8{,sؘOٲeyvi=z111:uӲeKv ԪUaÆI sjРptQ[RU+HĞB eɒ%[ޞkdddm66o)T5Jdp?xyy3ʖ-K׮]YN8 QQQ2l0ի +Wږ,YKKKlmmqssիWTZSbffT.|^w裏sΜ9ãGx=ЦMUz`Ob055M6϶m'--Ӯ];۷3gΐDʕڵ+ф0zhmV PRRVZS>! |/`}u(6{1c`cc|w"A_j  A$X  /5G֪U+DA~/ MMM5j$٥a kb<}իWcjjסIFFGaʕ\"-%D%|S>رc>CB akkf^ø|2AAA_]VU@7oNr:APH$X ŋxBI=CH?/Z~MbMyxx31G'[4h@ƍ:¥J0~NJTi۷W^^G#rD%|3W\vڿd_INNVXU._JJbUȷD᛹ue˖0ʄ ػw7o͚5? ¯N` ߌK2HJJbʕ,[TTTA~)K'O0~:~~~fOMOOɉ(:A_/Ճ hݺ5:::r-ʗ/СCTLk׮qa6m).\ÃϟӰaCMRRRزe $''caa(TLK.qY/|}}"-- +++Gd߿Çs5^zEҥcǎr"##ٽ{74nܘׯa=zD:u2e5߿ӓ'O[֭ː!C(RHO>eԩݻSSSzꅲrf]|||???BBB%11:EZZߗIĉXYYQpapss}}~߿gӦM̙3 \\\8p\ܼyggg ooo8 :1,,gHٸq#lٲDGGcee%`]x=z xxx0tP\\\dݼy3iii,\T*WqwwgʕCA(P+W&##7rJoN&Md޺u'OOJ`ݺu/_(U&`ݸq.]Dy5[nSN$Yݻw{{oF`` θŢE)|C/_iռ#QRQb&30P!W~+ٵk9rVZ1}tĦuִn###Yn6mo߾SX1:88pN:E ̞*+++XdL hѢ[[[Ù3ghԨv޼y,_\JHѣ+VA؞ǎER/;v,$''9pm۶2333Əϖ-[y_zUz6:t`ٟL|}}9s eʔ`„ lڴ={HIxueR⢫KeDLٲeٳg%J`R[]p#F䄒?I&8;;өS')SRR_˗/iܸ1r ֈ#0`e˖e˖-\rbjjJɒ%e:t(;vҥKi&&L@Vj~UbXt)=z`„ oߞݻ3sL OųtJl`%ImzGXHll̚>>5 wwwmسgs`֮]k׮_Y;vcd_pT*TݻweΕ*Uϟ3>Kڵ}\ =zG3"""O)++Lv޶mׯOJ#99۷oKq}7oްvZlllpwwqݤr}aÆR%J)s… ҹ'u)SFzRUUTzu s̹;wӧO͛¸kԨ! *))}Etuu._¸sKUU#Fжm[ڴiܹ7 |(##m۶ƍFN~T62dǐ@PK6mQ$}Jְ̛7oY'L^x# $%%:ٳg3oEd/]2e`h/^ŋs" ؘcǎ1vXf͚%ӫ%?~?7o(66-Z(<ܻO|޾}+{5#Fȑ#`hhHIII>˗/dF?s*88={k ׊,PAccO155^\\̼}ܓ,X@NSgVcڴiۗ7J!ȑ#_N8q"C QK,>|M6">} wD|||3@ګɓ WFtk!9sСtR}>Y޽ *ȝϮgK3{l6oLʕ9p LĀpssX[[3p@{e֭ӭ[7444Xf ,[L^{.k֬ŋ3h {ի\rJ*a .̈#dذa/^V6 m̓5k8::r(^}:III҂F'go'O/^;}4_ܳ|4oޜkM: RЉ'OOOg())Leddp! sA@n)RΝ;)%rY"##!φ رcϞ=yL>]^m2_d;hii1k,UJgϞqM/^EeC}}}-ʚ!VEey_tؑaÆQF qppU>0~x뇏t^zTR۷~zLJ5kТE ƌ5Wf֭~Z&+W޿O``t~lْƍ3uTΜ9# %JLJݻwcgg'мypttK.jՊYfjժ!_%{ݻGڵ#..///J,Ɍ3dڝ={СCY}]]]iҤrPOOP; &Mf9r5ks8p u֕tܙ3f`ooOdd$EeϞ=$''+;v^zիWs :t( fҥ ^x1!!!4i҄;J}}}9rdF `*Lhnʕ+W055%,,'NиqcZn-nƍRs?~5۷gɒ%Yd #FҒ-Zŋ ,k׎mRP!|}}y92= Ϗ.]йsgNUu?^jI@@=6n8LLLٳ\? ڴiAAA\|ooo§ׯ_e=~8`С2+߿Ϛ5kҥKvV4jH>}tʕ++V+VH (2(ٹs'=͛7$''!7ݧO444R(550BCC^|)C~vdյkW4h_k׮8::RbES1CCC.]ʕ+9s ׮]Аnݺ)f׮]ٳGͲe9rL%Jpyf͚)\0m۶e̘18q-[.w.|ΝcqMʕ+ǚ5kӧO% *̙3=pBnܸӧ)X 'Ndȑ 1 isTׯ'$$ իDze+Xr%dN߹s'7nd۶m)S9sЪU+/_pXWEE%q7iD*CJrr25kd̙rUUUUe~Vj/T9A[Ld7w)##… ̙rvvz³&kٳgׯnnnԯ_gϞaff:E"V)RDs L߿?d;+++ⓟˬYr^__իW]Ŋٸqm%Ru-1@O/e|w?,_.j͚֬5;_ڵ+]vq ًhѢ϶^\A[aip*WkkժŻwrp]i^ɓ'}ADD5 ,%%%+S%TRQFT5`444HKK*RJѯ_R.K5n8+Jv/_>UR%) zW^jժ]ү_?'w\II3x`5 M{CK&/`m޼9ϝY[[|ru놪koJ*W~u۷oaÆy BK%X|r~<%K P )?q/jԨ!m*#PR=)7opĉ&XTTT077ty KzFf6%swgS\vo%=#:*C\+))ZjaQVJ*u(129ס;JJʔoMM7|(O޼y#hт#F0i$-`'555|||;vz)Y%|CA~(y:ݻ|_۷L2ٶݻ7;wV[߿AGs~cժUh@fsYAA<@L0An_M:ׯS|y>6kkE{ jРk֬VZ,ZÇ˵)Y$C uuunܸaaarm˕+СC]6iii={7776 6~xzz䄪*VѣG̜9---/_ιs>}:>>>ԩS:̼yѣ:u899I%544hݺ57|򤦦r-6nܨpiӦqY̔)S5k&L`Ν3e˖'6FƍyݺuXb@WtORxqTfM._СCk8pTdɒ}hӦ gJ~бcG/_NPP˖-ٙo~Wr1/2 SQSM{tP!W]Utilق5d̘1ܻw/vvv0x`)HNN[իWgRrk.ӧLp JА^pAZKppT<66… 1,,LRWfϟ+J*%Ӯw$%%1|XZZ& VpqN]v_~3`װn{סK7`0 +סw 8;;hK+++.^ٶY;_rEܵkѣJr?gyG9iLfͨQ(++mTc0ezq=) qttѣbkk+,AȡGWDr F Cws"""`ʔ),]-OWP}L[[W^ɝ:VH/ 9WԩCxx8{ҒB >2_+++ShQ^x-,%%%̮]x'* DF300@^<#;w`ddٳ6l7nܠSNٶIـAYև/dk׮7ɓ'Kr+==$ ?nnn|Ͽ.(X uܽ{͛TXuuu%斆 , ((2eо}{t"3Meee s>ONu҅;&zVLDDDЬY3%̜9S&SUUM62mO8ALL C Doߞ5ky{rr2ϟ?@ hjj~O"E8ptxL4 s°gŊԪUKT}oHIIS ^xAxx8vvvy 6oLll,O%((HPLy%ׯ_~2_pccl g,߼y35jӓOʜ9\r\zEdd$Lj)[,!!!Y///x̪mٲeTVCڵk9r.\J*t!^bnbٲeܹe˖qe۷/Gѣ,\[:/͛1~xQA1c0`,--)Z(=zÇ9sPti)W ̌Ç3e @Vpuu}gڵkG???ӧO˔ $00PAY>/##-[evtt;6~xqΘ1Cϟ+6lܱHA;JJJTTIJ>IytÇhii喊 sΥ]vw… ӪU++Ƶk׸ty}}}5j:<{ ܉VZ;w===ʔ)CHH.\7oҬY3N|VZ^`CeڵDFFRR%[hGx Wk׮oߞT9l06l@@@ԋcԩ9zgfrǏ;ƤIaL8MMMvo߾ԬY}QX1̙Ò%KXp\B֭ l۶ 555WΦM?̌ʕ+k.pwwglݺUf-8;;3bDfx5jDBB*THj瓔ȽwQlYx RdIL (Tz|||ׯ˖-wޟ\.] >>>+ls]oN:E&MX`A{HYS~5L+4*|QhTmVTT *dyl|GRrP~}LLL8}4ԣ bԩPtiɓ'x{{3h )֭v"==]-Ye~qv6Z͊%<<<ڵ+zzzTRE:ȿK.]dHK*ŀ8tRRR߿?=z@WW???f͚0ФI `>}:kX~=eʔAYY˗RrЧOtttطo= hѢtԉx^*ϧPBq{ .zj{ҥKKO:ETTƍk׮UTlի6̭QvڵkqVVV~ނ B9svZڶmNNN2=HcϞ= )JVUƥKx ʕ;Ő!C?wPP-Z`sSJ*Xwp=8Ç'** ۤ@UUcJ_؋ё[MM4ܹp߰IHHذuV]룥%_Elll\29pɑ„\IIIod&',Ν]{/_&--MnC&&&I]ܪrq|tIMM%vر#4oޜ})>A>/&X2 ͇- 35j6_zYxOUMSzMhh([}}}_}nۙ'c}l̙e^O9t2r]# ˷ RsE*T@ |,KXbEsb~9ʫWdȼYf ,]T:~6oޜxd=m4/mrLMMC1i$5jĘ1c믿ell,/]$l>Tg˗ٷo{|rc}>|XPv+V`͚5,Y͛u4|||ҥK7oiӦ8;;Ӯ];Ο?O^ro@A|;D=y5jвeK^zɓ'X"ӦMiwa,Y;wPVVƆe˲i&]jnݢYf4n}}}BCC~: .drlܸCQF .\@dd$&M޵kW\\\'((mmm144H",Z.]HeΟ? !11455q4w\^JN0a ,SN;ƺux^SNaiiIzqaaaѤIvٳܹ@lmm۷=quu?ܜ NHH?~\W 28{ꅉ ߟD,X 5cǎѮ];ڵkׯI&^`P@QA;% <8Bׯ_VZy˷]ќ*4qϼ`kkKLL AAAyG~$ \N AZ̽(2,)gFQhT~,A !&X֢Bi%_}ȗ+Y2r$_܅ yu K}/qu+T&MKueΜ93QWWWgbN0qį*~rگ*==]>ݻwKxb?SNECCLn)_<.\ӴiS)3fHBB5kdʔ)2;k :/^b ٹs'xyyImn݊JJJ2l0:Vܜ;v|*y$K.KUU;;R^~Vϟ'**%KЫW/J,Çٺu+L<H XXXzbҥԩSe˖1j(~߳g4h;vӓ?+WPD ի bɒ%1J1N6~DJJ  )?êUbرH~XD~Vy`=f͚}Uk!۷oeAY9::J[ON>-%X:::hkkm6.^H2ehԨ?uҥ9sT/_2eHGŚ5ktTyCښ lٲ111K׬Z)S`jj 3qT IDAT)Sptt,Ai 9׾}{MƐ!Czzϟ |(+uV6͛7L2ҶYᨪ2n8阪*R100 **J:ŶmHMM%==]XnݻS@*T9s޽7O23޿K 4xqUVёFEtt46mQW |m Sj?cTT)GqAf̘X"w,1(++LÆ 144+J~fy>K[666x{{ņ x5-[dŊԨQ* ;_|v{011޽{4n86&T DFFJT6!))bWUUȈw}2A0 fҥK ˅ܹ{.k׮eĉ+V~ʂ wxl2}v^-?ɓ'3p@غu꼳gruk׮2FI3f VZ%bРAX[[gjԨlܸ;wܹ͛sXZZ{>}sΡAd>}:#..166fРA@:::--o@!;wLxx8#FvS*U?˗ٲe̲թS͛7D`` .DWWWMqvvf͚r Zn-}/d,,,pttҥKɓ MMM}ƼҥK&u/^6mPL,Yŋ9u {f%ϟ'00ӧlΝ; ヒuԑ9n``)׮]cƌ,XPa 䞣#qqqűk.ͥs4hЀ֭['Wsdɒ8q~ qvviSrezō7hذ!;w~4mڔuqe>>uCӦMCGG'J|݃UbEVZE6me˖ɵkժw۷߿d8~8&&&Km[lɾ}ۛbŊ1b:wϞ=ޞ;M˖-իWݻ qFy=}]vܿ#G0e4hW8pӦM#44EE8wGH"tܙnݺaffƕ+Wdc߾}122ȈGk.ZjҥK]j[`A=ѴiSr+۷oekxسg~-<3:kѴmۖw}Wf͚Ųe3fL:wLff&Ǐ/UYv-˖-#88~I&P !jEř3g5k?~~~YܭW33fІ+(v s~iԩ˵ `:u_|QKP%?t]pA;c.==p ?oׯO{ц:%''6lYYY,]T:uo߾|:n!!!iӆ &п.]]{GQFFv#\x1͚5{h .bZݩjN;@~ԤT}?Ȏ'ٜX,OCJ6EPZ>GR ] v-X~~~4jԈ3f'1Kwwd<ѝ ߟZjSJ@XV@dС4i777qss+wղL(+5k֌*ի ?jEq9ڵ+k+ēN3X'w2O+̓uq#FO4">}xR inDLn/mCkLNU sԘUCTu9>LF8q ,%cJNKJiiiJZJz؞~i6oLaa!?3.\@ЩSJfdd5`aam۰k׮ hL:S(JJpG G'1>-eaL6X+CH: 0ٳD !X:uD`` s%<<?K`eJJ nnnܺu.i)YN$''?G+Whhڴ)IIIÇRٸq#у7nݻtRYd ӧOg4 X׸B/Z3/aʙ̲W"`L)ˆw=-ݡDF!RPD X`c|iz9. %=k'Saɍ\`c[ S^uWbrWV播9/>QHHF!& ~+Wݕ\z:2vq+bUl.g )(f6&_ h;^Ț8%؜_3r[\YޖXW-.OcɌBQa ,WČlh`?/S m @Z*h`9N/zYa~{TZò\`RSn ʬX }v?NVJ{QWR2e˖: -[ʕ+ i`nnN  Wβ=ټy3t踺J]vB 2 hV_&Pj˩tշ{$T:5͍jޔrkR/e51渘)x.;Ƈe5t:O yw2);쏔|\ysؙʅLg7N\Q35REh[F^d\3o;HCJA q5'89>gu :*TW4fǭrղ\bYH ?6.34&8%_S 4,'TRs%Tj TgyCɩB.f< lLWs_+rS-VN^رc/駟'kkkun9pQn]_|xX03u>}/`iiɤIԩìYJT*ٲe ͛7sAի.]bL4֭[@B[DW/d>$JFWmMQ5ے\ԆyElMz&Ed1ӂO-foOr*R t΄{YݪF.1jct-%k+ imOa] c~ʖ.fLkhs13_+8ְ #1R@Tvqga+F9mlɌBciKniXs3 X26)*?eKTAP-%J#_mBQc/KճY'3k)9yWepr+jn,fu#rVjJݻM2g9S΋/Rĉ:uGǢEמ[^5gϞѣ,_;w`3͘1z… پ};ׯg֭x{{3bĈ*{ٲe8qO>#GqFΝ;Ǿ}J+зo_jԨ#GHIIO>͖-[hܸ1_}U !Duf6vt7빥OEտ;܈zƜv|~42&*GMT"|,9{/S״39~Nfx[s8ws#@%f̛*( i$F[ ޛvV |, i]`]\wYoUWwf?B;{jUz& 0ݜ*;TuCU>2}Sjl͜9Sgٳgl1W_ _b 2;F`` {lBUC+s+uyp5+nfF(5lV_ws53"6^L&]V1֣[һO w&CvZ>x0z؞u2Y'3r48 vd,##R {NjBظ$!h4Zȵ[IbddL#^{9[MZC;Z~OmPt힏Ci+GqQ5Ltr0e\Zn t6gj9jrVXttG.?レhe+u:w?I-nj KZEFV70R΄hʼj&MǜR٨Vq߻oG䫲{ךz))^ƜH/r-lt5qyeq37b]\.iwG7_kNtZۚp9[B;`QEAPV>'lNZÞ|6Ỳ,j*}5,"J-҈hO0=^Ȣŭ~ӯdokruČ-yϜMtFCf2 *u`i,t^GiL#+c^IG3S (gp2ֶ&46!%n]J+0prvvf޽,ZJ c‚aÆUzT!.?L;ok㡤)%哐_Hwsw1t>Vr4ej*5Y3Ew6bkc.s`cByE u5&6:jj]KEԯ_Tіϛ77nh'Z9sK/PV-wNc}]uƮ]nua|}}iٲ%o6`uAj5|pKN@@j'Ok"\|97owؘ~Ig۲>d V9lllOOOlD_>={_(D57rH,XYȈAj*4  0@&%%dq999脴&&&l\h֬999ԩSԗ5khwֶx֬@xH V9ׯϜ9sx"V2pĝڴiC636m \#!Bã/sXYY=R[뭕ׯv?~S/y1777: $%u$&&hK,sԯ_-Z{*nܸoIӦMW_ʕ+KNN>{Ѹqc̟Y}_u6oLdd$GaÆlْ3glR  /@DDC /j7n?# < Ю];V^o3aV\Ivv6e-Öʕ+yw=z4>^V?̸qطo_e^&nʛoRR*hؽ{7C՞/w*/y7[[[RRR(**yjz*7n`@ ;wK݄xPOMFzz:]vaÆlܸ3g z DDDШQ#z?,5+##Cxb/^7'%%?Ԡ/rĉR|2G9R8tK.eݺuо}{˜6mqqq:8P[iӦ M≯|2\raÆi˧NJ9pz{ڵkiܸ1[ne۶m >3bĈ ]h8q"3g~СCf vܙBBBP(lڴmquΛ7?E1~x?qƑυ P[a\?a֭ŋtivMǎٳ7x~9~wv @`` O?%00ʳ5 {e͚50j(_j , ??={ʷ~?~vFSӦM;w.ݮ];ڵks `BBBw?c5j`Μ9ѽ{*׽_}LJ?_߽;| bʕ[< :33Y&֭[tؑf͚ŋIJJҖGFFCӦMQ*h[ѣPMWX/$ IDAT)j득Mtt4Zg4h|}}iѢDDD`eeD5k'OLA-Ilق>>>-SBBBJ^مx\Tۀ^7o'''z).\P| 5ksLѰ|r4hx}{uL-ŅaÆ~z P<8˫̮=zbQ/(uQx\GY~=xyyU|ᇄӮ];prr*+WAtO=&''CLС5jرc(+:3JclL߾}uTiR!N;ve˖㯿b֬Y>p}ZnY~~>N*u+Ҳx7/ӧyΝ%JfܫJn::w͐!Cprrb4mccc+܇gIdTcΝی;ƍ{~7܇16n\z5[lI&h???222*0YXXw`sll,˖-ܹsPnrB|{=e_)u75pkbQ%6`99979;;;,%c`Yu/ cƌʊӧOL.kvt=Vu}|MuŋyѣF*$X=%^t.իWټy3:t`ԩ_^urh~~>7n_~Ɔ5ń#WJwKHadm*BTI X3>^;NgϞeٲe30bm~L8xV ^Q=q4;SkGe^B{#G"TլY1sssԩuf.nڴR+TVڵtG{9|;wo>_ }:JoHBxZ eҥlٲ____Nbb"C )s: X3g`ccͥKӞ_/̌3puu%<}hذ!jsQV-͛W:Ņ?X6ݭ&L`̙,\{{{bccܼ֭̿[ܹs%??;;;@T*:u*3f୷ˋ"nܸ M6N:xyyOw^puuŋҠAׯ5k֬;ٳ'wfҤI4jԈX ܹsuJdggH HHH 11mj7-ѦMjԨs=t!(4e-8y$+qqq8::ҷo{N/**СCҢE Zu۷sb'زe nݢI&9[[[{5jD@@uJ޽u?ciiIuw'Çё 63f[. 4oe$&&kV.̷~={Vr Φf͚o`T*Cll,4k֬.>J_EǎKMh4*'NܹsIU׬Y34CY 2 O= !e%uִnB! J` !B,!B=%BgB!L !\X4CWECWE*%/<6ֆJq^ǮVCWEJ.B!0[ O|@VYIfbܿ\#!CB]bb"cǎˋhCWG%_sNt邷m)[ӧwsԩS_~Lmgͬ] RV-bcclڴVZQ~} F\n`&MĜ9saÆ_^I^8wN>?xzzҮ];mZO?חF1e~~~ܹI& QHBj$&&m۶믵fb]_~ SSS,--8r|k֬UV3F{JVZ\t9s0ydbbbL40ѣoNoߎ;'OFTzjLLnϙZ`Üƍ1|oߎ-EEEyQ(ԪUKʕ͛7/Yl؝]B< $` !D5R033+u\Ty%;wֶPpww3駟ٷo\r6mڔy]5w233ԔӤIr)NBT3?>|-[VN:qMWXX3F*++LMMIJJ"""[[[-cc>Ȉ3<=^XXX @BT3| G!55;wsNmٖ-[8t*3g2i$cƌaǎߟ; VҞiӦ ;w{>>Y營ιwٳ= XjX 777 p{(eB!LZ@h4bmFڵ3;wL֭)??3xHBџY_CAOzϾu?ALcQ%@lj;3iJcdd_{#:%P( x w!B=%BgB!LB!IB!3E(.,`7={UvF4ڽ1UJ$` !a[7PcTKc;: ]!*M@2n>GG*]JNsmwHBP5˗:^F 7@x$` !W) FIhh(˗/UB$` !D5ԪU+GݺuKӽ{w:uꄵ%קVZ333|||ڵ+Pl5k{wwwz>666tܙ-Z`ddD͚5ӧk.u ۻTٸqx۷/ oGO !D5֭[quu%,,/^hР{ҥKjZjEϞ= QFDEEaooJfϞ͆ 8|0011Gݻի3fo⧟~nݺdddзo_ڶmKXX]ve֭˗/_%KH5 XBQ̚5 ZMfL/d֭L8ٳg_ЧO8p Ceĉ b„ pa<<<ԩSL>m۶?ۗ5k,H8p}ŋ3{l̙w}|}2!%"BjdȐ!|wOJJ Ջ͛7kߺu+]vLrss#99"򰰰^D^^yyyddd*W(0h 4h3 B?`ii @:uhذ!QQQz=B<2K! B e[ZZbffFjjXjj*888޽H9}),,Fz $,!&4 qqqԩSLufݹCZZ^Hzz:C ᫯n $%? 5Җoܸ 0227`˖-:HB`jjo߾:]ŋH4h^&ģDBT#3fܹsDGGɓ'iժ?f͚\vWRfMy=XZZƙ3g&88^{ W_%<<r)}Lrr2 Ǐ>1t%ItqjԨs,--{pj5۷o'33&Mh)((`ӦM <Rs}~~>ӧOܹsj֭[G5 ##/Z͛7Yr%5k֤v$%%r9dڵ sssƏO6mu_b))):Λ7sJVV~-ԩSR'??<<<*uՁt !D5vZ>}: FC~~~߹s'EEE|( zG}D111֖kdǎo^{,##ÇFW^Wvvv;~:vXxIBT#ǏG(  "N>KKK מR8z(dffO ;;J< hܬY3n޼YxI BT,|,?NddX (Y… ̙3^zၽ=ׯ_=t)RIvv6>нxTHBjBP3`\\\`Ĉeoݺ]jMLL *"99 W"]BQٓM6ig]z˗/뜣T*133mY=صkU*55siIKKÇWz[[[bbbtO.uNHHyyycڶm[g,!F BVVo GGGF]16mJ;2f6lSN Dx饗 ĄOXXM6ȨT%&&],] 6In033ӹFP0f233iРz@.\weR0IVVK^sF"tWSҽ{wzI^^^)33///ڴiS0$iBjHP3\AqKq022+!u2K! ҒDZggONNͨP! dvCW)**`ܹn]:%BgE(BgB!LB!IB!3 XB!z&K!B$` !B,!B=%BgB!LB!IB!3 XB!z&K!B$` !B,!B=%BgB!LB!IB!3 XB!z&K!B$` !B,!B=%BgB!LB!IB!3 XB!z&K!BL ]!xR{QF CW)**yP( ]!*Mh4B}Xz5tؑ-Z`brP|}}9{,PuU02_>>>B< $` !D5ү_?ZlɡCXnVb̙Gxx8kwu'SSSmb$` !D5Ɛ!Cx ]jGP~}}]CWE*%BgE(BgB!LB!IB!3 XB!z&K!Bd/B!xHnݺ©chakCXXw9_}ڽ304S9L(IʹT(E7ZDrȩBذ>W>\k>k&00@z{'%B!p1/"7&33r劾&fҤIL41biii/_n񾾾_s퍻ϖ-[8{, g}FFF|gSI%.ݽHSRnnnTX=ZpppFccǧ q'%B!p!իW/դӓ:uBB2%Ni /;2걵kז5;tPygJNNH%N0fLE{Cq=v tl'mI,!pNخCQ K!$B!p0IB!L,!BK!)B!p"NzN;e nC[& B8IbZbLCqIA1V[$XB$!B&v١$4V١Qb IL"j۶-ݺu+yv,{I,!UVW+8wcn.Bf…L:K2zh?dzh"رc$ܹsL8޽{Svm̙ժU FIJx75j:ubҤIEbԩSر8Ǎdž ӧY^ϨQׯ~->>>4iҤ[~}4h@޽K|pe` ! iݺ5}QmFo>QS6g ݎjn{xx8}%$$???QiiiLZ-5jLJ={UL>sz-Μ9swGY"B( :t>v٬lXPHӿquV믿[ousNz=@Yr%uJ!$Ba9rM6-=??xcu%99HѥK~WK,XVj׮3 E HLL䥗^rHBY"B2m4.\HZZe˖駟2al;̚5a3P^=EaԩXVjԨAll dڵL2_~jժ1l0vڥYr%UVeŊ<@͛p` ! "//Cҿ:￯ϙ3EQ7nFs2o<.]ĺuHLL_رcdeeaꫯX,deevZ0~ƍDFFʧ~Jrr2~!֭yj7p%5ݻwy p̟?AuS=gIE iB'Ux髶3fK߿݇Cm?a&N>(3I B!I%NR%O7pi^A["5XB$=ˁsItv(.GQwCB8;!n$XB$Zi>0:#%³]4~KadP!]#w9t=b˾B IB5 u8f=f},e[nۗ5<,)2So.ǰg=5lg Kj<i X3m 8o},Nb&98lTBlUte?+nhCeYEYRbIY 3=Cdo?E-"* xe5Z9iVA\φbij|F1X0ŖGg)day83F_1 ?P>3 ޴EUs~,NbM?{D9, chC~WIQeYabn9[Sny>|O9|ݖsr,ƭN[r, }źyY?O֚(>زx1n2%e/>M+WK RiK`_۱}H=<2<7y)`5zbGDFPpdYk&Ft|H])Avr}NCKs6-ī8?N"۳%󁡔Kj<>GF8f-ղۆf!xv"s|ݒKGo @=7]`_vK!]+Tش͟hWKJ,جr$肪y U?k<|1?9b-K0Tkjc 5[mxCBY/5ŭN; n!QLCx.-zx w>ON4ZctkX _n>}6Pw@\b]dP!]FI%ZЇc9ws„|M`=j?d9r. ]Ua vn݀[UÒ|H8Yim<՟2ƒf2ǣ U]{Bq1'œ'-o|C`9wS^ UTS~,N Ri+)?$eq15en+6+hn}ES߁LrfqfZY7$BqW1A}cܛ Pg}{0` ft{s[vl7<|6]]J: c.%VzO~:$XB!NH.c )8pOa:; WS5X ,]@%0rۖa˾ń Knο_u6+})v>U T’Pxo6r6JK9;;!JL,!p+ܕUzz:/ѣRnkt:-1$B!n#?NN8p~%BQ&BBӱcG F-Z֭[УG;w.}ݻwjժ>pB|}}/x'ܹ3O<nnn\t Dzؽ{7Z`ѢEj,۷駟wQ1Sߒ+?0j(jժԩS滦o>&MrמQR` !]y̟?>5k֤{lݺ-[WTvq˖-?~~~ԪU85b$&&ri222(+ѣժUd2 fc۶m>>Efӻwb܅/K!\hҥK S?srrصk)))걈CF\\<}iӦ/_ԩ`|@ጛdbժUxyy}۶m{Ǐ'77WfI,!paIIIjͨR !!!?vk|W3vXN}ʎ;ؿ>e~gONZZ Ef܄H%.FAVhӦ Vz-V^Ν;iҤ /^,rn6m6m/%Ơ VXA͚5={6-RG͚5iذ!:tRJ_-ҥ gϞe̙GOǎY&ӦMd2ׯ'33~{{ӧ; *nwvBq'Ux9Eկ_ p}JLk׮e̙lݺTwqdoZH!Ŷ0ZM4͑ qc2%B`` ! i߾=ʕ+񃂂xJm|!D!yP!$43gN^UV~W.::e}D,!pnjd;$]FJ7H%` !(€H B!I%B`` !B8$XB!& B!SBDr?0\FP'$w١qK$B'9JěK䕰(/b4ȯ*#kI;Ajz^n#ӤFg"DI B8j+G}!Cҹq{H%TթSzH? 77g2zh'F'DK!mxy7C3.]8;4!J,!p1ڵcժUl߾Sf"dƍ[W_}ի3n8&L/BY`ׯrh4>S֭[ӧcǎe֭nZfرy\˖-v|lݺ3gb0v)Sضm .lٲjbG᧟~bpl½[RK!\ȃ>ȷ~1b111iFm饗x7C{='d߾}ԯ__h4|żyh+# IDATر#O?z+VM޽{^cǎرCݝA#0o|[|y>s^x.]tBH%.nc՚3XV5jE?0/桇b߾}ԪUcǎ0Q<>>>|w|'|W>p!\` ! ٷoڵ+l6CFcM4ĉdff:mڴaǎҤ^_Wa2W뉊R6 Bqwp!Zlٳg6mSLa֬YX,l6SNv]v1du놢(9D˖-ٵk c̚5zjժ3ϰ{n?hDѨ{`>}Hu &LpvBq'ڳ{7|۾i6+r,>>uѴiS4hcX`>|ڵkG͚56mW عs'ZtߏNٳ=z/ffuV4 999޽cǎqiw&Mb˖-hZN< oݺ,p[cǎW]~gF#$$$ơC\2񸻻 7=}CHw/Fn`*ȫtQU? !}O;;;L<4l١q BRbٜMhZCbI%B`` !B8$XB!& B!>XBDOr@wQZm pv(BIIV'x>gb6>W1xqv8B$XB${wXbfE,!psY066vѣG~b).{&IB6fA… yg4h{uvhB8, ! ޽;˗/'))֭[ӽ{w}Æ ŋbرpy.]ʅ hٲ%=zPϏ%66P"##1 0Pڵkٻw/yyyԩSgy777}ɒ%deeѯ_?bbbXz5~~~jÇ@.]h޼9z_~OڵbÆ 4mڴ!3XBBf͚E߾}&,,_~oFm`ΝX'xʕ+X,ұcGhڴ) ,`ԩѼ̘1VZqiuFAA'&&ڵkӼysV^ËYx1FBѰf}ǎٓSF ƍ+ӂ"3XBb :%$$ܺuknܹsYf ͛7'** ___>5kƤIhӦ ͚5cĈxzzM-`0RNvI=zKڵҥKq,Y͛III!66Vm4i: LJ3gҡCk??CB,!p1AAAyہw\r,[L=j9u g F5j믿&))4(J$~igժUX,O6mΝ;GLL5!??^zQFR$K!\N#''*U*l&##x؀hZW^L:^z/Ry+VffqyVz|EY,JXX/ucJ,!pa'NZj7ݿN: ~oXWn:ZlɓO> pͬՍ VJDDD_LJ>D _! B3gAFF/fĉj[BBΝ… ǎcoߞ/^ODDUV믿&>>ΤIh4$&&OFGGMRR111ԩSHÇgĉxyyQremFpp0;w^ >>qƱ}vNaÆr?pIŴnݚSrE^~eu6 iݝٳg3k,G}ĢE^: *2~dذa;O>$Nb+WҲeKvܩ&XK,z?~H&O\dg}ٳΦaÆj`r,_\=ְaCIEFT̙;=]}9,~Ocgw}ڵk9s&[n-).9 iԢv &MȄ1K!$B2p@*W\jW^V"{!\` !5c4 {WKWru6oƿCwI%NRe["wm-H9hTjӉ@_gG"-K!JvЪB8` !B8$XB!& B!I%B`` !B8Xj ŭG1z8;!JLI~Fޥ \fb5pCH%Nr, ߟI&9f0Qx$J,!*((z?>gϲvZbccYd Z։ x` !mz=5k֤yԫW5jбcG:;$XBb̑#G8<[n}j^#))8,Xaaa,Z(:t([l!.. hظq#'O&)) &|rի矓ټy3E_С?IIIx{{Z~"Wf#FpI.^Ȟ={SNO!A,!p!WꕆJ*U6mZ峉'ҦM5kF- cĉxzzSO{1}t&LAW^(<ѣGիo&K,aȑ(y rڵKQF=AQn]xꩧ NڵׯuֽU:u(I%.W_weO?qF}L>gϒʴi8p~Å 8|0'N… }֬YÅ dӦM\pe˪{!##<"## GQ"q$%%ɾ}P>>>tޝ &A||<_}uoϞ=1G"K!\(Er刋S:u ???ȑ#ݻ72e[n ȼ 4jԈ_.IB6fYf1bxbbb޽;5kpvxB8$XBB&NH~裏HLLm۶Kh4 طoO<z9s搙ɂ ([,|\p-[2|pz=TPEK/Dƍ̙N^^ueĈ&MҥKlٲ(O>6l`tޝ^ϪU~ 4uڵK,qG%B!p!_}[n 6yg7&;;H^y:uĞ={ԩS<4i҄QFqAFϔ)S7n*UW^dff}y=z4IIIK;$XBbl6ԪU '==~L;$XBbƍ+BFFK.-R~?Nzz:k׮s }ذatЁI&ѪU+;ƙ3g:uرcjWTT*TM6@LSdd$7onGa08x 6… 8p`^1c+`2\2۶mVZ>}`2([,˖-\r{ҤI7&` !B8$XB,ʵO8Q-8/ 7w­!JTŎ_t?ɺ &/ǔSC'BiK!$|~#۶Fsy{@N~A\3(AJm|G1[|*>ا vy9V;:^ 12'_mG'˹Ń, n%LpII7i_~Zv'Et7B{CM̷e/dZ?a!Rh/G,!p"ЪVuvwZRn正s[N-`%3F /)k(k|.4/]-Q a@#M6 2%2,xi"t-k̶4l_+Gsh1S0jɧCH3XuğKFܯ;m4z`by.:i?]0c児4θ5keљvޮNwJFh #žL C*fY|ϫ,?t 4ꎟXvXR 3 pOOwz_:K!]fhV>[:j|.[/ietdoNNmO-'pV1&pNҠf;gakdNd0T< #CZfͦd[&C&\Ze2!.:Z"rAZΖfyj$)G.m%@B߲ y"ɧr)CgdK!]<q,碳9_SȼLj.g]lC7)sdA+{%3O΢WlV oβk)&f4oV;rNnoUq˙+{l>+S Kc/)sMM܍H%['Iy7x6/O!<]oZz 3h/dD97f*\L181o43'I6#|eFWU``#+9ȏ,3hI‰|5;7a6 WTl̡-Q`Sٔ_̈́lYNۈL5hhc幂"  Kɦ>X`c(7LpJ*FOR~wտ ]d 5ſJٗg[[M1(4<h\8$XB$'QϺ{w'aPacF0->ܨeW/ᵵC7ZkyX53%O g[i'e kRM6g`' &L(qB!nh=]915wpH ȥB 7=}z7p*aZy[;t#F-[ÓT\+1:_!F ˻bs:F-O-3kyk M[G+Ja=S[?= B`7tN(Ux-ԃJ B2ZAFnBQCc/RWF)OOB@Z?,5̨IT4Cܴ{FˠQ^@ ܴ}o fhN`%OrͶ5o#Sx;Ag|tN(ihyy ˹o#>߆NÈPwF8Rv/w9sqǰo;SƈW_nݺ  %$XBBZ-.pz{{{zjILLdƍ-[XްaC6mƍcƌ޽D"""jűl2=ٰa'N_ g6mDbb"ќ>}xH 8}ñc(W^jUS}Qzŋ/ /@Ӈ *Vә?~}}}yhݺ5!!!f|I}ѢE/_ ]EѰaC{(+K!\Ȑ!C1cr!pvw|ǘfV+s'DZjj*deeqI]OVV;v")))h49~8իWWǿ'//NttZcHiӦ~ߡCη~("w!pPJN8Ql{2e!))I=v<==wH zٳgӾ}{=h&sŊQHرcjް%` !deeW+9/R|8 .DQUFrr2O<_| Ϸl̞=-ZPJ4 hڵ0eؾ}3tU=$B=CDDL>c̙,Xuֱcϟoѽ{wVZ… ={6~&Nȳ>˦Mfr,Xf㣏>b<㔗}Fɚ5kؼy3]vu/b٘>}:'N*}ٲetRz->c,Yr\RRB=nډ,SPP@׮]֭&L`ΝF[LL رBȰaÈ`7x~GQQQa[n<3OTTw6^:wL?~7d͚5455Q[[k,]Gl6ɓ^P[[ɓ'/))geɒ%]ˆ! X"">nBCC[oZOÍ&L@Ϟ=3}Ν͛dff8.$${̃>SLqw1n8^z% tD*,SWWg޽{7=СCdgg_nra2l|7B\\555Fk1 0/^g2f{9ƌsM-Q18p*֮]v -[_R]]͂ 8qɓ!??_S]]ҥK>~-yyydggaRSSY￟˗rJ\.֭#""?UVqoߎdnϜ9!++I&0vX8|0f. <"moˉ'?4fy衇+gڵkYn$''3j(>=z`ԩl۶QFg'++~":toӧ63m4š՟mX~=< :hv9>b W?S6oYCԷ&wS0,S0,ӯEDt%;?y֟qw.EUDD%bKIG{Ӄ?Y|G,ahuW#r{hKDDn'|Lz!L&ۗ 6nHMM,`mφ X`={$??{DCL&DEEñc߿?W:pA㉋cԩԩScǎa2g̙ٳNCCcʔ)[FOfP__O޽رci& 3gKi&ӄݎ+rhKDć̜9D~0h z|`ϛ7}LZZ[laΜ9F{ll,#F`$%%͊+'xt~_0c sqw3x`q^{fuZ,2229h[lVbP\\|DFF;w&sDD|H^^ϧFii)pii?9FBrss%*l61k@]]!!!Ƙ'ORWWG]]eeeڛHHH ++P:wdrs͚5ybc*..TV\ \ڿU38x [nm5EZ"&N:J!!!q9㽊 h׮Gj# /kq8Xfܸq7`%"#\.tرjjjjTfyI&Aff&.'^Ю];uK/СC)//E|ED|Ⱥuxꩧ ./!!h߸q#Ǐ7'L_YŋLkɓ')))aرXVrrr19s0d=ꑺE|>}:555k.;ưaÌ^xw^ԩS=+V[nl߾"rssٳg&LsL2)++sk_z5l۶ǏsqcϖȏFEDn-r䫶5 -uޝvqce~~~ٓ +N'}l6VJMM qEϏ0'(( .~ٗٳg9s PYY \Vuu5MMMaN:ƨ(zÇill4j.CUDD$s>?J.1[wv"%"E~ ]z 00,S0,SE NSCu?#QvӣrDDn3gԢedyI'[s_l6{fDDn!iuRDnEDܹs71ocCyyi`Ge޼y8N@dd(`{7ޠGy*..f޽ Ouuu .p9SNPVVƾ}iV[}}q\VVFeeqb)//gϞ=TTT;e!+TTTȱc1b#GMMM̝;կ~EZZp)\=3tޝ9?OJJ ̘1}p8ŋ?ٳѣtŘU{׉Pˌ3HMMY X"">#.En_[r.\b̛77xLxx8˗/7] !CFcc#Ǐȑ#n}nꫯb2XbQD("C>Srss_vwEFFvvŋ~wvɅ jvabI]] }dL& 74^ p\lefݻwu3U\\+Brr2qqq-+VLMM C;0LDEEԦh2227n\k׮%33'x;vo*BBB䎢%B͆ p b4 ٲev;Ǐw\1ۻw/ 7TchhqR<جϡCp\\.lblShKDćQYYI~~>\x{xba̘18ÇIJJG{ZׯYGN=X""^bZlnqfF9"mED_|8oXpM&BDDDD{XIENDB`hyperfine-1.18.0/doc/execution-order.svg000064400000000000000000001050101046102023000163030ustar 00000000000000 hyperfine --warmup 2 --runs 3 --setup <setup> --prepare <prepare1> <command1> --prepare <prepare2> <command2> --cleanup <cleanup> command1 prepare1 command1 prepare1 command1 prepare1 2 warmup runs 3 benchmark runs setup cleanup command1 prepare1 command1 prepare1 command2 prepare2 command2 prepare2 command2 prepare2 2 warmup runs 3 benchmark runs setup cleanup command2 prepare2 command2 prepare2 hyperfine-1.18.0/doc/histogram.png000064400000000000000000000255441046102023000151660ustar 00000000000000PNG  IHDR `g pHYs+ IDATx}\p/7r/+e+,ILț ϱ-ܴՃGEϘ R@dfnG{D7YJ 7 Z¼]s]\8{iz 3b 1H$EL"IS)@R )b 1H$EL"IS)@R )b 1H$ELtgϞҵ`zkzmcxG׋)偁kWVVַo_Uwaʘ}g:::><===44TY4mڴ6lw}L744$''uuuFݻw;{ٳ݄ TUeʘzk^^^gyO>Q JLL_eoo 7n{QS 3}aʘg ݿ+sZmԬ]6''gB7g߾}111&, tEjR[[i׫W~^Rf555EGG+/BCC [mƈJ5SΝ;xellM8{9r駟nhhBTVV:88??V[[|m? PJ)&/2:tݻwŵ}^o{K%%%)i;j 0d*`a06ǔ nڴ髯ׯW ,,O>{ zGF9j(??fe~PPбc^y/rrry??ٳgus"z괴 6\r7ߜ0aŋ,Xu:O?=vr?mll,X/--y.]se˖ÇGGGO:… ΝU23gƍ|ҥ?0))ɐ!o߾wާ~:%%q۶m7rKVUU}v k W\9s3 h3gΜիWĴ}XSRKII2dMll?_ !t:^>}={ B3fL8!!!EqqqÆ ׯ˗Zv;vh~^{lmm 0k,e<ɓzJy˛o9siӦ7B{'N\p*o~g ߭e˖=?sܹVtÇؼ+UUU3Ν;rH{{ &ӧӿ&p -@^7oެ2eMMM#FB̞={...B}ǿo7ckkb<'((hر)))l``6Wyȝٳ~n~~N;s8'N<8ZjjjBBѣG[ZZ7mڤ3gKr護.>K&$$/߿aaammkה%Kdffn߾~ݿKx7+WXBo۶mΝnVXdɒnܸaccS____6l[&Mu麺,$ b @j.\8}tOOψↆO>ٳJ YtiMMͻ+puu裏?xzz._w.\\\*++!&&f̙/bt֬Y=رcDŽ /233Z?u[nzjΝ}#۹]C ȒߒVuի.DRY>)@R )b EEEqGZ )b 1pg]RP@kBFKS ? f윝/^hooochWKKŋ,)4II|G3#!$$>ppp1pg666p|Ȍ#@R )b 1H$EL"IS)@R )b 1Hvv[ ,)@R )b 1H$EL"IS)@Rtܝ1H$EL"IYX#|c NIS)@R)?xϞ=}}}Ǐ)"^УGӧO544̙3e„ &, zaʘPXXxGGѣG_rEYbŊ+Wfee9rDՎ3ʢ;vlٲvܸq& ]q`+}cްa7|3~x^dɒ8!6o}۷/&&ƄHsSjkk===%%%"GG#F ! BCCEkT3$8 @VL;w#""B???R???efeefZ-2|rUHEfj4ô^7~i쎋t:Ԩ1e…6m:p@~9ZVSQTUU)+Z"cNNNFL^3cJRR҆ 80``Vlll}yo~Mh]S8bqIS)]NDZ@CL"ISb 1H$EL"IL.Kxֆ )b 1H$EL"tI<` )@RĔnZ_ )b Ths)@Rɓ6lzȑ#n榬j*;;I&Ս5jݺu&, E4<SƔ(^v={''L$EL"InN-]>ֲ)@R )b 1HmvsX@FL"ts3t? )b 1H$ELуB1$EL"IS)@R )b 1H$]hgh )b },= )P )@R )b 1H$EL"ISvs芈)@R )b 1Hf IS)@R )b 1źp !IS)@R)Ǐh4;w4=z􈊊:}aQCCÜ9s|||\\\&LP^^nzQwLSnܸ1hРVWXrʬ#Gh1c\~]Ycǎ-[֎7ل%΄ۊm5Sgdd,Y$..N~z??͛7Ϝ9fڵ999GBlܸO>틉1aURܔh奣# EEEMMMEʢVk]3Z&zLB)3+++<==.je]3h4i^otU%1EՊc**epE666VWW]Ԋk2P=k\ecc###ENR JQ.))9q℗W߾}BBBBBBҜL"pww>} ”~LS=:rHezBu-Z.!!zذa{ussSV[jݤIFn:[[[.SƔ(^vFIMMMMMm)33333ӄegIS)@R<72:a@h )0 Fk&h )c4H$ELF<%Sa 9b  b 1օ1B)@R )b 7˷΍#)fE@8$EL"Iqn 'Th )'UUUƃ+Ɯ܍V`QoJCCÙ3g~k!C!<;uRS/.1%99y}Zlٵk5MbbbZZZHHHHHHZZ)SP:P8sĔɓ'_tw}rE w^7773sĔ-[qFIMMMMM5C у@Ruk;qqEL2db!`;GcJǔ+WN>}ƌB={|閮 $ۻMwd B2was]ӶV[[XwN? Or)EEE:0'::j իW555&/&,udjL;40]LogMj|?׿禌)۾]Yx|x/a{BnӶ;jM-3f﶑x7nۣVKi;jdi~E7nZQ~bz^ޢ~!7|co?íVKIIQGYY9}0_*t:]RR2r%__߶PMMM```YYz{Ӹq*5]ֻwon,S|||lmm+++ sZdxiݭZAֆƭ [50$;88FFFZ$ ?/fͲtQlSSS-[AhhwZZڻ[WW3h ˖|33t!F.h҅[n\7DOEL"IS%dgg;99w\NNN:>b IDAT[zaӧ'Nh222Z">>׷GaaaTRnӟܣG~-]ENӃ4f͚7^^^#G,((-[J?={?~ST>Wk4D,n4޽?ܶm6tP-@`˖-k֬)..7oK+..^f,:|prr~jWZe_~СC%%%_}Uqq_l]JJJmꚑa:2eJffc%$$8;;߲xtt|ɿ'N|Y{kz+>c͛7L-uzwvn߾}3g~nj [[-*bO<1k,tV,Zh3g>|xu[}[/^822z;W_5'Lո^oiiy衇?w|^nܸagg_jPׯ1B^VoӸ|!CO-ill,**6̉.,,lڷ~kNLLѣGΝ; /kYYYziCj?!g}t?6tM1Rܲx+f{=ٳǎ;zhj 6n=n7^g>S߲Et{ݗK.577?aVܺuҥKlNJJt'N3gI;$Uxڵ677ۓ'O6mfx gg{[oeܹ0ij[l9vؑ#GT+Tmz>Z5?jjjz衆[[1ct|cBz˻j :4==]1dȐ~ߗ_BƷnݚy8q"111 >>DUIv_[ W$''ښF.\7o޽{.~Vֶnvĉϟ?_~QQQ߲uckkk 3BնZۻ?WZeM@.\^z%!DXXXYYYzz$MxVV֒%K'x⾶l)5npM6}WSNR񢢢pesss~~~VV?Q0UvƻM>sLzzzTTn\ÜVEDDwޡC۷'|ٳgϞ 4E&j7o޴<왤񌌌ŋڵ׿nRk\aÆ 0@:IGu :tԩ'N!nw7c--- ߲e] QZvmqqqbbKii^t/rWRRRqqڵkjhh8~?~9eÇ-[vܹm۶S9L5C)m߾gѢEߺukmW^m˒P77]~"=ޑzJn4޽?rssw}n͚5o"?00W#F07d>0U3~׮]{1GGǐoii1SKR׮]7o^߾}-Y|]u4oY*58)))fk#HSj6ލ?i{-YNNN[l-N3cES)@R )b 1H$EL"弼{ƍY!99w,]UTT([n}7 ѣzիG;+(Hb&h {h_6m?ᑒr֭ zyy?6l… &M~JKK;R֣GooѣG߸qCfHd8/\rҥcǎ|^^^[uV\\\PPPXXXBBHsSml??~~~ʴwUU 絛駟AbbbzzzSaZѴz" ߴi}}}߲۽{fff.YСC&-t8~_;wW^666O>[ouq;vZEL`VSNy뒒Λ7w:t(--ѣϟ/^裏`Af윟?1..=ШQzz왟qڵ{/66< P^^ȑ#ۿ"(((11111 0'W޽ߵBZZVsb4~!Vrʕ+W9@BL1HB-UnIENDB`hyperfine-1.18.0/doc/hyperfine.1000064400000000000000000000233711046102023000145320ustar 00000000000000.TH HYPERFINE 1 .SH NAME hyperfine \- command\-line benchmarking tool .SH SYNOPSIS .B hyperfine .RB [ \-ihVN ] .RB [ \-\-warmup .IR NUM ] .RB [ \-\-min\-runs .IR NUM ] .RB [ \-\-max\-runs .IR NUM ] .RB [ \-\-runs .IR NUM ] .RB [ \-\-setup .IR CMD ] .RB [ \-\-prepare .IR CMD ] .RB [ \-\-cleanup .IR CMD ] .RB [ \-\-parameter\-scan .IR VAR .IR MIN .IR MAX ] .RB [ \-\-parameter\-step\-size .IR DELTA ] .RB [ \-\-parameter\-list .IR VAR .IR VALUES ] .RB [ \-\-shell .IR SHELL ] .RB [ \-\-style .IR TYPE ] .RB [ \-\-sort .IR METHOD ] .RB [ \-\-time-unit .IR UNIT ] .RB [ \-\-export\-asciidoc .IR FILE ] .RB [ \-\-export\-csv .IR FILE ] .RB [ \-\-export\-json .IR FILE ] .RB [ \-\-export\-markdown .IR FILE ] .RB [ \-\-export\-orgmode .IR FILE ] .RB [ \-\-output .IR WHERE ] .RB [ \-\-input .IR WHERE ] .RB [ \-\-command\-name .IR NAME ] .RI [ COMMAND... ] .SH DESCRIPTION A command\-line benchmarking tool which includes: .LP .RS * Statistical analysis across multiple runs .RE .RS * Support for arbitrary shell commands .RE .RS * Constant feedback about the benchmark progress and current estimates .RE .RS * Warmup runs can be executed before the actual benchmark .RE .RS * Cache-clearing commands can be set up before each timing run .RE .RS * Statistical outlier detection to detect interference from other programs and caching effects .RE .RS * Export results to various formats: CSV, JSON, Markdown, AsciiDoc .RE .RS * Parameterized benchmarks (e.g. vary the number of threads) .RE .SH OPTIONS .HP \fB\-w\fR, \fB\-\-warmup\fR \fINUM\fP .IP Perform \fINUM\fP warmup runs before the actual benchmark. This can be used to fill (disk) caches for I/O\-heavy programs. .HP \fB\-m\fR, \fB\-\-min\-runs\fR \fINUM\fP .IP Perform at least \fINUM\fP runs for each command. Default: 10. .HP \fB\-M\fR, \fB\-\-max\-runs\fR \fINUM\fP .IP Perform at most \fINUM\fP runs for each command. By default, there is no limit. .HP \fB\-r\fR, \fB\-\-runs\fR \fINUM\fP .IP Perform exactly \fINUM\fP runs for each command. If this option is not specified, \fBhyperfine\fR automatically determines the number of runs. .HP \fB\-s\fR, \fB\-\-setup\fR \fICMD...\fP .IP Execute \fICMD\fP once before each set of timing runs. This is useful for compiling your software or with the provided parameters, or to do any other work that should happen once before a series of benchmark runs, not every time as would happen with the \fB\-\-prepare\fR option. .HP \fB\-p\fR, \fB\-\-prepare\fR \fICMD...\fP .IP Execute \fICMD\fP before each timing run. This is useful for clearing disk caches, for example. The \fB\-\-prepare\fR option can be specified once for all commands or multiple times, once for each command. In the latter case, each preparation command will be run prior to the corresponding benchmark command. .HP \fB\-c\fR, \fB\-\-cleanup\fR \fICMD...\fP .IP Execute \fICMD\fP after the completion of all benchmarking runs for each individual command to be benchmarked. This is useful if the commands to be benchmarked produce artifacts that need to be cleaned up. .HP \fB\-P\fR, \fB\-\-parameter\-scan\fR \fIVAR\fP \fIMIN\fP \fIMAX\fP .IP Perform benchmark runs for each value in the range \fIMIN..MAX\fP. Replaces the string '{\fIVAR\fP}' in each command by the current parameter value. .IP .RS Example: .RS \fBhyperfine\fR \fB\-P\fR threads 1 8 'make \-j {threads}' .RE .RE .IP This performs benchmarks for 'make \-j 1', 'make \-j 2', ..., 'make \-j 8'. .IP To have the value increase following different patterns, use shell arithmetics. .IP .RS Example: .RS \fBhyperfine\fR \fB\-P\fR size 0 3 'sleep $((2**{size}))' .RE .RE .IP This performs benchmarks with power of 2 increases: 'sleep 1', 'sleep 2', 'sleep 4', ... .IP The exact syntax may vary depending on your shell and OS. .HP \fB\-D\fR, \fB\-\-parameter\-step\-size\fR \fIDELTA\fP .IP This argument requires \fB\-\-parameter\-scan\fR to be specified as well. Traverse the range \fIMIN..MAX\fP in steps of \fIDELTA\fP. .IP .RS Example: .RS \fBhyperfine\fR \fB\-P\fR delay 0.3 0.7 \fB\-D\fR 0.2 'sleep {delay}' .RE .RE .IP This performs benchmarks for 'sleep 0.3', 'sleep 0.5' and 'sleep 0.7'. .HP \fB\-L\fR, \fB\-\-parameter\-list\fR \fIVAR\fP \fIVALUES\fP .IP Perform benchmark runs for each value in the comma\-separated list of \fIVALUES\fP. Replaces the string '{\fIVAR\fP}' in each command by the current parameter value. .IP .RS Example: .RS \fBhyperfine\fR \fB\-L\fR compiler gcc,clang '{compiler} \-O2 main.cpp' .RE .RE .IP This performs benchmarks for 'gcc \-O2 main.cpp' and 'clang \-O2 main.cpp'. .IP The option can be specified multiple times to run benchmarks for all possible parameter combinations. .HP \fB\-S\fR, \fB\-\-shell\fR \fISHELL\fP .IP Set the shell to use for executing benchmarked commands. This can be the name or the path to the shell executable, or a full command line like "bash \fB\-\-norc\fR". It can also be set to "default" to explicitly select the default shell on this platform. Finally, this can also be set to "none" to disable the shell. In this case, commands will be executed directly. They can still have arguments, but more complex things like "sleep 0.1; sleep 0.2" are not possible without a shell. .HP \fB\-N\fR .IP An alias for '\-\-shell=none'. .HP \fB\-i\fR, \fB\-\-ignore\-failure\fR .IP Ignore non\-zero exit codes of the benchmarked programs. .HP \fB\-\-style\fR \fITYPE\fP .IP Set output style \fITYPE\fP (default: auto). Set this to 'basic' to disable output coloring and interactive elements. Set it to 'full' to enable all effects even if no interactive terminal was detected. Set this to 'nocolor' to keep the interactive output without any colors. Set this to 'color' to keep the colors without any interactive output. Set this to 'none' to disable all the output of the tool. .HP \fB\-\-sort\fR \fIMETHOD\fP .IP Specify the sort order of the speed comparison summary and the exported tables for markup formats (Markdown, AsciiDoc, org\-mode): .RS .IP "auto (default)" the speed comparison will be ordered by time and the markup tables will be ordered by command (input order). .IP "command" order benchmarks in the way they were specified .IP "mean\-time" order benchmarks by mean runtime .RE .HP \fB\-u\fR, \fB\-\-time\-unit\fR \fIUNIT\fP .IP Set the time unit to be used. Possible values: microsecond, millisecond, second. If the option is not given, the time unit is determined automatically. This option affects the standard output as well as all export formats except for CSV and JSON. .HP \fB\-\-export\-asciidoc\fR \fIFILE\fP .IP Export the timing summary statistics as an AsciiDoc table to the given \fIFILE\fP. The output time unit can be changed using the \fB\-\-time\-unit\fR option. .HP \fB\-\-export\-csv\fR \fIFILE\fP .IP Export the timing summary statistics as CSV to the given \fIFILE\fP. If you need the timing results for each individual run, use the JSON export format. The output time unit is always seconds. .HP \fB\-\-export\-json\fR \fIFILE\fP .IP Export the timing summary statistics and timings of individual runs as JSON to the given \fIFILE\fP. The output time unit is always seconds. .HP \fB\-\-export\-markdown\fR \fIFILE\fP .IP Export the timing summary statistics as a Markdown table to the given \fIFILE\fP. The output time unit can be changed using the \fB\-\-time\-unit\fR option. .HP \fB\-\-export\-orgmode\fR \fIFILE\fP .IP Export the timing summary statistics as an Emacs org\-mode table to the given \fIFILE\fP. The output time unit can be changed using the \fB\-\-time\-unit\fR option. .HP \fB\-\-show\-output\fR .IP Print the stdout and stderr of the benchmark instead of suppressing it. This will increase the time it takes for benchmarks to run, so it should only be used for debugging purposes or when trying to benchmark output speed. .HP \fB\-\-output\fR \fIWHERE\fP .IP Control where the output of the benchmark is redirected. Note that some programs like 'grep' detect when standard output is \fI\,/dev/null\/\fP and apply certain optimizations. To avoid that, consider using \-\-output=pipe. .IP \fIWHERE\fP can be: .RS .IP null Redirect output to \fI\,/dev/null\/\fP (the default). .IP pipe Feed the output through a pipe before discarding it. .IP inherit Don't redirect the output at all (same as \&'\-\-show\-output'). .IP "" Write the output to the given file. .RE .HP \fB\-\-input\fR \fIWHERE\fP .IP Control where the input of the benchmark comes from. .IP \fIWHERE\fP can be: .RS .IP null Read from \fI\,/dev/null\/\fP (the default). .IP "" Read the input from the given file. .RE .HP \fB\-n\fR, \fB\-\-command\-name\fR \fiNAME\fP .IP Give a meaningful \fiNAME\fP to a command. This can be specified multiple times if several commands are benchmarked. .HP \fB\-h\fR, \fB\-\-help\fR .IP Print help .HP \fB\-V\fR, \fB\-\-version\fR .IP Print version .SH EXAMPLES .LP Basic benchmark of 'find . -name todo.txt': .RS .nf \fBhyperfine\fR 'find . -name todo.txt' .fi .RE .LP Perform benchmarks for 'sleep 0.2' and 'sleep 3.2' with a minimum 5 runs each: .RS .nf \fBhyperfine\fR \fB\-\-min\-runs\fR 5 'sleep 0.2' 'sleep 3.2' .fi .RE .LP Perform a benchmark of 'grep' with a warm disk cache by executing 3 runs up front that are not part of the measurement: .RS .nf \fBhyperfine\fR \fB\-\-warmup\fR 3 'grep -R TODO *' .fi .RE .LP Export the results of a parameter scan benchmark to a markdown table: .RS .nf \fBhyperfine\fR \fB\-\-export\-markdown\fR output.md \fB\-\-parameter-scan\fR time 1 5 'sleep {time}' .fi .RE .LP Demonstrate when each of \fB\-\-setup\fR, \fB\-\-prepare\fR, \fIcmd\fP and \fB\-\-cleanup\fR will run: .RS .nf \fBhyperfine\fR \fB\-L\fR n 1,2 \fB\-r\fR 2 \fB\-\-show-output\fR \\ \fB\-\-setup\fR 'echo setup n={n}' \\ \fB\-\-prepare\fR 'echo prepare={n}' \\ \fB\-\-cleanup\fR 'echo cleanup n={n}' \\ 'echo command n={n}' .fi .RE .RE .SH AUTHOR .LP David Peter .LP Source, bug tracker, and additional information can be found on GitHub: .I https://github.com/sharkdp/hyperfine hyperfine-1.18.0/doc/whisker.png000064400000000000000000000121411046102023000146320ustar 00000000000000PNG  IHDRl|iCCPicmx}=H@_S*E;(dNDE EjVL.& IZpc⬫ ~:)HK -b=8ǻ{wP-2j46JWt}Of1'Iq_.³Zst>xMAv Tf>I4г \\74e ٔ]OSf34 tyq$ pp({Ż;{LgrSǍHPLTE888!!!nnnֽJJJ~~~ơ\\\1@DLIDATxz*F6Li@ڵf2id hhED$ި""Ϊu|6p8]`:w,"YC4"biYɦND7"-+f /Z "Dqn(KʉŸgqEܠ5J _6cD.\΂= 7: H|a w&ڋDkL@\7q$ 4ivsB\h gUBąOxSq:NIDdB;լ3BK¸qE\E\?\f@?`TU }\hY$"g Z"wC+ZJhlp b;mg!.C4".à: .j8%q%FUV{E\L>wH\h}RBS-qcpm6AM.g{qvqm&q!N/ݏ ^l.Ϝ=pLߐe!FNC_W'TJnCiѪ/k!.|p^Uj➧J-n z\hH[ .>C\@\@\@\@\@\E\@\Ľ3*:[|#.V/+ .֮ "n]jz"nJ@\%qqqv* nk :A .K@WqL@qI\E\j\ą*: q[4$[ǀ$.".5.])̚DqZq+؋o0^㒸R`SD$# 5f㒸[^tq+ի,"q]Yj\ĭ}WtX~>.V̜֡!n!O-XyڲC:]qL[LMfw}mU ߜުK٬}.G 6ujTՔT,E]h,w԰ ;k@!MfM&Ӵnފe/C4"n{=hR1=7t(x4 C٫uH]5)Z1)(;_,vZUdjUUm 7a퉛\iհ!.Q[Iq+tЈWܴNxz)68IE{yF]Q(m*q}v;\ʽο叮‰O{E&c'g8q4`㾼Ө&;uhlN/sKty\^$k5'IvJ^}u}cZ\jHFMpS3g~(-n?= { N"2ןRhrfQ{\\·:WElh2cwx/ZUHSUxxẍZVN{ÛQDHrҎRՃ\U]^䥃! !Or4=s9ZW\gfyW^}7qpы/_KwxR'JwOrM\95rJ8qɴcpID.Uwn&S:]Iϊam  w/yLu\1- 70{ .S}p>t5Z+I ՜S6v)\9;.vn䡕Ç< Q;9ImeN'yFrg|q)%Gp>~l|]4,7*ofE*Hj;.vRtXr5g\\6.B!nQ$33Rw#gpw/5^j*fýtpqpS0oP]i&űqgpȃpJ)%> 4#\*}+yP.%qqp 7#\h7uSF \pA/G zO#5]pAMp K \pA K \pA %\.@ \ %\.@ \. \.p. \.p  .pAp pht}jVD b66ֻkZIb#bVR,vJkgk$1K#b:%5N)9Aph gg{R%jh9\AEqw?/%\.p. \.p.pAp  .pAp K \pA K \pA %\.@ \ %\.@ \. \.p. \.p  .pAp K \pA ~>Z8͏O$i233b66;Im&sbVR,6Jj4yvp̱.7vd1vyyJJ.vQR;S7?.$ U$Վ}\ď5JmRNFn].@Ԡ m4ߍ"\7φw \ %\.@ \. \.p. \.p  .pAp  .pA K \pA K \ %\.@ \ %\.p. \.p.pAp GNS8IR-6ؔ[$IηyFb#bVR,6JjT;I]%\5ܳ5sKč{I}$\5\grm4I9Y# mlPZ- Rr+O|w2CLuVgg}ʗG97tXzߟ; wQq9-φ(dM[$0Cw;HW=rWyy =<mcׇF3|;ukԭ~E}}clyutk' 1Sl_+?>9wMZgnC-;T3z/[^TS?9CC?.q0T8 ݼUkf&yDxʝtV-ARZ_a м90TٗCW隩,C^Է*$klO;'XI5 z[*ݬwItˡtM5pS)d"O,ǓC/Lo77/oWn5tݚɢ~fQZ%ܠRRz"xzh/nWJ.bWa,M]UAbߵ1۽g6wKл5~eT0كg[ӧn7uŢt޹T^{TI9tO<19_|yypwC׭SZԇm>]ς\{ ,J=qɡC} .bw7o:t՚>;uEnQˆ~ПIENDB`hyperfine-1.18.0/scripts/README.md000064400000000000000000000007361046102023000146600ustar 00000000000000This folder contains scripts that can be used in combination with hyperfines `--export-json` option. ### Example: ```bash hyperfine 'sleep 0.020' 'sleep 0.021' 'sleep 0.022' --export-json sleep.json python plot_whisker.py sleep.json ``` ### Pre-requisites To make these scripts work, you will need to install `numpy`, `matplotlib` and `scipy`. Install them via your package manager or `pip`: ```bash pip install numpy matplotlib scipy # pip3, if you are using python3 ``` hyperfine-1.18.0/scripts/advanced_statistics.py000075500000000000000000000021721046102023000177710ustar 00000000000000#!/usr/bin/env python import argparse import json import numpy as np parser = argparse.ArgumentParser() parser.add_argument("file", help="JSON file with benchmark results") args = parser.parse_args() with open(args.file) as f: results = json.load(f)["results"] commands = [b["command"] for b in results] times = [b["times"] for b in results] for command, ts in zip(commands, times): p05 = np.percentile(ts, 5) p25 = np.percentile(ts, 25) p75 = np.percentile(ts, 75) p95 = np.percentile(ts, 95) iqr = p75 - p25 print("Command '{}'".format(command)) print(" runs: {:8d}".format(len(ts))) print(" mean: {:8.3f} s".format(np.mean(ts))) print(" stddev: {:8.3f} s".format(np.std(ts, ddof=1))) print(" median: {:8.3f} s".format(np.median(ts))) print(" min: {:8.3f} s".format(np.min(ts))) print(" max: {:8.3f} s".format(np.max(ts))) print() print(" percentiles:") print(" P_05 .. P_95: {:.3f} s .. {:.3f} s".format(p05, p95)) print( " P_25 .. P_75: {:.3f} s .. {:.3f} s " "(IQR = {:.3f} s)".format(p25, p75, iqr) ) print() hyperfine-1.18.0/scripts/plot_histogram.py000075500000000000000000000035541046102023000170120ustar 00000000000000#!/usr/bin/env python """This program shows `hyperfine` benchmark results as a histogram.""" import argparse import json import numpy as np import matplotlib.pyplot as plt parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with benchmark results") parser.add_argument("--title", help="Plot title") parser.add_argument( "--labels", help="Comma-separated list of entries for the plot legend" ) parser.add_argument("--bins", help="Number of bins (default: auto)") parser.add_argument( "--type", help="Type of histogram (*bar*, barstacked, step, stepfilled)" ) parser.add_argument("-o", "--output", help="Save image to the given filename.") parser.add_argument( "--t-min", metavar="T", help="Minimum time to be displayed (seconds)" ) parser.add_argument( "--t-max", metavar="T", help="Maximum time to be displayed (seconds)" ) parser.add_argument( "--log-count", help="Use a logarithmic y-axis for the event count", action="store_true", ) args = parser.parse_args() with open(args.file) as f: results = json.load(f)["results"] if args.labels: labels = args.labels.split(",") else: labels = [b["command"] for b in results] all_times = [b["times"] for b in results] t_min = float(args.t_min) if args.t_min else np.min(list(map(np.min, all_times))) t_max = float(args.t_max) if args.t_max else np.max(list(map(np.max, all_times))) bins = int(args.bins) if args.bins else "auto" histtype = args.type if args.type else "bar" plt.hist( all_times, label=labels, bins=bins, histtype=histtype, range=(t_min, t_max), ) plt.legend(prop={"family": ["Source Code Pro", "Fira Mono", "Courier New"]}) plt.xlabel("Time [s]") if args.title: plt.title(args.title) if args.log_count: plt.yscale("log") else: plt.ylim(0, None) if args.output: plt.savefig(args.output) else: plt.show() hyperfine-1.18.0/scripts/plot_parametrized.py000075500000000000000000000057251046102023000175060ustar 00000000000000#!/usr/bin/env python """This program shows parametrized `hyperfine` benchmark results as an errorbar plot.""" import argparse import json import matplotlib.pyplot as plt import sys parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with benchmark results", nargs="+") parser.add_argument( "--parameter-name", metavar="name", type=str, help="Deprecated; parameter names are now inferred from benchmark files", ) parser.add_argument( "--log-x", help="Use a logarithmic x (parameter) axis", action="store_true" ) parser.add_argument( "--log-time", help="Use a logarithmic time axis", action="store_true" ) parser.add_argument( "--titles", help="Comma-separated list of titles for the plot legend" ) parser.add_argument( "-o", "--output", help="Save image to the given filename." ) args = parser.parse_args() if args.parameter_name is not None: sys.stderr.write( "warning: --parameter-name is deprecated; names are inferred from " "benchmark results\n" ) def die(msg): sys.stderr.write("fatal: %s\n" % (msg,)) sys.exit(1) def extract_parameters(results): """Return `(parameter_name: str, parameter_values: List[float])`.""" if not results: die("no benchmark data to plot") (names, values) = zip(*(unique_parameter(b) for b in results)) names = frozenset(names) if len(names) != 1: die( "benchmarks must all have the same parameter name, but found: %s" % sorted(names) ) return (next(iter(names)), list(values)) def unique_parameter(benchmark): """Return the unique parameter `(name: str, value: float)`, or die.""" params_dict = benchmark.get("parameters", {}) if not params_dict: die("benchmarks must have exactly one parameter, but found none") if len(params_dict) > 1: die( "benchmarks must have exactly one parameter, but found multiple: %s" % sorted(params_dict) ) [(name, value)] = params_dict.items() return (name, float(value)) parameter_name = None for filename in args.file: with open(filename) as f: results = json.load(f)["results"] (this_parameter_name, parameter_values) = extract_parameters(results) if parameter_name is not None and this_parameter_name != parameter_name: die( "files must all have the same parameter name, but found %r vs. %r" % (parameter_name, this_parameter_name) ) parameter_name = this_parameter_name times_mean = [b["mean"] for b in results] times_stddev = [b["stddev"] for b in results] plt.errorbar(x=parameter_values, y=times_mean, yerr=times_stddev, capsize=2) plt.xlabel(parameter_name) plt.ylabel("Time [s]") if args.log_time: plt.yscale("log") else: plt.ylim(0, None) if args.log_x: plt.xscale("log") if args.titles: plt.legend(args.titles.split(",")) if args.output: plt.savefig(args.output) else: plt.show() hyperfine-1.18.0/scripts/plot_progression.py000075500000000000000000000031611046102023000173610ustar 00000000000000#!/usr/bin/env python """This program shows `hyperfine` benchmark results in a sequential way in order to debug possible background interference, caching effects, thermal throttling and similar effects. """ import argparse import json import matplotlib.pyplot as plt import numpy as np def moving_average(times, num_runs): times_padded = np.pad( times, (num_runs // 2, num_runs - 1 - num_runs // 2), mode="edge" ) kernel = np.ones(num_runs) / num_runs return np.convolve(times_padded, kernel, mode="valid") parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with benchmark results") parser.add_argument("--title", help="Plot Title") parser.add_argument("-o", "--output", help="Save image to the given filename.") parser.add_argument( "-w", "--moving-average-width", type=int, metavar="num_runs", help="Width of the moving-average window (default: N/5)", ) args = parser.parse_args() with open(args.file) as f: results = json.load(f)["results"] label = results[0]["command"] times = results[0]["times"] num = len(times) nums = range(num) plt.scatter(x=nums, y=times, marker=".", color="orange") plt.ylim([0, None]) plt.xlim([-1, num]) moving_average_width = ( num // 5 if args.moving_average_width is None else args.moving_average_width ) moving_average = moving_average(times, moving_average_width) plt.plot(nums, moving_average, "-", color="blue") if args.title: plt.title(args.title) plt.legend(labels=[label], loc="best", fontsize="medium") plt.ylabel("Time [s]") if args.output: plt.savefig(args.output) else: plt.show() hyperfine-1.18.0/scripts/plot_whisker.py000075500000000000000000000027331046102023000164670ustar 00000000000000#!/usr/bin/env python """This program shows `hyperfine` benchmark results as a box and whisker plot. Quoting from the matplotlib documentation: The box extends from the lower to upper quartile values of the data, with a line at the median. The whiskers extend from the box to show the range of the data. Flier points are those past the end of the whiskers. """ import argparse import json import matplotlib.pyplot as plt parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with benchmark results") parser.add_argument("--title", help="Plot Title") parser.add_argument( "--labels", help="Comma-separated list of entries for the plot legend" ) parser.add_argument( "-o", "--output", help="Save image to the given filename." ) args = parser.parse_args() with open(args.file) as f: results = json.load(f)["results"] if args.labels: labels = args.labels.split(",") else: labels = [b["command"] for b in results] times = [b["times"] for b in results] boxplot = plt.boxplot(times, vert=True, patch_artist=True) cmap = plt.get_cmap("rainbow") colors = [cmap(val / len(times)) for val in range(len(times))] for patch, color in zip(boxplot["boxes"], colors): patch.set_facecolor(color) if args.title: plt.title(args.title) plt.legend(handles=boxplot["boxes"], labels=labels, loc="best", fontsize="medium") plt.ylabel("Time [s]") plt.ylim(0, None) if args.output: plt.savefig(args.output) else: plt.show() hyperfine-1.18.0/scripts/welch_ttest.py000075500000000000000000000017721046102023000163040ustar 00000000000000#!/usr/bin/env python """This script performs Welch's t-test on a JSON export file with two benchmark results to test whether or not the two distributions are the same.""" import argparse import json import sys from scipy import stats parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("file", help="JSON file with two benchmark results") args = parser.parse_args() with open(args.file) as f: results = json.load(f)["results"] if len(results) != 2: print("The input file has to contain exactly two benchmarks") sys.exit(1) a, b = [x["command"] for x in results[:2]] X, Y = [x["times"] for x in results[:2]] print("Command 1: {}".format(a)) print("Command 2: {}\n".format(b)) t, p = stats.ttest_ind(X, Y, equal_var=False) th = 0.05 dispose = p < th print("t = {:.3}, p = {:.3}".format(t, p)) print() if dispose: print("There is a difference between the two benchmarks (p < {}).".format(th)) else: print("The two benchmarks are almost the same (p >= {}).".format(th)) hyperfine-1.18.0/src/benchmark/benchmark_result.rs000064400000000000000000000030421046102023000203220ustar 00000000000000use std::collections::BTreeMap; use serde::Serialize; use crate::util::units::Second; /// Set of values that will be exported. // NOTE: `serde` is used for JSON serialization, but not for CSV serialization due to the // `parameters` map. Update `src/hyperfine/export/csv.rs` with new fields, as appropriate. #[derive(Debug, Default, Clone, Serialize, PartialEq)] pub struct BenchmarkResult { /// The full command line of the program that is being benchmarked pub command: String, /// The full command line of the program that is being benchmarked, possibly including a list of /// parameters that were not used in the command line template. #[serde(skip_serializing)] pub command_with_unused_parameters: String, /// The average run time pub mean: Second, /// The standard deviation of all run times. Not available if only one run has been performed pub stddev: Option, /// The median run time pub median: Second, /// Time spent in user mode pub user: Second, /// Time spent in kernel mode pub system: Second, /// Minimum of all measured times pub min: Second, /// Maximum of all measured times pub max: Second, /// All run time measurements #[serde(skip_serializing_if = "Option::is_none")] pub times: Option>, /// Exit codes of all command invocations pub exit_codes: Vec>, /// Parameter values for this benchmark #[serde(skip_serializing_if = "BTreeMap::is_empty")] pub parameters: BTreeMap, } hyperfine-1.18.0/src/benchmark/executor.rs000064400000000000000000000216661046102023000166440ustar 00000000000000#[cfg(windows)] use std::os::windows::process::CommandExt; use std::process::ExitStatus; use crate::command::Command; use crate::options::{ CmdFailureAction, CommandInputPolicy, CommandOutputPolicy, Options, OutputStyleOption, Shell, }; use crate::output::progress_bar::get_progress_bar; use crate::timer::{execute_and_measure, TimerResult}; use crate::util::randomized_environment_offset; use crate::util::units::Second; use super::timing_result::TimingResult; use anyhow::{bail, Context, Result}; use statistical::mean; pub trait Executor { /// Run the given command and measure the execution time fn run_command_and_measure( &self, command: &Command<'_>, command_failure_action: Option, ) -> Result<(TimingResult, ExitStatus)>; /// Perform a calibration of this executor. For example, /// when running commands through a shell, we need to /// measure the shell spawning time separately in order /// to subtract it from the full runtime later. fn calibrate(&mut self) -> Result<()>; /// Return the time overhead for this executor when /// performing a measurement. This should return the time /// that is being used in addition to the actual runtime /// of the command. fn time_overhead(&self) -> Second; } fn run_command_and_measure_common( mut command: std::process::Command, command_failure_action: CmdFailureAction, command_input_policy: &CommandInputPolicy, command_output_policy: &CommandOutputPolicy, command_name: &str, ) -> Result { let stdin = command_input_policy.get_stdin()?; let (stdout, stderr) = command_output_policy.get_stdout_stderr()?; command.stdin(stdin).stdout(stdout).stderr(stderr); command.env( "HYPERFINE_RANDOMIZED_ENVIRONMENT_OFFSET", randomized_environment_offset::value(), ); let result = execute_and_measure(command) .with_context(|| format!("Failed to run command '{}'", command_name))?; if command_failure_action == CmdFailureAction::RaiseError && !result.status.success() { bail!( "{}. Use the '-i'/'--ignore-failure' option if you want to ignore this. \ Alternatively, use the '--show-output' option to debug what went wrong.", result.status.code().map_or( "The process has been terminated by a signal".into(), |c| format!("Command terminated with non-zero exit code: {}", c) ) ); } Ok(result) } pub struct RawExecutor<'a> { options: &'a Options, } impl<'a> RawExecutor<'a> { pub fn new(options: &'a Options) -> Self { RawExecutor { options } } } impl<'a> Executor for RawExecutor<'a> { fn run_command_and_measure( &self, command: &Command<'_>, command_failure_action: Option, ) -> Result<(TimingResult, ExitStatus)> { let result = run_command_and_measure_common( command.get_command()?, command_failure_action.unwrap_or(self.options.command_failure_action), &self.options.command_input_policy, &self.options.command_output_policy, &command.get_command_line(), )?; Ok(( TimingResult { time_real: result.time_real, time_user: result.time_user, time_system: result.time_system, }, result.status, )) } fn calibrate(&mut self) -> Result<()> { Ok(()) } fn time_overhead(&self) -> Second { 0.0 } } pub struct ShellExecutor<'a> { options: &'a Options, shell: &'a Shell, shell_spawning_time: Option, } impl<'a> ShellExecutor<'a> { pub fn new(shell: &'a Shell, options: &'a Options) -> Self { ShellExecutor { shell, options, shell_spawning_time: None, } } } impl<'a> Executor for ShellExecutor<'a> { fn run_command_and_measure( &self, command: &Command<'_>, command_failure_action: Option, ) -> Result<(TimingResult, ExitStatus)> { let on_windows_cmd = cfg!(windows) && *self.shell == Shell::Default("cmd.exe"); let mut command_builder = self.shell.command(); command_builder.arg(if on_windows_cmd { "/C" } else { "-c" }); // Windows needs special treatment for its behavior on parsing cmd arguments if on_windows_cmd { #[cfg(windows)] command_builder.raw_arg(command.get_command_line()); } else { command_builder.arg(command.get_command_line()); } let mut result = run_command_and_measure_common( command_builder, command_failure_action.unwrap_or(self.options.command_failure_action), &self.options.command_input_policy, &self.options.command_output_policy, &command.get_command_line(), )?; // Subtract shell spawning time if let Some(spawning_time) = self.shell_spawning_time { result.time_real = (result.time_real - spawning_time.time_real).max(0.0); result.time_user = (result.time_user - spawning_time.time_user).max(0.0); result.time_system = (result.time_system - spawning_time.time_system).max(0.0); } Ok(( TimingResult { time_real: result.time_real, time_user: result.time_user, time_system: result.time_system, }, result.status, )) } /// Measure the average shell spawning time fn calibrate(&mut self) -> Result<()> { const COUNT: u64 = 50; let progress_bar = if self.options.output_style != OutputStyleOption::Disabled { Some(get_progress_bar( COUNT, "Measuring shell spawning time", self.options.output_style, )) } else { None }; let mut times_real: Vec = vec![]; let mut times_user: Vec = vec![]; let mut times_system: Vec = vec![]; for _ in 0..COUNT { // Just run the shell without any command let res = self.run_command_and_measure(&Command::new(None, ""), None); match res { Err(_) => { let shell_cmd = if cfg!(windows) { format!("{} /C \"\"", self.shell) } else { format!("{} -c \"\"", self.shell) }; bail!( "Could not measure shell execution time. Make sure you can run '{}'.", shell_cmd ); } Ok((r, _)) => { times_real.push(r.time_real); times_user.push(r.time_user); times_system.push(r.time_system); } } if let Some(bar) = progress_bar.as_ref() { bar.inc(1) } } if let Some(bar) = progress_bar.as_ref() { bar.finish_and_clear() } self.shell_spawning_time = Some(TimingResult { time_real: mean(×_real), time_user: mean(×_user), time_system: mean(×_system), }); Ok(()) } fn time_overhead(&self) -> Second { self.shell_spawning_time.unwrap().time_real } } #[derive(Clone)] pub struct MockExecutor { shell: Option, } impl MockExecutor { pub fn new(shell: Option) -> Self { MockExecutor { shell } } fn extract_time>(sleep_command: S) -> Second { assert!(sleep_command.as_ref().starts_with("sleep ")); sleep_command .as_ref() .trim_start_matches("sleep ") .parse::() .unwrap() } } impl Executor for MockExecutor { fn run_command_and_measure( &self, command: &Command<'_>, _command_failure_action: Option, ) -> Result<(TimingResult, ExitStatus)> { #[cfg(unix)] let status = { use std::os::unix::process::ExitStatusExt; ExitStatus::from_raw(0) }; #[cfg(windows)] let status = { use std::os::windows::process::ExitStatusExt; ExitStatus::from_raw(0) }; Ok(( TimingResult { time_real: Self::extract_time(command.get_command_line()), time_user: 0.0, time_system: 0.0, }, status, )) } fn calibrate(&mut self) -> Result<()> { Ok(()) } fn time_overhead(&self) -> Second { match &self.shell { None => 0.0, Some(shell) => Self::extract_time(shell), } } } #[test] fn test_mock_executor_extract_time() { assert_eq!(MockExecutor::extract_time("sleep 0.1"), 0.1); } hyperfine-1.18.0/src/benchmark/mod.rs000064400000000000000000000315161046102023000155600ustar 00000000000000pub mod benchmark_result; pub mod executor; pub mod relative_speed; pub mod scheduler; pub mod timing_result; use std::cmp; use crate::command::Command; use crate::options::{CmdFailureAction, ExecutorKind, Options, OutputStyleOption}; use crate::outlier_detection::{modified_zscores, OUTLIER_THRESHOLD}; use crate::output::format::{format_duration, format_duration_unit}; use crate::output::progress_bar::get_progress_bar; use crate::output::warnings::{OutlierWarningOptions, Warnings}; use crate::parameter::ParameterNameAndValue; use crate::util::exit_code::extract_exit_code; use crate::util::min_max::{max, min}; use crate::util::units::Second; use benchmark_result::BenchmarkResult; use timing_result::TimingResult; use anyhow::{anyhow, Result}; use colored::*; use statistical::{mean, median, standard_deviation}; use self::executor::Executor; /// Threshold for warning about fast execution time pub const MIN_EXECUTION_TIME: Second = 5e-3; pub struct Benchmark<'a> { number: usize, command: &'a Command<'a>, options: &'a Options, executor: &'a dyn Executor, } impl<'a> Benchmark<'a> { pub fn new( number: usize, command: &'a Command<'a>, options: &'a Options, executor: &'a dyn Executor, ) -> Self { Benchmark { number, command, options, executor, } } /// Run setup, cleanup, or preparation commands fn run_intermediate_command( &self, command: &Command<'_>, error_output: &'static str, ) -> Result { self.executor .run_command_and_measure(command, Some(CmdFailureAction::RaiseError)) .map(|r| r.0) .map_err(|_| anyhow!(error_output)) } /// Run the command specified by `--setup`. fn run_setup_command( &self, parameters: impl IntoIterator>, ) -> Result { let command = self .options .setup_command .as_ref() .map(|setup_command| Command::new_parametrized(None, setup_command, parameters)); let error_output = "The setup command terminated with a non-zero exit code. \ Append ' || true' to the command if you are sure that this can be ignored."; Ok(command .map(|cmd| self.run_intermediate_command(&cmd, error_output)) .transpose()? .unwrap_or_default()) } /// Run the command specified by `--cleanup`. fn run_cleanup_command( &self, parameters: impl IntoIterator>, ) -> Result { let command = self .options .cleanup_command .as_ref() .map(|cleanup_command| Command::new_parametrized(None, cleanup_command, parameters)); let error_output = "The cleanup command terminated with a non-zero exit code. \ Append ' || true' to the command if you are sure that this can be ignored."; Ok(command .map(|cmd| self.run_intermediate_command(&cmd, error_output)) .transpose()? .unwrap_or_default()) } /// Run the command specified by `--prepare`. fn run_preparation_command(&self, command: &Command<'_>) -> Result { let error_output = "The preparation command terminated with a non-zero exit code. \ Append ' || true' to the command if you are sure that this can be ignored."; self.run_intermediate_command(command, error_output) } /// Run the benchmark for a single command pub fn run(&self) -> Result { if self.options.output_style != OutputStyleOption::Disabled { println!( "{}{}: {}", "Benchmark ".bold(), (self.number + 1).to_string().bold(), self.command.get_name_with_unused_parameters(), ); } let mut times_real: Vec = vec![]; let mut times_user: Vec = vec![]; let mut times_system: Vec = vec![]; let mut exit_codes: Vec> = vec![]; let mut all_succeeded = true; let preparation_command = self.options.preparation_command.as_ref().map(|values| { let preparation_command = if values.len() == 1 { &values[0] } else { &values[self.number] }; Command::new_parametrized( None, preparation_command, self.command.get_parameters().iter().cloned(), ) }); let run_preparation_command = || { preparation_command .as_ref() .map(|cmd| self.run_preparation_command(cmd)) .transpose() }; self.run_setup_command(self.command.get_parameters().iter().cloned())?; // Warmup phase if self.options.warmup_count > 0 { let progress_bar = if self.options.output_style != OutputStyleOption::Disabled { Some(get_progress_bar( self.options.warmup_count, "Performing warmup runs", self.options.output_style, )) } else { None }; for _ in 0..self.options.warmup_count { let _ = run_preparation_command()?; let _ = self.executor.run_command_and_measure(self.command, None)?; if let Some(bar) = progress_bar.as_ref() { bar.inc(1) } } if let Some(bar) = progress_bar.as_ref() { bar.finish_and_clear() } } // Set up progress bar (and spinner for initial measurement) let progress_bar = if self.options.output_style != OutputStyleOption::Disabled { Some(get_progress_bar( self.options.run_bounds.min, "Initial time measurement", self.options.output_style, )) } else { None }; let preparation_result = run_preparation_command()?; let preparation_overhead = preparation_result.map_or(0.0, |res| res.time_real + self.executor.time_overhead()); // Initial timing run let (res, status) = self.executor.run_command_and_measure(self.command, None)?; let success = status.success(); // Determine number of benchmark runs let runs_in_min_time = (self.options.min_benchmarking_time / (res.time_real + self.executor.time_overhead() + preparation_overhead)) as u64; let count = { let min = cmp::max(runs_in_min_time, self.options.run_bounds.min); self.options .run_bounds .max .as_ref() .map(|max| cmp::min(min, *max)) .unwrap_or(min) }; let count_remaining = count - 1; // Save the first result times_real.push(res.time_real); times_user.push(res.time_user); times_system.push(res.time_system); exit_codes.push(extract_exit_code(status)); all_succeeded = all_succeeded && success; // Re-configure the progress bar if let Some(bar) = progress_bar.as_ref() { bar.set_length(count) } if let Some(bar) = progress_bar.as_ref() { bar.inc(1) } // Gather statistics (perform the actual benchmark) for _ in 0..count_remaining { run_preparation_command()?; let msg = { let mean = format_duration(mean(×_real), self.options.time_unit); format!("Current estimate: {}", mean.to_string().green()) }; if let Some(bar) = progress_bar.as_ref() { bar.set_message(msg.to_owned()) } let (res, status) = self.executor.run_command_and_measure(self.command, None)?; let success = status.success(); times_real.push(res.time_real); times_user.push(res.time_user); times_system.push(res.time_system); exit_codes.push(extract_exit_code(status)); all_succeeded = all_succeeded && success; if let Some(bar) = progress_bar.as_ref() { bar.inc(1) } } if let Some(bar) = progress_bar.as_ref() { bar.finish_and_clear() } // Compute statistical quantities let t_num = times_real.len(); let t_mean = mean(×_real); let t_stddev = if times_real.len() > 1 { Some(standard_deviation(×_real, Some(t_mean))) } else { None }; let t_median = median(×_real); let t_min = min(×_real); let t_max = max(×_real); let user_mean = mean(×_user); let system_mean = mean(×_system); // Formatting and console output let (mean_str, time_unit) = format_duration_unit(t_mean, self.options.time_unit); let min_str = format_duration(t_min, Some(time_unit)); let max_str = format_duration(t_max, Some(time_unit)); let num_str = format!("{} runs", t_num); let user_str = format_duration(user_mean, Some(time_unit)); let system_str = format_duration(system_mean, Some(time_unit)); if self.options.output_style != OutputStyleOption::Disabled { if times_real.len() == 1 { println!( " Time ({} ≡): {:>8} {:>8} [User: {}, System: {}]", "abs".green().bold(), mean_str.green().bold(), " ", // alignment user_str.blue(), system_str.blue() ); } else { let stddev_str = format_duration(t_stddev.unwrap(), Some(time_unit)); println!( " Time ({} ± {}): {:>8} ± {:>8} [User: {}, System: {}]", "mean".green().bold(), "σ".green(), mean_str.green().bold(), stddev_str.green(), user_str.blue(), system_str.blue() ); println!( " Range ({} … {}): {:>8} … {:>8} {}", "min".cyan(), "max".purple(), min_str.cyan(), max_str.purple(), num_str.dimmed() ); } } // Warnings let mut warnings = vec![]; // Check execution time if matches!(self.options.executor_kind, ExecutorKind::Shell(_)) && times_real.iter().any(|&t| t < MIN_EXECUTION_TIME) { warnings.push(Warnings::FastExecutionTime); } // Check program exit codes if !all_succeeded { warnings.push(Warnings::NonZeroExitCode); } // Run outlier detection let scores = modified_zscores(×_real); let outlier_warning_options = OutlierWarningOptions { warmup_in_use: self.options.warmup_count > 0, prepare_in_use: self .options .preparation_command .as_ref() .map(|v| v.len()) .unwrap_or(0) > 0, }; if scores[0] > OUTLIER_THRESHOLD { warnings.push(Warnings::SlowInitialRun( times_real[0], outlier_warning_options, )); } else if scores.iter().any(|&s| s.abs() > OUTLIER_THRESHOLD) { warnings.push(Warnings::OutliersDetected(outlier_warning_options)); } if !warnings.is_empty() { eprintln!(" "); for warning in &warnings { eprintln!(" {}: {}", "Warning".yellow(), warning); } } if self.options.output_style != OutputStyleOption::Disabled { println!(" "); } self.run_cleanup_command(self.command.get_parameters().iter().cloned())?; Ok(BenchmarkResult { command: self.command.get_name(), command_with_unused_parameters: self.command.get_name_with_unused_parameters(), mean: t_mean, stddev: t_stddev, median: t_median, user: user_mean, system: system_mean, min: t_min, max: t_max, times: Some(times_real), exit_codes, parameters: self .command .get_parameters() .iter() .map(|(name, value)| (name.to_string(), value.to_string())) .collect(), }) } } hyperfine-1.18.0/src/benchmark/relative_speed.rs000064400000000000000000000101731046102023000177700ustar 00000000000000use std::cmp::Ordering; use super::benchmark_result::BenchmarkResult; use crate::{options::SortOrder, util::units::Scalar}; #[derive(Debug)] pub struct BenchmarkResultWithRelativeSpeed<'a> { pub result: &'a BenchmarkResult, pub relative_speed: Scalar, pub relative_speed_stddev: Option, pub is_fastest: bool, } pub fn compare_mean_time(l: &BenchmarkResult, r: &BenchmarkResult) -> Ordering { l.mean.partial_cmp(&r.mean).unwrap_or(Ordering::Equal) } fn fastest_of(results: &[BenchmarkResult]) -> &BenchmarkResult { results .iter() .min_by(|&l, &r| compare_mean_time(l, r)) .expect("at least one benchmark result") } fn compute_relative_speeds<'a>( results: &'a [BenchmarkResult], fastest: &'a BenchmarkResult, sort_order: SortOrder, ) -> Vec> { let mut results: Vec<_> = results .iter() .map(|result| { let is_fastest = result == fastest; if result.mean == 0.0 { return BenchmarkResultWithRelativeSpeed { result, relative_speed: if is_fastest { 1.0 } else { f64::INFINITY }, relative_speed_stddev: None, is_fastest, }; } let ratio = result.mean / fastest.mean; // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas // Covariance asssumed to be 0, i.e. variables are assumed to be independent let ratio_stddev = match (result.stddev, fastest.stddev) { (Some(result_stddev), Some(fastest_stddev)) => Some( ratio * ((result_stddev / result.mean).powi(2) + (fastest_stddev / fastest.mean).powi(2)) .sqrt(), ), _ => None, }; BenchmarkResultWithRelativeSpeed { result, relative_speed: ratio, relative_speed_stddev: ratio_stddev, is_fastest, } }) .collect(); match sort_order { SortOrder::Command => {} SortOrder::MeanTime => { results.sort_unstable_by(|r1, r2| compare_mean_time(r1.result, r2.result)); } } results } pub fn compute_with_check( results: &[BenchmarkResult], sort_order: SortOrder, ) -> Option> { let fastest = fastest_of(results); if fastest.mean == 0.0 { return None; } Some(compute_relative_speeds(results, fastest, sort_order)) } /// Same as compute_with_check, potentially resulting in relative speeds of infinity pub fn compute( results: &[BenchmarkResult], sort_order: SortOrder, ) -> Vec { let fastest = fastest_of(results); compute_relative_speeds(results, fastest, sort_order) } #[cfg(test)] fn create_result(name: &str, mean: Scalar) -> BenchmarkResult { use std::collections::BTreeMap; BenchmarkResult { command: name.into(), command_with_unused_parameters: name.into(), mean, stddev: Some(1.0), median: mean, user: mean, system: 0.0, min: mean, max: mean, times: None, exit_codes: Vec::new(), parameters: BTreeMap::new(), } } #[test] fn test_compute_relative_speed() { use approx::assert_relative_eq; let results = vec![ create_result("cmd1", 3.0), create_result("cmd2", 2.0), create_result("cmd3", 5.0), ]; let annotated_results = compute_with_check(&results, SortOrder::Command).unwrap(); assert_relative_eq!(1.5, annotated_results[0].relative_speed); assert_relative_eq!(1.0, annotated_results[1].relative_speed); assert_relative_eq!(2.5, annotated_results[2].relative_speed); } #[test] fn test_compute_relative_speed_for_zero_times() { let results = vec![create_result("cmd1", 1.0), create_result("cmd2", 0.0)]; let annotated_results = compute_with_check(&results, SortOrder::Command); assert!(annotated_results.is_none()); } hyperfine-1.18.0/src/benchmark/scheduler.rs000064400000000000000000000116201046102023000167510ustar 00000000000000use colored::*; use super::benchmark_result::BenchmarkResult; use super::executor::{Executor, MockExecutor, RawExecutor, ShellExecutor}; use super::{relative_speed, Benchmark}; use crate::command::Commands; use crate::export::ExportManager; use crate::options::{ExecutorKind, Options, OutputStyleOption, SortOrder}; use anyhow::Result; pub struct Scheduler<'a> { commands: &'a Commands<'a>, options: &'a Options, export_manager: &'a ExportManager, results: Vec, } impl<'a> Scheduler<'a> { pub fn new( commands: &'a Commands, options: &'a Options, export_manager: &'a ExportManager, ) -> Self { Self { commands, options, export_manager, results: vec![], } } pub fn run_benchmarks(&mut self) -> Result<()> { let mut executor: Box = match self.options.executor_kind { ExecutorKind::Raw => Box::new(RawExecutor::new(self.options)), ExecutorKind::Mock(ref shell) => Box::new(MockExecutor::new(shell.clone())), ExecutorKind::Shell(ref shell) => Box::new(ShellExecutor::new(shell, self.options)), }; executor.calibrate()?; for (number, cmd) in self.commands.iter().enumerate() { self.results .push(Benchmark::new(number, cmd, self.options, &*executor).run()?); // We export results after each individual benchmark, because // we would risk losing them if a later benchmark fails. self.export_manager.write_results( &self.results, self.options.sort_order_exports, true, )?; } Ok(()) } pub fn print_relative_speed_comparison(&self) { if self.options.output_style == OutputStyleOption::Disabled { return; } if self.results.len() < 2 { return; } if let Some(annotated_results) = relative_speed::compute_with_check( &self.results, self.options.sort_order_speed_comparison, ) { match self.options.sort_order_speed_comparison { SortOrder::MeanTime => { println!("{}", "Summary".bold()); let fastest = annotated_results.iter().find(|r| r.is_fastest).unwrap(); let others = annotated_results.iter().filter(|r| !r.is_fastest); println!( " {} ran", fastest.result.command_with_unused_parameters.cyan() ); for item in others { println!( "{}{} times faster than {}", format!("{:8.2}", item.relative_speed).bold().green(), if let Some(stddev) = item.relative_speed_stddev { format!(" ± {}", format!("{:.2}", stddev).green()) } else { "".into() }, &item.result.command_with_unused_parameters.magenta() ); } } SortOrder::Command => { println!("{}", "Relative speed comparison".bold()); for item in annotated_results { println!( " {}{} {}", format!("{:10.2}", item.relative_speed).bold().green(), if item.is_fastest { " ".into() } else if let Some(stddev) = item.relative_speed_stddev { format!(" ± {}", format!("{:5.2}", stddev).green()) } else { " ".into() }, &item.result.command_with_unused_parameters, ); } } } } else { eprintln!( "{}: The benchmark comparison could not be computed as some benchmark times are zero. \ This could be caused by background interference during the initial calibration phase \ of hyperfine, in combination with very fast commands (faster than a few milliseconds). \ Try to re-run the benchmark on a quiet system. If you did not do so already, try the \ --shell=none/-N option. If it does not help either, you command is most likely too fast \ to be accurately benchmarked by hyperfine.", "Note".bold().red() ); } } pub fn final_export(&self) -> Result<()> { self.export_manager .write_results(&self.results, self.options.sort_order_exports, false) } } hyperfine-1.18.0/src/benchmark/timing_result.rs000064400000000000000000000004741046102023000176650ustar 00000000000000use crate::util::units::Second; /// Results from timing a single command #[derive(Debug, Default, Copy, Clone)] pub struct TimingResult { /// Wall clock time pub time_real: Second, /// Time spent in user mode pub time_user: Second, /// Time spent in kernel mode pub time_system: Second, } hyperfine-1.18.0/src/cli.rs000064400000000000000000000374751046102023000136300ustar 00000000000000use std::ffi::OsString; use clap::{ builder::NonEmptyStringValueParser, crate_version, Arg, ArgAction, ArgMatches, Command, }; pub fn get_cli_arguments<'a, I, T>(args: I) -> ArgMatches where I: IntoIterator, T: Into + Clone + 'a, { let command = build_command(); command.get_matches_from(args) } /// Build the clap command for parsing command line arguments fn build_command() -> Command { Command::new("hyperfine") .version(crate_version!()) .next_line_help(true) .hide_possible_values(true) .about("A command-line benchmarking tool.") .help_expected(true) .max_term_width(80) .arg( Arg::new("command") .help("The command to benchmark. This can be the name of an executable, a command \ line like \"grep -i todo\" or a shell command like \"sleep 0.5 && echo test\". \ The latter is only available if the shell is not explicitly disabled via \ '--shell=none'. If multiple commands are given, hyperfine will show a \ comparison of the respective runtimes.") .required(true) .action(ArgAction::Append) .value_parser(NonEmptyStringValueParser::new()), ) .arg( Arg::new("warmup") .long("warmup") .short('w') .value_name("NUM") .action(ArgAction::Set) .help( "Perform NUM warmup runs before the actual benchmark. This can be used \ to fill (disk) caches for I/O-heavy programs.", ), ) .arg( Arg::new("min-runs") .long("min-runs") .short('m') .action(ArgAction::Set) .value_name("NUM") .help("Perform at least NUM runs for each command (default: 10)."), ) .arg( Arg::new("max-runs") .long("max-runs") .short('M') .action(ArgAction::Set) .value_name("NUM") .help("Perform at most NUM runs for each command. By default, there is no limit."), ) .arg( Arg::new("runs") .long("runs") .conflicts_with_all(["max-runs", "min-runs"]) .short('r') .action(ArgAction::Set) .value_name("NUM") .help("Perform exactly NUM runs for each command. If this option is not specified, \ hyperfine automatically determines the number of runs."), ) .arg( Arg::new("setup") .long("setup") .short('s') .action(ArgAction::Set) .value_name("CMD") .help( "Execute CMD before each set of timing runs. This is useful for \ compiling your software with the provided parameters, or to do any \ other work that should happen once before a series of benchmark runs, \ not every time as would happen with the --prepare option." ), ) .arg( Arg::new("prepare") .long("prepare") .short('p') .action(ArgAction::Append) .num_args(1) .value_name("CMD") .help( "Execute CMD before each timing run. This is useful for \ clearing disk caches, for example.\nThe --prepare option can \ be specified once for all commands or multiple times, once for \ each command. In the latter case, each preparation command will \ be run prior to the corresponding benchmark command.", ), ) .arg( Arg::new("cleanup") .long("cleanup") .short('c') .action(ArgAction::Set) .value_name("CMD") .help( "Execute CMD after the completion of all benchmarking \ runs for each individual command to be benchmarked. \ This is useful if the commands to be benchmarked produce \ artifacts that need to be cleaned up." ), ) .arg( Arg::new("parameter-scan") .long("parameter-scan") .short('P') .action(ArgAction::Set) .allow_hyphen_values(true) .value_names(["VAR", "MIN", "MAX"]) .help( "Perform benchmark runs for each value in the range MIN..MAX. Replaces the \ string '{VAR}' in each command by the current parameter value.\n\n \ Example: hyperfine -P threads 1 8 'make -j {threads}'\n\n\ This performs benchmarks for 'make -j 1', 'make -j 2', …, 'make -j 8'.\n\n\ To have the value increase following different patterns, use shell arithmetics.\n\n \ Example: hyperfine -P size 0 3 'sleep $((2**{size}))'\n\n\ This performs benchmarks with power of 2 increases: 'sleep 1', 'sleep 2', 'sleep 4', …\n\ The exact syntax may vary depending on your shell and OS." ), ) .arg( Arg::new("parameter-step-size") .long("parameter-step-size") .short('D') .action(ArgAction::Set) .value_names(["DELTA"]) .requires("parameter-scan") .help( "This argument requires --parameter-scan to be specified as well. \ Traverse the range MIN..MAX in steps of DELTA.\n\n \ Example: hyperfine -P delay 0.3 0.7 -D 0.2 'sleep {delay}'\n\n\ This performs benchmarks for 'sleep 0.3', 'sleep 0.5' and 'sleep 0.7'.", ), ) .arg( Arg::new("parameter-list") .long("parameter-list") .short('L') .action(ArgAction::Append) .allow_hyphen_values(true) .value_names(["VAR", "VALUES"]) .conflicts_with_all(["parameter-scan", "parameter-step-size"]) .help( "Perform benchmark runs for each value in the comma-separated list VALUES. \ Replaces the string '{VAR}' in each command by the current parameter value\ .\n\nExample: hyperfine -L compiler gcc,clang '{compiler} -O2 main.cpp'\n\n\ This performs benchmarks for 'gcc -O2 main.cpp' and 'clang -O2 main.cpp'.\n\n\ The option can be specified multiple times to run benchmarks for all \ possible parameter combinations.\n" ), ) .arg( Arg::new("shell") .long("shell") .short('S') .action(ArgAction::Set) .value_name("SHELL") .overrides_with("shell") .help("Set the shell to use for executing benchmarked commands. This can be the \ name or the path to the shell executable, or a full command line \ like \"bash --norc\". It can also be set to \"default\" to explicitly select \ the default shell on this platform. Finally, this can also be set to \ \"none\" to disable the shell. In this case, commands will be executed \ directly. They can still have arguments, but more complex things like \ \"sleep 0.1; sleep 0.2\" are not possible without a shell.") ) .arg( Arg::new("no-shell") .short('N') .action(ArgAction::SetTrue) .conflicts_with_all(["shell", "debug-mode"]) .help("An alias for '--shell=none'.") ) .arg( Arg::new("ignore-failure") .long("ignore-failure") .action(ArgAction::SetTrue) .short('i') .help("Ignore non-zero exit codes of the benchmarked programs."), ) .arg( Arg::new("style") .long("style") .action(ArgAction::Set) .value_name("TYPE") .value_parser(["auto", "basic", "full", "nocolor", "color", "none"]) .help( "Set output style type (default: auto). Set this to 'basic' to disable output \ coloring and interactive elements. Set it to 'full' to enable all effects \ even if no interactive terminal was detected. Set this to 'nocolor' to \ keep the interactive output without any colors. Set this to 'color' to keep \ the colors without any interactive output. Set this to 'none' to disable all \ the output of the tool.", ), ) .arg( Arg::new("sort") .long("sort") .action(ArgAction::Set) .value_name("METHOD") .value_parser(["auto", "command", "mean-time"]) .default_value("auto") .hide_default_value(true) .help( "Specify the sort order of the speed comparison summary and the exported tables for \ markup formats (Markdown, AsciiDoc, org-mode):\n \ * 'auto' (default): the speed comparison will be ordered by time and\n \ the markup tables will be ordered by command (input order).\n \ * 'command': order benchmarks in the way they were specified\n \ * 'mean-time': order benchmarks by mean runtime\n" ), ) .arg( Arg::new("time-unit") .long("time-unit") .short('u') .action(ArgAction::Set) .value_name("UNIT") .value_parser(["microsecond", "millisecond", "second"]) .help("Set the time unit to be used. Possible values: microsecond, millisecond, second. \ If the option is not given, the time unit is determined automatically. \ This option affects the standard output as well as all export formats except for CSV and JSON."), ) .arg( Arg::new("export-asciidoc") .long("export-asciidoc") .action(ArgAction::Set) .value_name("FILE") .help("Export the timing summary statistics as an AsciiDoc table to the given FILE. \ The output time unit can be changed using the --time-unit option."), ) .arg( Arg::new("export-csv") .long("export-csv") .action(ArgAction::Set) .value_name("FILE") .help("Export the timing summary statistics as CSV to the given FILE. If you need \ the timing results for each individual run, use the JSON export format. \ The output time unit is always seconds."), ) .arg( Arg::new("export-json") .long("export-json") .action(ArgAction::Set) .value_name("FILE") .help("Export the timing summary statistics and timings of individual runs as JSON to the given FILE. \ The output time unit is always seconds"), ) .arg( Arg::new("export-markdown") .long("export-markdown") .action(ArgAction::Set) .value_name("FILE") .help("Export the timing summary statistics as a Markdown table to the given FILE. \ The output time unit can be changed using the --time-unit option."), ) .arg( Arg::new("export-orgmode") .long("export-orgmode") .action(ArgAction::Set) .value_name("FILE") .help("Export the timing summary statistics as an Emacs org-mode table to the given FILE. \ The output time unit can be changed using the --time-unit option."), ) .arg( Arg::new("show-output") .long("show-output") .action(ArgAction::SetTrue) .conflicts_with("style") .help( "Print the stdout and stderr of the benchmark instead of suppressing it. \ This will increase the time it takes for benchmarks to run, \ so it should only be used for debugging purposes or \ when trying to benchmark output speed.", ), ) .arg( Arg::new("output") .long("output") .conflicts_with("show-output") .action(ArgAction::Set) .value_name("WHERE") .help( "Control where the output of the benchmark is redirected. Note \ that some programs like 'grep' detect when standard output is \ /dev/null and apply certain optimizations. To avoid that, consider \ using '--output=pipe'.\n\ \n\ can be:\n\ \n \ null: Redirect output to /dev/null (the default).\n\ \n \ pipe: Feed the output through a pipe before discarding it.\n\ \n \ inherit: Don't redirect the output at all (same as '--show-output').\n\ \n \ : Write the output to the given file.", ), ) .arg( Arg::new("input") .long("input") .action(ArgAction::Set) .num_args(1) .value_name("WHERE") .help("Control where the input of the benchmark comes from.\n\ \n\ can be:\n\ \n \ null: Read from /dev/null (the default).\n\ \n \ : Read the input from the given file."), ) .arg( Arg::new("command-name") .long("command-name") .short('n') .action(ArgAction::Append) .num_args(1) .value_name("NAME") .help("Give a meaningful name to a command. This can be specified multiple times \ if several commands are benchmarked."), ) // This option is hidden for now, as it is not yet clear yet if we want to 'stabilize' this, // see discussion in https://github.com/sharkdp/hyperfine/issues/527 .arg( Arg::new("min-benchmarking-time") .long("min-benchmarking-time") .action(ArgAction::Set) .hide(true) .help("Set the minimum time (in seconds) to run benchmarks. Note that the number of \ benchmark runs is additionally influenced by the `--min-runs`, `--max-runs`, and \ `--runs` option.") ) .arg( Arg::new("debug-mode") .long("debug-mode") .action(ArgAction::SetTrue) .hide(true) .help("Enable debug mode which does not actually run commands, but returns fake times when the command is 'sleep