DEV Community

阿豪
阿豪

Posted on

Improving yt-dlp-ejs with Rust: Smaller and Faster

Motivation

In the mpv-easy project, there's a web tool for customizing mpv players and scripts, including yt-dlp. To leverage GitHub CDN (which requires files under 25MB), file size is a constraint. Node/Bun/Deno runtimes are nearly impossible to compress below 25MB, and QuickJS performance in yt-dlp-ejs is suboptimal.

The Problem

The yt-dlp/ejs implementation uses meriyah to parse JavaScript, modifies the AST, regenerates code, and executes it via a JS engine.

Profiling reveals that for lightweight engines like QuickJS and Boa, the bottleneck is code generation. When generating code from a large AST, extensive string concatenation occurs—something these engines don't optimize well. (Note: QuickJS recently added optimizations for this scenario.)

In contrast, SWC's code generation is highly efficient. This explains why SWC + QuickJS outperforms Deno/Node/Bun—their CLI startup time is comparable to JS execution time.

[TRACE] → readFile
[TRACE] ← readFile (2.14ms)
[TRACE] → main
  [TRACE] → preprocessPlayer
    [TRACE] → parse
    [TRACE] ← parse (3187.57ms)
    [TRACE] → filterExpressions
    [TRACE] ← filterExpressions (251.01ms)
    [TRACE] → generate
Enter fullscreen mode Exit fullscreen mode

Solution

Using Rust, SWC replaces meriyah for AST manipulation. For Deno/Node/Bun, code is saved to a temp file and executed via CLI. For QuickJS/Boa, code is executed directly, eliminating CLI startup overhead.

Benchmark Results (Ubuntu)

JavaScript Implementation

Latest results: bench.yml

Runtime Pass Fail Total Time
bun 316 0 316 93.207s
node 316 0 316 90.037s
deno 316 0 316 100.980s

Rust Implementation

Latest results: bench.yml

Runtime Pass Fail Total Time
qjs 316 0 316 80.101s
node 316 0 316 87.947s
bun 316 0 316 147.197s
boa 316 0 316 178.557s
deno 316 0 316 238.250s

Binary Size Analysis

Using bloaty-metafile for analysis:

  • Boa: ~6MB
  • SWC: ~2MB
  • QuickJS + std::core: ~1MB

Size breakdown

With Boa's intl_bundled feature, an additional ~10MB is added. Using SWC + QuickJS keeps the binary under 5MB.

Size with intl

Conclusion

Based on benchmarks, SWC + QuickJS appears to be a compelling alternative—faster execution with a smaller footprint.

Top comments (0)