refactor(benchmark): use codspeed for all benchmarks (#839)

This commit is contained in:
Boshen 2023-09-02 20:35:48 +08:00 committed by GitHub
parent e4a3838ecb
commit 56aaf31fb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 228 additions and 396 deletions

View file

@ -1,104 +1,47 @@
# Note: macos-latest is too unstable to be useful for benchmark, the variance is always huge.
name: Benchmark
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths-ignore:
- '**/*.md'
- '**/*.yml'
- 'npm/**'
- 'website/**'
- 'crates/oxc/**'
- 'crates/oxc_cli/**'
- 'crates/oxc_formatter/**'
- 'crates/oxc_linter/**'
- 'crates/oxc_napi/**'
- 'crates/oxc_query/**'
- 'crates/oxc_resolver/**'
- 'crates/oxc_type_synthesis/**'
- 'crates/oxc_wasm/**'
paths:
- '**/*.rs'
- 'Cargo.lock'
push:
branches:
- main
paths:
- '**/*.rs'
- 'Cargo.lock'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
benchmark_ubuntu:
name: Benchmark Linux
uses: ./.github/workflows/reusable_benchmark.yml
with:
os: ubuntu-latest
benchmark_windows:
name: Benchmark Windows
uses: ./.github/workflows/reusable_benchmark.yml
with:
os: windows-latest
compare:
benchmark:
name: Benchmark
runs-on: ubuntu-latest
name: Compare Benchmarks
needs: [benchmark_ubuntu, benchmark_windows]
steps:
- name: Install critcmp
- name: Checkout Branch
uses: actions/checkout@v3
- name: Install codspeed
uses: taiki-e/install-action@v2
with:
tool: critcmp
tool: cargo-codspeed
- name: Linux | Download benchmark results
uses: actions/download-artifact@v3
- name: Install Rust Toolchain
uses: ./.github/actions/rustup
with:
name: benchmark-results-ubuntu-latest
path: ./target/criterion
shared-key: benchmark
save-cache: ${{ github.ref_name == 'main' }}
- name: Linux | Compare benchmark results
shell: bash
run: |
echo "## Benchmark Results" >> summary.md
echo "### Linux" >> summary.md
echo "\`\`\`" >> summary.md
critcmp main pr >> summary.md
echo "\`\`\`" >> summary.md
echo "" >> summary.md
- name: Build Benchmark
run: cargo codspeed build --features codspeed -p oxc_benchmark
- name: Linux | Cleanup benchmark results
run: rm -rf ./target/criterion
- name: Windows | Download PR benchmark results
uses: actions/download-artifact@v3
- name: Run benchmark
uses: CodSpeedHQ/action@v1
with:
name: benchmark-results-windows-latest
path: ./target/criterion
- name: Windows | Compare benchmark results
shell: bash
run: |
echo "### Windows" >> summary.md
echo "\`\`\`" >> summary.md
critcmp main pr >> summary.md
echo "\`\`\`" >> summary.md
echo "" >> summary.md
cat summary.md > $GITHUB_STEP_SUMMARY
- name: Find Comment
# Check if the event is not triggered by a fork
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/find-comment@v2
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Benchmark Results
- name: Create or update comment
# Check if the event is not triggered by a fork
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
edit-mode: replace
comment-id: ${{ steps.fc.outputs.comment-id }}
body-file: summary.md
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}

View file

@ -1,41 +0,0 @@
name: Benchmark Cache
on:
push:
branches:
- main
paths-ignore:
- '**/*.md'
- '**/*.yml'
- 'npm/**'
- 'website/**'
- 'crates/oxc/**'
- 'crates/oxc_cli/**'
- 'crates/oxc_formatter/**'
- 'crates/oxc_linter/**'
- 'crates/oxc_napi/**'
- 'crates/oxc_query/**'
- 'crates/oxc_type_synthesis/**'
- 'crates/oxc_wasm/**'
jobs:
cache: # Warm cache factory for the benchmark job
name: Benchmark Cache
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
- os: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install Rust Toolchain
uses: ./.github/actions/rustup
with:
shared-key: benchmark
save-cache: true
- name: Build Benchmark
run: cargo build --release -p oxc_benchmark

View file

@ -1,46 +0,0 @@
name: Benchmark Resolver
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- 'crates/oxc_resolver/**'
- '.github/workflows/benchmark_resolver.yml'
push:
branches:
- main
paths:
- 'crates/oxc_resolver/**'
- '.github/workflows/benchmark_resolver.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
benchmark:
name: Resolver Benchmark
runs-on: ubuntu-latest
steps:
- name: Checkout Branch
uses: actions/checkout@v3
- name: Install codspeed
uses: taiki-e/install-action@v2
with:
tool: cargo-codspeed
- name: Install Rust Toolchain
uses: ./.github/actions/rustup
with:
shared-key: benchmark
- name: Build Benchmark
run: cargo codspeed build --features codspeed -p oxc_resolver
- name: Run benchmark
uses: CodSpeedHQ/action@v1
with:
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}

View file

@ -51,11 +51,11 @@ jobs:
# https://github.com/napi-rs/napi-rs/issues/1405
- name: Build cache by Cargo Check and Cargo Test
if: runner.os == 'Windows'
run: cargo test run --no-run --workspace --all-targets --all-features
run: cargo test run --no-run --workspace --all-features
- name: Build cache by Cargo Check and Cargo Test
if: runner.os != 'Windows'
run: cargo nextest run --no-run --workspace --all-targets --all-features
run: cargo nextest run --no-run --workspace --all-features
wasm:
name: Check Wasm

9
Cargo.lock generated
View file

@ -1508,16 +1508,18 @@ dependencies = [
name = "oxc_benchmark"
version = "0.0.0"
dependencies = [
"codspeed-criterion-compat",
"criterion",
"jemallocator",
"mimalloc",
"oxc_allocator",
"oxc_minifier",
"oxc_parser",
"oxc_resolver",
"oxc_semantic",
"oxc_span",
"oxc_tasks_common",
"pico-args",
"rayon",
]
[[package]]
@ -1767,15 +1769,10 @@ dependencies = [
name = "oxc_resolver"
version = "0.0.0"
dependencies = [
"codspeed-criterion-compat",
"criterion",
"dashmap",
"dunce",
"indexmap 2.0.0",
"jemallocator",
"mimalloc",
"once_cell",
"rayon",
"rustc-hash",
"serde",
"serde_json",

View file

@ -258,7 +258,7 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "oxc_allocator"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"bumpalo",
"serde",
@ -266,7 +266,7 @@ dependencies = [
[[package]]
name = "oxc_ast"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"bitflags",
"num-bigint",
@ -279,7 +279,7 @@ dependencies = [
[[package]]
name = "oxc_diagnostics"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"is-terminal",
"miette",
@ -295,13 +295,12 @@ version = "0.0.0"
dependencies = [
"libfuzzer-sys",
"oxc_allocator",
"oxc_ast",
"oxc_parser",
]
[[package]]
name = "oxc_index"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"index_vec",
"static_assertions",
@ -309,7 +308,7 @@ dependencies = [
[[package]]
name = "oxc_parser"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"bitflags",
"num-bigint",
@ -324,7 +323,7 @@ dependencies = [
[[package]]
name = "oxc_span"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"compact_str",
"miette",
@ -332,7 +331,7 @@ dependencies = [
[[package]]
name = "oxc_syntax"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"bitflags",
"oxc_index",
@ -409,9 +408,23 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "serde"
version = "1.0.183"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
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",
]
[[package]]
name = "smawk"
@ -438,9 +451,9 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.16.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
dependencies = [
"smawk",
"unicode-linebreak",

View file

@ -29,22 +29,6 @@ dunce = "1.0.4"
once_cell = "1.18.0"
[dev-dependencies]
vfs = "0.9.0" # for testing with in memory file system
rayon = { workspace = true } # for benchmark
static_assertions = { workspace = true }
tracing-subscriber = { workspace = true }
criterion = { workspace = true }
codspeed-criterion-compat = { workspace = true }
[features]
codspeed = []
[target.'cfg(not(target_env = "msvc"))'.dev-dependencies]
jemallocator = { workspace = true }
[target.'cfg(target_os = "windows")'.dev-dependencies]
mimalloc = { workspace = true }
[[bench]]
name = "resolver"
harness = false
vfs = "0.9.0" # for testing with in memory file system
static_assertions = { workspace = true }
tracing-subscriber = { workspace = true }

View file

@ -10,19 +10,42 @@ keywords.workspace = true
license.workspace = true
repository.workspace = true
[lib]
bench = false
[[bench]]
name = "parser"
harness = false
[[bench]]
name = "semantic"
harness = false
[[bench]]
name = "minifier"
harness = false
[[bench]]
name = "resolver"
harness = false
[dependencies]
oxc_span = { workspace = true }
oxc_allocator = { workspace = true }
oxc_parser = { workspace = true }
oxc_minifier = { workspace = true }
oxc_tasks_common = { workspace = true }
oxc_semantic = { workspace = true }
oxc_resolver = { workspace = true }
rayon = { workspace = true }
criterion = { workspace = true }
codspeed-criterion-compat = { workspace = true, optional = true }
[target.'cfg(not(target_env = "msvc"))'.dependencies]
jemallocator = { workspace = true }
[target.'cfg(target_os = "windows")'.dependencies]
mimalloc = { workspace = true }
[dependencies]
oxc_span = { workspace = true }
oxc_allocator = { workspace = true }
oxc_parser = { workspace = true }
oxc_minifier = { workspace = true }
oxc_tasks_common = { workspace = true }
oxc_semantic = { workspace = true }
pico-args = { workspace = true }
criterion = { workspace = true }
[features]
codspeed = ["codspeed-criterion-compat"]

View file

@ -1,31 +1,18 @@
# Benchmark
## Single run using criterion
See https://codspeed.io/web-infra-dev/oxc
```bash
cargo benchmark
```
# Files for benchmarks
## Comparing between branches
Install critcmp `cargo install critcmp`
```bash
# on pr branch
cargo benchmark --save-baseline pr
# on main branch
cargo benchmark --save-baseline main
critcmp main pr
```
## bench file sizes
| File | Size |
| -------------- | ---- |
| pdf.js | 412K |
| lodash.js | 526K |
| d3.js | 559K |
| typescript.js | 9.6M |
| babylon.max.js | 10M |
* https://cdn.jsdelivr.net/npm/react@17.0.2/cjs/react.development.js
* https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.js
* https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.js
* https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js
* https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js
* https://cdn.jsdelivr.net/npm/d3@6.3.1/dist/d3.js
* https://cdn.jsdelivr.net/npm/terser@5.17.4/dist/bundle.min.js
* https://cdn.jsdelivr.net/npm/three@0.124.0/build/three.js
* https://cdn.jsdelivr.net/npm/victory@35.8.4/dist/victory.js
* https://cdn.jsdelivr.net/npm/echarts@5.1.1/dist/echarts.js
* https://cdn.jsdelivr.net/npm/antd@4.16.1/dist/antd.js
* https://cdn.jsdelivr.net/npm/typescript@4.8.3/lib/typescript.js

View file

@ -0,0 +1,31 @@
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(target_os = "windows")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
use oxc_minifier::{Minifier, MinifierOptions};
use oxc_span::SourceType;
use oxc_tasks_common::TestFiles;
fn bench_minifier(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("minifier");
for file in TestFiles::new().files() {
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
let source_type = SourceType::from_path(&file.file_name).unwrap();
let options = MinifierOptions::default();
b.iter_with_large_drop(|| Minifier::new(source_text, source_type, options).build());
},
);
}
group.finish();
}
criterion_group!(parser, bench_minifier);
criterion_main!(parser);

View file

@ -0,0 +1,37 @@
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(target_os = "windows")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use oxc_allocator::Allocator;
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
use oxc_parser::Parser;
use oxc_span::SourceType;
use oxc_tasks_common::TestFiles;
fn bench_parser(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("parser");
for file in TestFiles::new().files() {
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
b.iter_with_large_drop(|| {
// Include the allocator drop time to make time measurement consistent.
// Otherwise the allocator will allocate huge memory chunks (by power of two) from the
// system allocator, which makes time measurement unequal during long runs.
let allocator = Allocator::default();
_ = Parser::new(&allocator, source_text, SourceType::default()).parse();
allocator
});
},
);
}
group.finish();
}
criterion_group!(parser, bench_parser);
criterion_main!(parser);

View file

@ -1,13 +1,3 @@
//! Resolver benchmark
//!
//! ```bash
//! git switch main
//! cargo bench --bench resolver -- --save-baseline main
//! git switch -
//! cargo bench --bench resolver -- --save-baseline pr
//! critcmp
//! ```
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
@ -16,18 +6,15 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use std::{env, path::PathBuf};
use std::path::PathBuf;
#[cfg(codspeed)]
use codspeed_criterion_compat::{criterion_group, criterion_main, BenchmarkId, Criterion};
#[cfg(not(codspeed))]
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
use oxc_tasks_common::project_root;
use rayon::prelude::*;
fn data() -> Vec<(PathBuf, &'static str)> {
let cwd = env::current_dir().unwrap().join("tests/enhanced_resolve/");
let cwd = project_root().join("crates/oxc_resolver/tests/enhanced_resolve/");
vec![
(cwd.clone(), "./"),
(cwd.clone(), "./lib/index"),
@ -63,11 +50,12 @@ fn oxc_resolver() -> oxc_resolver::Resolver {
})
}
fn resolver_benchmark(c: &mut Criterion) {
fn bench_resolver(c: &mut Criterion) {
let data = data();
// Bench oxc_resolver with cache
c.bench_with_input(BenchmarkId::new("resolver", "single-thread"), &data, |b, data| {
let mut group = c.benchmark_group("resolver");
group.bench_with_input(BenchmarkId::from_parameter("single-thread"), &data, |b, data| {
let oxc_resolver = oxc_resolver();
b.iter(|| {
for (path, request) in data {
@ -76,7 +64,7 @@ fn resolver_benchmark(c: &mut Criterion) {
});
});
c.bench_with_input(BenchmarkId::new("resolver", "multi-thread"), &data, |b, data| {
group.bench_with_input(BenchmarkId::from_parameter("single-thread"), &data, |b, data| {
let oxc_resolver = oxc_resolver();
b.iter(|| {
data.par_iter().for_each(|(path, request)| {
@ -86,5 +74,5 @@ fn resolver_benchmark(c: &mut Criterion) {
});
}
criterion_group!(benches, resolver_benchmark);
criterion_group!(benches, bench_resolver);
criterion_main!(benches);

View file

@ -0,0 +1,39 @@
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(target_os = "windows")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use oxc_allocator::Allocator;
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use oxc_tasks_common::TestFiles;
fn bench_semantic(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("semantic");
for file in TestFiles::new().files() {
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
let source_type = SourceType::from_path(&file.file_name).unwrap();
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, SourceType::default()).parse();
let program = allocator.alloc(ret.program);
b.iter_with_large_drop(|| {
SemanticBuilder::new(source_text, source_type)
.with_module_record_builder(true)
.build(program)
});
},
);
}
group.finish();
}
criterion_group!(parser, bench_semantic);
criterion_main!(parser);

View file

@ -0,0 +1,5 @@
#[cfg(not(codspeed))]
pub use criterion::*;
#[cfg(codspeed)]
pub use codspeed_criterion_compat::*;

View file

@ -1,120 +0,0 @@
#![cfg(not(miri))] // Miri does not support custom allocators
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(target_os = "windows")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use criterion::{BenchmarkId, Criterion, Throughput};
use oxc_allocator::Allocator;
use oxc_minifier::{Minifier, MinifierOptions};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
use oxc_tasks_common::{TestFile, TestFiles};
use pico_args::Arguments;
/// # Errors
/// # Panics
pub fn main() -> Result<(), String> {
let files = TestFiles::new();
let files = files
.files()
.iter()
.filter(|file| {
["react", "vue", "antd", "typescript"].iter().any(|f| file.file_name.contains(f))
})
.collect::<Vec<_>>();
let mut args = Arguments::from_env();
let baseline: Option<String> = args.opt_value_from_str("--save-baseline").unwrap();
let mut criterion = Criterion::default().without_plots();
if let Some(ref baseline) = baseline {
criterion = criterion.save_baseline(baseline.to_string());
}
// Check files
for file in &files {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, &file.source_text, SourceType::default()).parse();
if !ret.errors.is_empty() {
println!("{} failed", &file.file_name);
for error in &ret.errors {
println!("{error:?}");
}
return Err("Parse Failed.".to_string());
}
}
bench_parser(&mut criterion, &files);
bench_semantic(&mut criterion, &files);
bench_minifier(&mut criterion, &files);
drop(criterion);
Ok(())
}
fn bench_parser(criterion: &mut Criterion, files: &[&TestFile]) {
let mut group = criterion.benchmark_group("parser");
for file in files {
group.throughput(Throughput::Bytes(file.source_text.len() as u64));
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
b.iter_with_large_drop(|| {
// Include the allocator drop time to make time measurement consistent.
// Otherwise the allocator will allocate huge memory chunks (by power of two) from the
// system allocator, which makes time measurement unequal during long runs.
let allocator = Allocator::default();
_ = Parser::new(&allocator, source_text, SourceType::default()).parse();
allocator
});
},
);
}
group.finish();
}
fn bench_minifier(criterion: &mut Criterion, files: &[&TestFile]) {
let mut group = criterion.benchmark_group("minifier");
for file in files {
group.throughput(Throughput::Bytes(file.source_text.len() as u64));
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
let source_type = SourceType::from_path(&file.file_name).unwrap();
let options = MinifierOptions::default();
b.iter_with_large_drop(|| Minifier::new(source_text, source_type, options).build());
},
);
}
group.finish();
}
fn bench_semantic(criterion: &mut Criterion, files: &[&TestFile]) {
let mut group = criterion.benchmark_group("semantic");
for file in files {
group.throughput(Throughput::Bytes(file.source_text.len() as u64));
group.bench_with_input(
BenchmarkId::from_parameter(&file.file_name),
&file.source_text,
|b, source_text| {
let source_type = SourceType::from_path(&file.file_name).unwrap();
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, SourceType::default()).parse();
let program = allocator.alloc(ret.program);
b.iter_with_large_drop(|| {
SemanticBuilder::new(source_text, source_type)
.with_module_record_builder(true)
.build(program)
});
},
);
}
group.finish();
}

View file

@ -1,12 +1,4 @@
https://cdn.jsdelivr.net/npm/react@17.0.2/cjs/react.development.js
https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.js
https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.js
https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js
https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js
https://cdn.jsdelivr.net/npm/d3@6.3.1/dist/d3.js
https://cdn.jsdelivr.net/npm/terser@5.17.4/dist/bundle.min.js
https://cdn.jsdelivr.net/npm/three@0.124.0/build/three.js
https://cdn.jsdelivr.net/npm/victory@35.8.4/dist/victory.js
https://cdn.jsdelivr.net/npm/echarts@5.1.1/dist/echarts.js
https://cdn.jsdelivr.net/npm/antd@4.16.1/dist/antd.js
https://cdn.jsdelivr.net/npm/typescript@4.8.3/lib/typescript.js