Skip to content

Commit 068dc2d

Browse files
authored
Improve parsing performance (#719)
1 parent 0e8a243 commit 068dc2d

File tree

5 files changed

+68
-41
lines changed

5 files changed

+68
-41
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<PackageVersion Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
1717

1818
<!-- Common to all TFMs -->
19-
<PackageVersion Include="Parlot" Version="1.0.2" />
19+
<PackageVersion Include="Parlot" Version="1.1.0" />
2020
<PackageVersion Include="TimeZoneConverter" Version="6.1.0" />
2121

2222
<!-- Benchmarks -->

Fluid.Benchmarks/FluidBenchmarks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Fluid.Benchmarks
44
{
5-
[MemoryDiagnoser]
5+
[MemoryDiagnoser, ShortRunJob]
66
public class FluidBenchmarks : BaseBenchmarks
77
{
88
private readonly TemplateOptions _options = new TemplateOptions();

Fluid/Parser/IdentifierParser.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Parlot;
1+
using Parlot;
22
using Parlot.Fluent;
33

44
namespace Fluid.Parser
@@ -35,7 +35,7 @@ public override bool Parse(ParseContext context, ref ParseResult<TextSpan> resul
3535

3636
cursor.Advance();
3737

38-
while (!context.Scanner.Cursor.Eof)
38+
while (!cursor.Eof)
3939
{
4040
current = cursor.Current;
4141

@@ -93,4 +93,4 @@ private static bool IsNonDigitStart(char ch)
9393
(ch == '_')
9494
;
9595
}
96-
}
96+
}

Fluid/Parser/TagParsers.cs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using Fluid.Ast;
1+
using Fluid.Ast;
22
using Parlot;
33
using Parlot.Fluent;
4+
using Parlot.Rewriting;
45

56
namespace Fluid.Parser
67
{
@@ -36,7 +37,7 @@ public static class TagParsers
3637
public static Parser<TagResult> OutputTagStart(bool skipWhiteSpace = false) => new OutputTagStartParser(skipWhiteSpace);
3738
public static Parser<TagResult> OutputTagEnd(bool skipWhiteSpace = false) => new OutputTagEndParser(skipWhiteSpace);
3839

39-
private sealed class TagStartParser : Parser<TagResult>
40+
private sealed class TagStartParser : Parser<TagResult>, ISeekable
4041
{
4142
private readonly bool _skipWhiteSpace;
4243

@@ -45,6 +46,12 @@ public TagStartParser(bool skipWhiteSpace = false)
4546
_skipWhiteSpace = skipWhiteSpace;
4647
}
4748

49+
public bool CanSeek => true;
50+
51+
public char[] ExpectedChars => ['{'];
52+
53+
public bool SkipWhitespace => _skipWhiteSpace;
54+
4855
public override bool Parse(ParseContext context, ref ParseResult<TagResult> result)
4956
{
5057
if (_skipWhiteSpace)
@@ -90,7 +97,7 @@ public override bool Parse(ParseContext context, ref ParseResult<TagResult> resu
9097
}
9198
}
9299

93-
private sealed class TagEndParser : Parser<TagResult>
100+
private sealed class TagEndParser : Parser<TagResult>, ISeekable
94101
{
95102
private readonly bool _skipWhiteSpace;
96103

@@ -99,6 +106,12 @@ public TagEndParser(bool skipWhiteSpace = false)
99106
_skipWhiteSpace = skipWhiteSpace;
100107
}
101108

109+
public bool CanSeek => true;
110+
111+
public char[] ExpectedChars => ['-', '}'];
112+
113+
public bool SkipWhitespace => _skipWhiteSpace;
114+
102115
public override bool Parse(ParseContext context, ref ParseResult<TagResult> result)
103116
{
104117
var p = (FluidParseContext)context;
@@ -183,7 +196,7 @@ public override bool Parse(ParseContext context, ref ParseResult<TagResult> resu
183196
}
184197
}
185198

186-
private sealed class OutputTagStartParser : Parser<TagResult>
199+
private sealed class OutputTagStartParser : Parser<TagResult>, ISeekable
187200
{
188201
private readonly bool _skipWhiteSpace;
189202

@@ -192,6 +205,12 @@ public OutputTagStartParser(bool skipWhiteSpace = false)
192205
_skipWhiteSpace = skipWhiteSpace;
193206
}
194207

208+
public bool CanSeek => true;
209+
210+
public char[] ExpectedChars => ['{'];
211+
212+
public bool SkipWhitespace => _skipWhiteSpace;
213+
195214
public override bool Parse(ParseContext context, ref ParseResult<TagResult> result)
196215
{
197216
if (_skipWhiteSpace)
@@ -231,7 +250,7 @@ public override bool Parse(ParseContext context, ref ParseResult<TagResult> resu
231250
}
232251
}
233252

234-
private sealed class OutputTagEndParser : Parser<TagResult>
253+
private sealed class OutputTagEndParser : Parser<TagResult>, ISeekable
235254
{
236255
private readonly bool _skipWhiteSpace;
237256

@@ -240,6 +259,12 @@ public OutputTagEndParser(bool skipWhiteSpace = false)
240259
_skipWhiteSpace = skipWhiteSpace;
241260
}
242261

262+
public bool CanSeek => true;
263+
264+
public char[] ExpectedChars => ['-', '}'];
265+
266+
public bool SkipWhitespace => _skipWhiteSpace;
267+
243268
public override bool Parse(ParseContext context, ref ParseResult<TagResult> result)
244269
{
245270
if (_skipWhiteSpace)

README.md

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,40 +1077,42 @@ Run it locally to analyze the time it takes to execute specific templates.
10771077
#### Results
10781078

10791079
Fluid is faster and allocates less memory than all other well-known .NET Liquid parsers.
1080-
For parsing, Fluid is 19% faster than the second, Scriban, allocating nearly 3 times less memory.
1081-
For rendering, Fluid is 26% faster than the second, Handlebars, 5 times faster than Scriban, but allocates half the memory.
1082-
Compared to DotLiquid, Fluid renders 11 times faster, and allocates 35 times less memory.
1080+
For parsing, Fluid is 20% faster than the second, Scriban, allocating 2 times less memory.
1081+
For rendering, Fluid is 30% faster than the second, Handlebars, allocating half the memory, and 5 times faster than Scriban.
1082+
Compared to DotLiquid, Fluid renders 10 times faster, and allocates 34 times less memory.
10831083

10841084
``` text
1085-
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2033)
1085+
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.2314)
10861086
12th Gen Intel Core i7-1260P, 1 CPU, 16 logical and 12 physical cores
1087-
.NET SDK 9.0.100-rc.2.24474.11
1088-
[Host] : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2
1089-
DefaultJob : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX2
1090-
1091-
1092-
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
1093-
|------------------- |--------------:|------------:|------------:|-------:|--------:|----------:|---------:|--------:|------------:|------------:|
1094-
| Fluid_Parse | 3.393 us | 0.0628 us | 0.0524 us | 1.00 | 0.02 | 0.3052 | - | - | 2.81 KB | 1.00 |
1095-
| Scriban_Parse | 3.785 us | 0.0696 us | 0.1063 us | 1.12 | 0.04 | 0.7744 | 0.0267 | - | 7.14 KB | 2.54 |
1096-
| DotLiquid_Parse | 7.339 us | 0.1385 us | 0.1228 us | 2.16 | 0.05 | 1.7395 | - | - | 16.21 KB | 5.76 |
1097-
| LiquidNet_Parse | 28.002 us | 0.5425 us | 0.6663 us | 8.25 | 0.23 | 6.7444 | 0.6104 | - | 62.04 KB | 22.06 |
1098-
| Handlebars_Parse | 2,597.261 us | 30.8705 us | 27.3659 us | 765.59 | 13.89 | 15.6250 | 7.8125 | - | 156.37 KB | 55.60 |
1099-
| | | | | | | | | | | |
1100-
| Fluid_ParseBig | 17.882 us | 0.2029 us | 0.1584 us | 1.00 | 0.01 | 1.2512 | 0.0305 | - | 11.64 KB | 1.00 |
1101-
| Scriban_ParseBig | 19.891 us | 0.3979 us | 0.3907 us | 1.11 | 0.02 | 3.4790 | 0.4883 | - | 32.07 KB | 2.75 |
1102-
| DotLiquid_ParseBig | 30.766 us | 0.6128 us | 1.0069 us | 1.72 | 0.06 | 10.2539 | 0.4883 | - | 94.36 KB | 8.11 |
1103-
| LiquidNet_ParseBig | 14,207.006 us | 347.1824 us | 984.8987 us | 794.52 | 55.23 | 3093.7500 | 15.6250 | - | 28543.38 KB | 2,452.05 |
1104-
| | | | | | | | | | | |
1105-
| Fluid_Render | 158.640 us | 3.1074 us | 7.4451 us | 1.00 | 0.06 | 10.2539 | 0.4883 | - | 95.87 KB | 1.00 |
1106-
| Handlebars_Render | 216.572 us | 4.2552 us | 9.1598 us | 1.37 | 0.08 | 20.9961 | 3.4180 | - | 194.92 KB | 2.03 |
1107-
| Scriban_Render | 768.660 us | 14.5379 us | 29.3673 us | 4.86 | 0.28 | 68.3594 | 68.3594 | 68.3594 | 498.65 KB | 5.20 |
1108-
| LiquidNet_Render | 1,073.246 us | 20.1804 us | 21.5928 us | 6.78 | 0.33 | 339.8438 | 160.1563 | - | 3130.83 KB | 32.66 |
1109-
| DotLiquid_Render | 1,812.898 us | 52.1755 us | 148.8597 us | 11.45 | 1.07 | 351.5625 | 140.6250 | 23.4375 | 3368.09 KB | 35.13 |
1110-
```
1111-
1112-
Tested on May 28, 2024 with
1113-
- Scriban 5.11.0
1087+
.NET SDK 9.0.100
1088+
[Host] : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
1089+
ShortRun : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
1090+
1091+
Job=ShortRun IterationCount=3 LaunchCount=1
1092+
WarmupCount=3
1093+
1094+
| Method | Mean | Error | StdDev | Ratio | Allocated | Alloc Ratio |
1095+
|------------------- |--------------:|--------------:|------------:|---------:|------------:|------------:|
1096+
| Fluid_Parse | 2.622 us | 1.4586 us | 0.0800 us | 1.00 | 2.83 KB | 1.00 |
1097+
| Scriban_Parse | 3.149 us | 0.8304 us | 0.0455 us | 1.20 | 7.14 KB | 2.53 |
1098+
| DotLiquid_Parse | 6.133 us | 1.5094 us | 0.0827 us | 2.34 | 16.21 KB | 5.73 |
1099+
| LiquidNet_Parse | 23.112 us | 6.0582 us | 0.3321 us | 8.82 | 62.04 KB | 21.94 |
1100+
| Handlebars_Parse | 2,662.991 us | 4,830.0818 us | 264.7531 us | 1,016.17 | 155.42 KB | 54.95 |
1101+
| | | | | | | |
1102+
| Fluid_ParseBig | 10.642 us | 2.0982 us | 0.1150 us | 1.00 | 11.66 KB | 1.00 |
1103+
| Scriban_ParseBig | 18.546 us | 14.2197 us | 0.7794 us | 1.74 | 32.07 KB | 2.75 |
1104+
| DotLiquid_ParseBig | 25.980 us | 8.1228 us | 0.4452 us | 2.44 | 94.36 KB | 8.10 |
1105+
| LiquidNet_ParseBig | 11,175.713 us | 5,605.1094 us | 307.2350 us | 1,050.22 | 28542.56 KB | 2,448.69 |
1106+
| | | | | | | |
1107+
| Fluid_Render | 127.984 us | 46.8250 us | 2.5666 us | 1.00 | 95.87 KB | 1.00 |
1108+
| Scriban_Render | 601.083 us | 86.9414 us | 4.7656 us | 4.70 | 498.66 KB | 5.20 |
1109+
| DotLiquid_Render | 1,248.906 us | 231.9350 us | 12.7131 us | 9.76 | 3270.3 KB | 34.11 |
1110+
| LiquidNet_Render | 903.463 us | 2,324.0151 us | 127.3871 us | 7.06 | 3126.47 KB | 32.61 |
1111+
| Handlebars_Render | 170.182 us | 30.0175 us | 1.6454 us | 1.33 | 194.92 KB | 2.03 |
1112+
```
1113+
1114+
Tested on November 24, 2024 with
1115+
- Scriban 5.12.0
11141116
- DotLiquid 2.2.692
11151117
- Liquid.NET 0.10.0
11161118
- Handlebars.Net 2.1.6

0 commit comments

Comments
 (0)