Skip to content

Commit 49ebe01

Browse files
committed
add benchmark
1 parent ae42d81 commit 49ebe01

35 files changed

+4117
-73
lines changed

ToonEncoder.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<Solution>
22
<Folder Name="/sandbox/">
3+
<Project Path="sandbox/BenchmarkSuite/BenchmarkSuite.csproj" Id="a601f4ad-3dfd-43cc-b51b-845076f73eef" />
34
<Project Path="sandbox/ConsoleApp1/ConsoleApp1.csproj" />
45
</Folder>
56
<Folder Name="/src/">
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<LangVersion>14</LangVersion>
6+
<OutputType>Exe</OutputType>
7+
<Nullable>enable</Nullable>
8+
9+
</PropertyGroup>
10+
11+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
12+
<NoWarn>1701;1702;CS0436</NoWarn>
13+
</PropertyGroup>
14+
15+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
16+
<NoWarn>1701;1702;CS0436</NoWarn>
17+
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
21+
<PackageReference Include="Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers" Version="18.0.36313.1" />
22+
<PackageReference Include="Toon" Version="0.0.1-preview-01" />
23+
<PackageReference Include="ToonSharp" Version="1.0.0" />
24+
<PackageReference Include="ToonNet" Version="1.0.4" />
25+
<PackageReference Include="Toon.DotNet" Version="1.5.1" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<ProjectReference Include="..\..\src\ToonEncoder\ToonEncoder.csproj" />
30+
</ItemGroup>
31+
</Project>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Order;
3+
using System.Linq;
4+
5+
namespace BenchmarkSuite
6+
{
7+
public record Person(int Id, string Name, int Age);
8+
9+
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
10+
public class EncodeBenchmark
11+
{
12+
Person[] data;
13+
14+
public EncodeBenchmark()
15+
{
16+
data = Enumerable.Range(0, 100)
17+
.Select(x => new Person(x, $"Person {x}", x * 2))
18+
.ToArray();
19+
}
20+
21+
// mine
22+
23+
[Benchmark]
24+
public string ToonEncoder_Encode()
25+
{
26+
return global::Cysharp.AI.ToonEncoder.Encode(data);
27+
}
28+
29+
[Benchmark]
30+
public string ToonEncoder_EncodeAsTabularArray()
31+
{
32+
return global::Cysharp.AI.ToonEncoder.EncodeAsTabularArray(data);
33+
}
34+
35+
// https://github.com/StefH/Toon.NET
36+
[Benchmark]
37+
public string Toon_Encode()
38+
{
39+
return global::Toon.ToonEncoder.Encode(data);
40+
}
41+
42+
// https://github.com/0xZunia/ToonSharp
43+
[Benchmark]
44+
public string ToonSharp_Serialize()
45+
{
46+
return global::ToonSharp.ToonSerializer.Serialize(data);
47+
}
48+
49+
// https://github.com/Nicola898989/ToonNet
50+
[Benchmark]
51+
public string ToonNet_Encode()
52+
{
53+
return global::ToonNetSerializer.ToonNet.Encode(data);
54+
}
55+
56+
// https://github.com/CharlesHunt/ToonDotNet
57+
[Benchmark]
58+
public string ToonDotNet_Encode()
59+
{
60+
return global::ToonFormat.Toon.Encode(data);
61+
}
62+
63+
// Official impl: https://github.com/toon-format/toon-dotnet
64+
[Benchmark]
65+
public string ToonFormat_Encode()
66+
{
67+
return global::Toon.Format.ToonEncoder.Encode(data);
68+
}
69+
}
70+
}

sandbox/BenchmarkSuite/Program.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using BenchmarkDotNet.Configs;
2+
using BenchmarkDotNet.Diagnosers;
3+
using BenchmarkDotNet.Jobs;
4+
using BenchmarkDotNet.Reports;
5+
using BenchmarkDotNet.Running;
6+
using BenchmarkDotNet.Toolchains.CsProj;
7+
using Perfolizer.Horology;
8+
9+
10+
var config = DefaultConfig.Instance
11+
.WithSummaryStyle(SummaryStyle.Default)
12+
// .WithTimeUnit(TimeUnit.Millisecond))
13+
.HideColumns(BenchmarkDotNet.Columns.Column.Error)
14+
;
15+
16+
config.AddDiagnoser(MemoryDiagnoser.Default);
17+
18+
config.AddJob(Job.ShortRun
19+
.WithToolchain(CsProjCoreToolchain.NetCoreApp10_0) // .NET 10
20+
.DontEnforcePowerPlan());
21+
22+
var _ = BenchmarkRunner.Run(typeof(Program).Assembly, config);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
3+
namespace ToonFormat
4+
{
5+
6+
public static class Constants
7+
{
8+
public const char LIST_ITEM_MARKER = '-';
9+
10+
public const string LIST_ITEM_PREFIX = "- ";
11+
12+
// #region Structural characters
13+
public const char COMMA = ',';
14+
public const char COLON = ':';
15+
public const char SPACE = ' ';
16+
public const char PIPE = '|';
17+
public const char HASH = '#';
18+
public const char DOT = '.';
19+
// #endregion
20+
21+
// #region Brackets and braces
22+
public const char OPEN_BRACKET = '[';
23+
public const char CLOSE_BRACKET = ']';
24+
public const char OPEN_BRACE = '{';
25+
public const char CLOSE_BRACE = '}';
26+
// #endregion
27+
28+
// #region Literals
29+
public const string NULL_LITERAL = "null";
30+
public const string TRUE_LITERAL = "true";
31+
public const string FALSE_LITERAL = "false";
32+
// #endregion
33+
34+
// #region Escape/control characters
35+
public const char BACKSLASH = '\\';
36+
public const char DOUBLE_QUOTE = '"';
37+
public const char NEWLINE = '\n';
38+
public const char CARRIAGE_RETURN = '\r';
39+
public const char TAB = '\t';
40+
41+
// #region Delimiter defaults and mapping
42+
public const ToonDelimiter DEFAULT_DELIMITER_ENUM = ToonDelimiter.COMMA;
43+
44+
/// <summary>Default delimiter character (comma).</summary>
45+
public const char DEFAULT_DELIMITER_CHAR = COMMA;
46+
47+
/// <summary>Maps delimiter enum values to their specific characters.</summary>
48+
public static char ToDelimiterChar(ToonDelimiter delimiter) => delimiter switch
49+
{
50+
ToonDelimiter.COMMA => COMMA,
51+
ToonDelimiter.TAB => TAB,
52+
ToonDelimiter.PIPE => PIPE,
53+
_ => COMMA
54+
};
55+
56+
/// <summary>Maps delimiter characters to enum; unknown characters fall back to comma.</summary>
57+
public static ToonDelimiter FromDelimiterChar(char delimiter) => delimiter switch
58+
{
59+
COMMA => ToonDelimiter.COMMA,
60+
TAB => ToonDelimiter.TAB,
61+
PIPE => ToonDelimiter.PIPE,
62+
_ => ToonDelimiter.COMMA
63+
};
64+
65+
/// <summary>Returns whether the character is a supported delimiter.</summary>
66+
public static bool IsDelimiterChar(char c) => c == COMMA || c == TAB || c == PIPE;
67+
68+
/// <summary>Returns whether the character is a whitespace character (space or tab).</summary>
69+
public static bool IsWhitespace(char c) => c == SPACE || c == TAB;
70+
71+
/// <summary>Returns whether the character is a structural character.</summary>
72+
public static bool IsStructural(char c)
73+
=> c == COLON || c == OPEN_BRACKET || c == CLOSE_BRACKET || c == OPEN_BRACE || c == CLOSE_BRACE;
74+
// #endregion
75+
}
76+
77+
/// <summary>
78+
/// TOON's unified options configuration, styled to align with System.Text.Json. Used to control indentation,
79+
/// delimiters, strict mode, length markers, and underlying JSON behavior.
80+
/// </summary>
81+
public enum ToonDelimiter
82+
{
83+
/// <summary>Comma ,</summary>
84+
COMMA,
85+
86+
/// <summary>Tab \t</summary>
87+
TAB,
88+
89+
/// <summary>Pipe |</summary>
90+
PIPE
91+
}
92+
93+
/// <summary>
94+
/// Key folding options
95+
/// </summary>
96+
public enum ToonKeyFolding
97+
{
98+
/// <summary>Key folding disabled</summary>
99+
Off,
100+
101+
/// <summary>Nested objects with single keys are collapsed into dotted paths</summary>
102+
Safe
103+
}
104+
105+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace ToonFormat.Internal.Converters
6+
{
7+
/// <summary>
8+
/// Normalizes double NaN/Infinity to null when writing JSON, keeping original numeric precision otherwise.
9+
/// Reading still uses default handling, no special conversion.
10+
/// Purpose: Consistent with TS spec (NaN/±Infinity -> null), and provides stable JsonElement for subsequent TOON encoding phase.
11+
/// </summary>
12+
internal sealed class DoubleNamedFloatToNullConverter : JsonConverter<double>
13+
{
14+
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
15+
=> reader.GetDouble();
16+
17+
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
18+
{
19+
if (double.IsNaN(value) || double.IsInfinity(value))
20+
{
21+
writer.WriteNullValue();
22+
return;
23+
}
24+
writer.WriteNumberValue(value);
25+
}
26+
}
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace ToonFormat.Internal.Converters
6+
{
7+
/// <summary>
8+
/// Normalizes float NaN/Infinity to null when writing JSON; reading keeps default behavior.
9+
/// </summary>
10+
internal sealed class SingleNamedFloatToNullConverter : JsonConverter<float>
11+
{
12+
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
13+
=> reader.GetSingle();
14+
15+
public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
16+
{
17+
if (float.IsNaN(value) || float.IsInfinity(value))
18+
{
19+
writer.WriteNullValue();
20+
return;
21+
}
22+
writer.WriteNumberValue(value);
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)