Skip to content

Commit 35ddc1c

Browse files
committed
setup memory measurement
1 parent eb01af2 commit 35ddc1c

File tree

7 files changed

+198
-125
lines changed

7 files changed

+198
-125
lines changed

Cargo.toml

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ publish = false
88
name = "transformer"
99
harness = false
1010

11-
[profile.release]
12-
opt-level = 3
13-
lto = "fat"
14-
codegen-units = 1
15-
strip = "symbols"
16-
debug = false
17-
panic = "abort"
11+
[[bin]]
12+
name = "oxc"
13+
path = "src/oxc.rs"
14+
15+
[[bin]]
16+
name = "swc"
17+
path = "src/swc.rs"
1818

1919
[dependencies]
2020
oxc = { version = "0.13.5", features = ["transformer", "codegen"] }
@@ -34,3 +34,12 @@ mimalloc = "0.1.42"
3434

3535
[features]
3636
codspeed = ["criterion2/codspeed"]
37+
38+
[profile.release]
39+
opt-level = 3
40+
lto = "fat"
41+
codegen-units = 1
42+
strip = "symbols"
43+
debug = false
44+
panic = "abort"
45+

README.md

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ A transformer is also known as a transpiler, similar to Babel transform.
44

55
The purpose of this benchmark is for people who wants to evaluate and compare the performance characteristics of the two transformer.
66

7-
The benchmark measures the whole parse -> transform -> codegen pipeline as a realword scenario.
7+
The benchmark measures the whole parse -> transform -> codegen pipeline as a real-word scenario.
88

9-
The numbers indicate that Oxc is at least 3 times faster than Swc.
9+
The numbers indicate that Oxc is at least 2 times faster than Swc.
1010

1111
## Results
1212

@@ -37,7 +37,40 @@ Codspeed measures performance by cpu instructions.
3737
| parallel | `195.8 ms` (1.00x) | `437.2 ms` (2.23x) |
3838
| single-thread | `106.0 ms` (1.00x) | `221.7 ms` (2.09x) |
3939

40-
#### single-thread
40+
### Run benchmark locally
41+
42+
Run the following command on your machine for replication.
43+
44+
```bash
45+
cargo bench
46+
```
47+
48+
Generate the table
49+
50+
```bash
51+
pnpm i
52+
pnpm run table
53+
```
54+
55+
## Maximum Resident Set Size
56+
57+
58+
## Setup
59+
60+
* Uses `mimalloc` as the global allocator
61+
* Uses the following release profile
62+
63+
```toml
64+
[profile.release]
65+
opt-level = 3
66+
lto = "fat"
67+
codegen-units = 1
68+
strip = "symbols"
69+
debug = false
70+
panic = "abort"
71+
```
72+
73+
### single-thread
4174

4275
This is the standard benchmark run in a single thread.
4376

@@ -47,7 +80,7 @@ group.bench_with_input(id, &source, |b, source| {
4780
});
4881
```
4982

50-
#### no-drop
83+
### no-drop
5184

5285
This uses the [`iter_with_large_drop`](https://docs.rs/criterion/0.5.1/criterion/struct.Bencher.html#method.iter_with_large_drop) function, which does not take AST drop time into account.
5386

@@ -60,7 +93,7 @@ group.bench_with_input(id, &source, |b, source| {
6093
});
6194
```
6295

63-
#### parallel
96+
### parallel
6497

6598
This benchmark uses the total number of physical cores as the total number of files to parse per bench iteration. For example it parses 6 files in parallel on my Mac i7 6 cores.
6699

@@ -77,32 +110,3 @@ group.bench_with_input(id, &source, |b, source| {
77110
});
78111
```
79112

80-
## Run
81-
82-
Run the following command on your machine for replication.
83-
84-
```bash
85-
cargo bench
86-
```
87-
88-
Generate the table
89-
90-
```bash
91-
pnpm i
92-
pnpm run table
93-
```
94-
95-
## Input
96-
97-
* Uses `mimalloc` as the global allocator
98-
* Uses the following release profile
99-
100-
```toml
101-
[profile.release]
102-
opt-level = 3
103-
lto = "fat"
104-
codegen-units = 1
105-
strip = "symbols"
106-
debug = false
107-
panic = "abort"
108-
```

benches/transformer.rs

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -42,40 +42,7 @@ impl TheBencher for OxcBencher {
4242
const ID: &'static str = "oxc";
4343

4444
fn run(path: &Path, source_text: &str) -> Self::RunOutput {
45-
use oxc::{
46-
allocator::Allocator,
47-
codegen::{Codegen, CodegenOptions},
48-
parser::Parser,
49-
span::SourceType,
50-
transformer::{ReactOptions, TransformOptions, Transformer, TypeScriptOptions},
51-
};
52-
53-
let allocator = Allocator::default();
54-
let source_type = SourceType::from_path(path).unwrap();
55-
{
56-
let ret = Parser::new(&allocator, source_text, source_type).parse();
57-
let trivias = ret.trivias;
58-
let mut program = ret.program;
59-
let transform_options = TransformOptions {
60-
typescript: TypeScriptOptions::default(),
61-
react: ReactOptions::default(),
62-
..TransformOptions::default()
63-
};
64-
Transformer::new(
65-
&allocator,
66-
Path::new(""),
67-
source_type,
68-
&source_text,
69-
&trivias,
70-
transform_options,
71-
)
72-
.build(&mut program)
73-
.unwrap();
74-
let _transformed_text =
75-
Codegen::<false>::new("", source_text, CodegenOptions::default(), None)
76-
.build(&program);
77-
}
78-
allocator
45+
bench_transformer::oxc::transform(path, source_text)
7946
}
8047
}
8148

@@ -86,56 +53,8 @@ impl TheBencher for SwcBencher {
8653

8754
const ID: &'static str = "swc";
8855

89-
fn run(path: &Path, source: &str) -> Self::RunOutput {
90-
use std::sync::Arc;
91-
use swc::{Compiler, PrintArgs, SwcComments};
92-
use swc_common::{chain, source_map::SourceMap, sync::Lrc, Mark, GLOBALS};
93-
use swc_ecma_parser::{EsConfig, Parser, StringInput, Syntax, TsConfig};
94-
use swc_ecma_transforms_react::{react, Options};
95-
use swc_ecma_transforms_typescript::strip;
96-
use swc_ecma_visit::FoldWith;
97-
98-
let cm = Lrc::new(SourceMap::new(swc_common::FilePathMapping::empty()));
99-
let compiler = Compiler::new(Arc::clone(&cm));
100-
let comments = SwcComments::default();
101-
let syntax = match path.extension().unwrap().to_str().unwrap() {
102-
"js" => Syntax::Es(EsConfig::default()),
103-
"tsx" => Syntax::Typescript(TsConfig {
104-
tsx: true,
105-
..TsConfig::default()
106-
}),
107-
_ => panic!("need to define syntax for swc"),
108-
};
109-
110-
GLOBALS.set(&Default::default(), || {
111-
let program = Parser::new(
112-
syntax,
113-
StringInput::new(source, Default::default(), Default::default()),
114-
Some(&comments),
115-
)
116-
.parse_program()
117-
.unwrap();
118-
119-
let top_level_mark = Mark::new();
120-
let unresolved_mark = Mark::new();
121-
let mut ast_pass = chain!(
122-
strip(top_level_mark),
123-
react(
124-
Arc::clone(&cm),
125-
Some(comments),
126-
Options::default(),
127-
top_level_mark,
128-
unresolved_mark
129-
),
130-
);
131-
let program = program.fold_with(&mut ast_pass);
132-
133-
let _ret = compiler
134-
.print(&program, PrintArgs::default())
135-
.expect("print failed");
136-
137-
program
138-
})
56+
fn run(path: &Path, source_text: &str) -> Self::RunOutput {
57+
bench_transformer::swc::transform(path, source_text)
13958
}
14059
}
14160

memory.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cargo build --release
2+
3+
for FILE in "./files/cal.com.tsx" "./files/typescript.js"
4+
do
5+
echo $FILE
6+
7+
for APP in oxc swc
8+
do
9+
hyperfine --warmup 10 --show-output "/usr/bin/time -al ./target/release/$APP $FILE > /dev/null" 2>&1 | \
10+
grep "maximum resident set size" | \
11+
awk '{ print $1 }' \
12+
> ./target/output
13+
14+
TOTAL=$(awk '{ total += $1 } END { print total }' ./target/output)
15+
COUNT=$(wc -l ./target/output| awk '{ print $1 }')
16+
AVERAGE_MB=$(echo "$TOTAL $COUNT" | awk '{printf "%.1f", $1 / $2 / 1000 / 1000}')
17+
18+
echo $APP $AVERAGE_MB mb
19+
done
20+
done
21+

src/lib.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
pub mod oxc {
2+
use std::path::Path;
3+
4+
use oxc::{
5+
allocator::Allocator,
6+
codegen::{Codegen, CodegenOptions},
7+
parser::Parser,
8+
span::SourceType,
9+
transformer::{ReactOptions, TransformOptions, Transformer, TypeScriptOptions},
10+
};
11+
12+
pub fn transform(path: &Path, source_text: &str) -> Allocator {
13+
let allocator = Allocator::default();
14+
let source_type = SourceType::from_path(path).unwrap();
15+
{
16+
let ret = Parser::new(&allocator, source_text, source_type).parse();
17+
let trivias = ret.trivias;
18+
let mut program = ret.program;
19+
let transform_options = TransformOptions {
20+
typescript: TypeScriptOptions::default(),
21+
react: ReactOptions::default(),
22+
..TransformOptions::default()
23+
};
24+
Transformer::new(
25+
&allocator,
26+
Path::new(""),
27+
source_type,
28+
source_text,
29+
&trivias,
30+
transform_options,
31+
)
32+
.build(&mut program)
33+
.unwrap();
34+
let _transformed_text =
35+
Codegen::<false>::new("", source_text, CodegenOptions::default(), None)
36+
.build(&program);
37+
}
38+
allocator
39+
}
40+
}
41+
42+
pub mod swc {
43+
use std::path::Path;
44+
45+
use std::sync::Arc;
46+
use swc::{Compiler, PrintArgs, SwcComments};
47+
use swc_common::{chain, source_map::SourceMap, sync::Lrc, Mark, GLOBALS};
48+
use swc_ecma_ast::Program;
49+
use swc_ecma_parser::{EsConfig, Parser, StringInput, Syntax, TsConfig};
50+
use swc_ecma_transforms_react::{react, Options};
51+
use swc_ecma_transforms_typescript::strip;
52+
use swc_ecma_visit::FoldWith;
53+
54+
pub fn transform(path: &Path, source_text: &str) -> Program {
55+
let cm = Lrc::new(SourceMap::new(swc_common::FilePathMapping::empty()));
56+
let compiler = Compiler::new(Arc::clone(&cm));
57+
let comments = SwcComments::default();
58+
let syntax = match path.extension().unwrap().to_str().unwrap() {
59+
"js" => Syntax::Es(EsConfig::default()),
60+
"tsx" => Syntax::Typescript(TsConfig {
61+
tsx: true,
62+
..TsConfig::default()
63+
}),
64+
_ => panic!("need to define syntax for swc"),
65+
};
66+
67+
GLOBALS.set(&Default::default(), || {
68+
let input = StringInput::new(source_text, Default::default(), Default::default());
69+
let program = Parser::new(syntax, input, Some(&comments))
70+
.parse_program()
71+
.unwrap();
72+
73+
let top_level_mark = Mark::new();
74+
let unresolved_mark = Mark::new();
75+
let mut ast_pass = chain!(
76+
strip(top_level_mark),
77+
react(
78+
Arc::clone(&cm),
79+
Some(comments),
80+
Options::default(),
81+
top_level_mark,
82+
unresolved_mark
83+
),
84+
);
85+
let program = program.fold_with(&mut ast_pass);
86+
87+
let _ret = compiler
88+
.print(&program, PrintArgs::default())
89+
.expect("print failed");
90+
91+
program
92+
})
93+
}
94+
}

src/oxc.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#[global_allocator]
2+
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
3+
4+
use std::{env, fs, path::Path};
5+
6+
use bench_transformer::oxc;
7+
8+
pub fn main() {
9+
let path = env::args().nth(1).unwrap();
10+
let path = Path::new(&path);
11+
let source_text = fs::read_to_string(path).unwrap();
12+
let _ = oxc::transform(path, &source_text);
13+
}

src/swc.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#[global_allocator]
2+
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
3+
4+
use std::{env, fs, path::Path};
5+
6+
use bench_transformer::swc;
7+
8+
pub fn main() {
9+
let path = env::args().nth(1).unwrap();
10+
let path = Path::new(&path);
11+
let source_text = fs::read_to_string(path).unwrap();
12+
let _ = swc::transform(path, &source_text);
13+
}

0 commit comments

Comments
 (0)