pnpm / pacquet

experimental package manager for node.js
Apache License 2.0
762 stars 21 forks source link

perf(tarball): use tokio::task::spawn_blocking #203

Open KSXGitHub opened 10 months ago

KSXGitHub commented 10 months ago

Thanks @TmLev for the suggestion.

The benchmark proved that spawn_blocking does improve the performance, even if it's only slightly.

codecov[bot] commented 10 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Comparison is base (bd7002a) 87.01% compared to head (aca2c2b) 87.01%. Report is 4 commits behind head on main.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #203 +/- ## ========================================== - Coverage 87.01% 87.01% -0.01% ========================================== Files 56 56 Lines 2827 2826 -1 ========================================== - Hits 2460 2459 -1 Misses 367 367 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

github-actions[bot] commented 10 months ago

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01      6.8±0.43ms   641.9 KB/sec    1.00      6.7±0.19ms   648.4 KB/sec
github-actions[bot] commented 10 months ago

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 146.9 ± 8.6 132.7 163.9 1.10 ± 0.09
pacquet@main 133.3 ± 8.1 121.4 145.7 1.00
BENCHMARK_REPORT.json ```json { "results": [ { "command": "pacquet@HEAD", "mean": 0.1468807116646154, "stddev": 0.008624976803142425, "median": 0.14604016428000002, "user": 0.05915556615384616, "system": 0.21062344000000002, "min": 0.13269790728000003, "max": 0.16390395028000002, "times": [ 0.15807113828000002, 0.13269790728000003, 0.16390395028000002, 0.14180815828, 0.14087458528000002, 0.15693850728000003, 0.15110898528000002, 0.14158109328000001, 0.14697706728, 0.14723647928000003, 0.14124804228, 0.14096317328000002, 0.14604016428000002 ], "exit_codes": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { "command": "pacquet@main", "mean": 0.13325084153, "stddev": 0.008055379851433987, "median": 0.13200574728000003, "user": 0.06228834499999999, "system": 0.2023497525, "min": 0.12138287328, "max": 0.14567323328, "times": [ 0.13184945428000003, 0.14567323328, 0.13439720528000001, 0.13196570628, 0.14213949628000003, 0.12138287328, 0.12524184628, 0.14269580628, 0.12960056628000002, 0.12766472928000003, 0.13885216228000002, 0.12325346428, 0.12273155828, 0.14476963928000003, 0.13774993528, 0.13204578828000002 ], "exit_codes": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] } ] } ```
TmLev commented 9 months ago

Here are a few general rules of thumb I follow for I/O operations:

  1. Tokio tasks excel at Network I/O. If you need to fetch something or update a registry/index, use them.
  2. Async File I/O is another story – the issue often stems from the API provided by the OS. For extensive file operations, it may be better to use tokio::spawn_blocking with a synchronous version. However, tasks should never directly use std::fs as it blocks the worker thread. For minor file operations, tokio::fs is the preferred choice.
    image
  3. For significant CPU-bound computations, it's advisable to offload them to a specialized thread pool, like Rayon. Here's an example of mixing Tokio with Rayon: https://ryhl.io/blog/async-what-is-blocking/#the-rayon-crate.
  4. Cloning numerous Strings incurs a real performance hit. If Strings need to be shared between threads/tasks but aren't modified, consider using Arc<str>.