Skip to content

Commit efa0c9e

Browse files
authored
Optimize parser a little (#18)
This PR adds the following: * Optimizes the parser by caching the next lexed token * Adds support for [tracy](https://github.com/wolfpld/tracy) (sampling profiling doesn't work for some reason, only instrumentation) * Adds a new more complicated benchmark in three sized: 1x, 10x, 100x, 1000x. (Typechecking doesn't work 100% in that benchmark yet so that timing doesn't tell anything.) Note that while the speedup is significant, the parser is still too slow. This is likely due to the lexer being too intertwined with the parser and too much control flow / indirection in the hot loop. The next PR will try to extract and optimize the lexer, also hopefully simplifying the parser logic. Benchmarks: ``` main this PR def_parse [ 3.8 us ... 1.4 us ] -63.30%* def_resolve [ 925.3 ns ... 919.2 ns ] -0.66%* def_typecheck [ 780.7 ns ... 791.9 ns ] +1.44% def_resolve_merge [ 1.1 us ... 1.1 us ] +1.18%* def_typecheck_merge [ 831.7 ns ... 808.0 ns ] -2.85%* def_compile [ 5.2 us ... 2.8 us ] -46.44%* def_nu_old [ 3.4 us ... 3.4 us ] +2.43%* if_parse [ 5.0 us ... 1.8 us ] -63.52%* if_resolve [ 469.7 ns ... 457.5 ns ] -2.58%* if_typecheck [ 404.4 ns ... 414.4 ns ] +2.47%* if_resolve_merge [ 580.7 ns ... 594.2 ns ] +2.31%* if_typecheck_merge [ 429.4 ns ... 432.4 ns ] +0.69%* if_compile [ 5.7 us ... 2.4 us ] -57.44%* if_nu_old [ 5.2 us ... 5.3 us ] +1.48%* combined_parse [ 25.2 us ... 8.3 us ] -66.96%* combined_resolve [ 4.4 us ... 4.3 us ] -2.46%* combined_typecheck [ 3.9 us ... 3.9 us ] +0.22% combined_resolve_merge [ 5.0 us ... 4.8 us ] -4.29%* combined_typecheck_merge [ 4.0 us ... 4.0 us ] +0.44% combined_compile [ 32.3 us ... 14.8 us ] -54.11%* combined_nu_old [ 4.2 us ... 4.3 us ] +1.93%* combined10_parse [ 250.8 us ... 82.4 us ] -67.16%* combined10_resolve [ 51.5 us ... 48.6 us ] -5.68%* combined10_typecheck [ 43.2 us ... 43.8 us ] +1.41%* combined10_resolve_merge [ 51.0 us ... 49.9 us ] -2.00%* combined10_typecheck_merge [ 44.4 us ... 43.2 us ] -2.50% combined10_compile [ 330.1 us ... 151.7 us ] -54.04%* combined10_nu_old [ 39.0 us ... 39.8 us ] +1.96%* combined100_parse [ 2.5 ms ... 748.1 us ] -69.91%* combined100_resolve [ 480.6 us ... 476.4 us ] -0.88% combined100_typecheck [ 435.4 us ... 451.7 us ] +3.73% combined100_resolve_merge [ 506.0 us ... 520.9 us ] +2.93% combined100_typecheck_merge [ 456.8 us ... 451.6 us ] -1.15% combined100_compile [ 3.2 ms ... 1.4 ms ] -54.94%* combined100_nu_old [ 383.4 us ... 392.0 us ] +2.24%* combined1000_parse [ 23.9 ms ... 6.5 ms ] -72.61%* combined1000_resolve [ 4.9 ms ... 4.7 ms ] -3.39% combined1000_typecheck [ 4.6 ms ... 4.6 ms ] -0.20% combined1000_resolve_merge [ 5.6 ms ... 5.4 ms ] -3.75% combined1000_typecheck_merge [ 5.6 ms ... 5.8 ms ] +4.97% combined1000_compile [ 31.6 ms ... 13.7 ms ] -56.69%* combined1000_nu_old [ 3.9 ms ... 3.9 ms ] +0.56% nu_old_empty [ 332.2 ns ... 330.2 ns ] -0.60% ```
1 parent 8a6aa37 commit efa0c9e

File tree

10 files changed

+30411
-78
lines changed

10 files changed

+30411
-78
lines changed

Cargo.lock

Lines changed: 267 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
tracy-client = { version = "0.17.3", default-features = false } # for tracy v0.11.1
10+
11+
[profile.profiling]
12+
inherits = "release"
13+
debug = true
14+
15+
[features]
16+
# By default, profiling is disabled. Enable it by the "profile" feature
17+
tracy = [
18+
"tracy-client/enable",
19+
"tracy-client/flush-on-exit",
20+
"tracy-client/sampling",
21+
"tracy-client/code-transfer",
22+
]
923

1024
[lib]
1125
name = "new_nu_parser"
@@ -14,8 +28,8 @@ path = "src/lib.rs"
1428
[dev-dependencies]
1529
insta = { version = "1.33.0", features = ["glob"] }
1630
tango-bench = "0.6"
17-
nu-parser = "0.98"
18-
nu-protocol = "0.98"
31+
nu-parser = "0.99"
32+
nu-protocol = "0.99"
1933

2034
[[bench]]
2135
name = "benchmarks"

benches/benchmarks.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ use new_nu_parser::resolver::Resolver;
99
use new_nu_parser::typechecker::Typechecker;
1010

1111
/// Files in benches/nu/ we want to benchmark (without .nu suffix)
12-
const BENCHMARKS: &[&str] = &["def", "if"];
12+
const BENCHMARKS: &[&str] = &[
13+
"def",
14+
"if",
15+
"combined",
16+
"combined10",
17+
"combined100",
18+
"combined1000",
19+
];
1320

1421
enum Stage {
1522
Parse,

benches/nu/combined.nu

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
def foo [ x: bool, y: int, z: list<list<int>> ] {
2+
def bar [ y: int ] {
3+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
4+
}
5+
6+
def baz [ y: int ] {
7+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
8+
}
9+
10+
let res = if $x {
11+
bar $y
12+
} else {
13+
baz $y
14+
}
15+
16+
mut out = [ $y ]
17+
18+
for a in $z {
19+
for b in $a {
20+
$out = $out ++ $b
21+
}
22+
}
23+
24+
$out = $out ++ $res
25+
$out
26+
}

benches/nu/combined10.nu

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
def foo1 [ x: bool, y: int, z: list<list<int>> ] {
2+
def bar [ y: int ] {
3+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
4+
}
5+
6+
def baz [ y: int ] {
7+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
8+
}
9+
10+
let res = if $x {
11+
bar $y
12+
} else {
13+
baz $y
14+
}
15+
16+
mut out = [ $y ]
17+
18+
for a in $z {
19+
for b in $a {
20+
$out = $out ++ $b
21+
}
22+
}
23+
24+
$out = $out ++ $res
25+
$out
26+
}
27+
28+
def foo2 [ x: bool, y: int, z: list<list<int>> ] {
29+
def bar [ y: int ] {
30+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
31+
}
32+
33+
def baz [ y: int ] {
34+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
35+
}
36+
37+
let res = if $x {
38+
bar $y
39+
} else {
40+
baz $y
41+
}
42+
43+
mut out = [ $y ]
44+
45+
for a in $z {
46+
for b in $a {
47+
$out = $out ++ $b
48+
}
49+
}
50+
51+
$out = $out ++ $res
52+
$out
53+
}
54+
55+
def foo3 [ x: bool, y: int, z: list<list<int>> ] {
56+
def bar [ y: int ] {
57+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
58+
}
59+
60+
def baz [ y: int ] {
61+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
62+
}
63+
64+
let res = if $x {
65+
bar $y
66+
} else {
67+
baz $y
68+
}
69+
70+
mut out = [ $y ]
71+
72+
for a in $z {
73+
for b in $a {
74+
$out = $out ++ $b
75+
}
76+
}
77+
78+
$out = $out ++ $res
79+
$out
80+
}
81+
82+
def foo4 [ x: bool, y: int, z: list<list<int>> ] {
83+
def bar [ y: int ] {
84+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
85+
}
86+
87+
def baz [ y: int ] {
88+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
89+
}
90+
91+
let res = if $x {
92+
bar $y
93+
} else {
94+
baz $y
95+
}
96+
97+
mut out = [ $y ]
98+
99+
for a in $z {
100+
for b in $a {
101+
$out = $out ++ $b
102+
}
103+
}
104+
105+
$out = $out ++ $res
106+
$out
107+
}
108+
109+
def foo5 [ x: bool, y: int, z: list<list<int>> ] {
110+
def bar [ y: int ] {
111+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
112+
}
113+
114+
def baz [ y: int ] {
115+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
116+
}
117+
118+
let res = if $x {
119+
bar $y
120+
} else {
121+
baz $y
122+
}
123+
124+
mut out = [ $y ]
125+
126+
for a in $z {
127+
for b in $a {
128+
$out = $out ++ $b
129+
}
130+
}
131+
132+
$out = $out ++ $res
133+
$out
134+
}
135+
136+
def foo6 [ x: bool, y: int, z: list<list<int>> ] {
137+
def bar [ y: int ] {
138+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
139+
}
140+
141+
def baz [ y: int ] {
142+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
143+
}
144+
145+
let res = if $x {
146+
bar $y
147+
} else {
148+
baz $y
149+
}
150+
151+
mut out = [ $y ]
152+
153+
for a in $z {
154+
for b in $a {
155+
$out = $out ++ $b
156+
}
157+
}
158+
159+
$out = $out ++ $res
160+
$out
161+
}
162+
163+
def foo7 [ x: bool, y: int, z: list<list<int>> ] {
164+
def bar [ y: int ] {
165+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
166+
}
167+
168+
def baz [ y: int ] {
169+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
170+
}
171+
172+
let res = if $x {
173+
bar $y
174+
} else {
175+
baz $y
176+
}
177+
178+
mut out = [ $y ]
179+
180+
for a in $z {
181+
for b in $a {
182+
$out = $out ++ $b
183+
}
184+
}
185+
186+
$out = $out ++ $res
187+
$out
188+
}
189+
190+
def foo8 [ x: bool, y: int, z: list<list<int>> ] {
191+
def bar [ y: int ] {
192+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
193+
}
194+
195+
def baz [ y: int ] {
196+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
197+
}
198+
199+
let res = if $x {
200+
bar $y
201+
} else {
202+
baz $y
203+
}
204+
205+
mut out = [ $y ]
206+
207+
for a in $z {
208+
for b in $a {
209+
$out = $out ++ $b
210+
}
211+
}
212+
213+
$out = $out ++ $res
214+
$out
215+
}
216+
217+
def foo9 [ x: bool, y: int, z: list<list<int>> ] {
218+
def bar [ y: int ] {
219+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
220+
}
221+
222+
def baz [ y: int ] {
223+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
224+
}
225+
226+
let res = if $x {
227+
bar $y
228+
} else {
229+
baz $y
230+
}
231+
232+
mut out = [ $y ]
233+
234+
for a in $z {
235+
for b in $a {
236+
$out = $out ++ $b
237+
}
238+
}
239+
240+
$out = $out ++ $res
241+
$out
242+
}
243+
244+
def foo10 [ x: bool, y: int, z: list<list<int>> ] {
245+
def bar [ y: int ] {
246+
$y * 10 * (($y * 10 + $y * 10) - ($y * 10 * 10))
247+
}
248+
249+
def baz [ y: int ] {
250+
$y * 20 * (($y * 20 + $y * 20) - ($y * 20 * 20))
251+
}
252+
253+
let res = if $x {
254+
bar $y
255+
} else {
256+
baz $y
257+
}
258+
259+
mut out = [ $y ]
260+
261+
for a in $z {
262+
for b in $a {
263+
$out = $out ++ $b
264+
}
265+
}
266+
267+
$out = $out ++ $res
268+
$out
269+
}

0 commit comments

Comments
 (0)