Repository navigation
Expand file tree
/
Copy pathMaskBenchmarks.cs
More file actions
144 lines (120 loc) · 4.86 KB
/
Copy pathMaskBenchmarks.cs
File metadata and controls
144 lines (120 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System.Buffers;
using System.Text;
using BenchmarkDotNet.Configs;
using JsonMasking;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters.Csv;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
namespace DragoAnt.System.Text.Json.Observer.Benchmarks;
public sealed class AuditConfig : ManualConfig
{
public AuditConfig()
{
var fast = Environment.GetEnvironmentVariable("AUDIT_FAST") == "1";
AddJob(Job.Default
.WithToolchain(InProcessEmitToolchain.Instance)
.WithLaunchCount(1)
.WithWarmupCount(fast ? 2 : 3)
.WithIterationCount(fast ? 5 : 8)
.WithId("Audit"));
AddDiagnoser(MemoryDiagnoser.Default);
AddExporter(CsvExporter.Default);
AddColumn(BenchmarkDotNet.Columns.StatisticColumn.Median);
WithOptions(ConfigOptions.DisableOptimizationsValidator);
HideColumns("Job", "Toolchain", "LaunchCount", "WarmupCount", "IterationCount");
}
public static T[]? FromEnv<T>(string name, Func<string, T> parse)
{
var value = Environment.GetEnvironmentVariable(name);
return string.IsNullOrWhiteSpace(value)
? null
: value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(parse).ToArray();
}
}
[Config(typeof(AuditConfig))]
public class MaskBenchmarks
{
private JsonObserver _observer = null!;
private string _json = null!;
private byte[] _utf8 = null!;
private ArrayBufferWriter<byte> _output = null!;
private Utf8JsonWriter _writer = null!;
private string[] _jsonMaskingPatterns = null!;
[ParamsSource(nameof(Shapes))]
public PayloadShape Shape { get; set; }
[ParamsSource(nameof(Sizes))]
public int Size { get; set; }
public static IEnumerable<PayloadShape> Shapes =>
AuditConfig.FromEnv("AUDIT_SHAPES", s => Enum.Parse<PayloadShape>(s, ignoreCase: true))
?? [PayloadShape.Flat, PayloadShape.Nested, PayloadShape.Array];
public static IEnumerable<int> Sizes =>
AuditConfig.FromEnv("AUDIT_SIZES", s => int.Parse(s, global::System.Globalization.CultureInfo.InvariantCulture))
?? [1024, 8 * 1024, 64 * 1024];
[GlobalSetup]
public void Setup()
{
_observer = Baselines.BuildObserver();
_json = Payloads.Build(Shape, Size);
_utf8 = Encoding.UTF8.GetBytes(_json);
_output = new ArrayBufferWriter<byte>(_utf8.Length * 2);
_writer = new Utf8JsonWriter(_output);
_jsonMaskingPatterns = Payloads.SensitiveNames.Select(n => "*" + n).ToArray();
var masked = _observer.Mask(_json)!;
if (masked.Contains(Payloads.SensitiveMarker, StringComparison.Ordinal))
{
throw new InvalidOperationException("Library output leaks a sensitive value; benchmark setup is wrong.");
}
}
[GlobalCleanup]
public void Cleanup() => _writer.Dispose();
[Benchmark(Baseline = true, Description = "LowerBound copy string->string")]
public string CopyString() => Baselines.CopyString(_json);
[Benchmark(Description = "LowerBound copy bytes->pooled IBufferWriter")]
public int CopyBytes()
{
ResetOutput();
Baselines.CopyBytes(_utf8, _output, _writer, maskNames: false);
return _output.WrittenCount;
}
[Benchmark(Description = "Lib JsonObserver.Mask string->string (built once)")]
public string? Library() => _observer.Mask(_json);
[Benchmark(Description = "Lib JsonObserver.Mask bytes->pooled IBufferWriter (built once)")]
public int LibraryBytes()
{
ResetOutput();
_observer.Mask(_utf8.AsSpan(), _output);
return _output.WrittenCount;
}
[Benchmark(Description = "Hand-rolled span masker string->string")]
public string SpanMasker() => Baselines.SpanMaskString(_json);
[Benchmark(Description = "Hand-rolled span masker bytes->pooled IBufferWriter")]
public int SpanMaskerBytes()
{
ResetOutput();
Baselines.CopyBytes(_utf8, _output, _writer, maskNames: true);
return _output.WrittenCount;
}
[Benchmark(Description = "DOM JsonNode.Parse->replace->ToJsonString")]
public string Dom() => Baselines.Dom(_json);
[Benchmark(Description = "JsonMasking 2.0.0 (DOM ThiagoBarradas)")]
public string JsonMaskingLib() => _json.MaskFields(_jsonMaskingPatterns, Baselines.Mask);
[Benchmark(Description = "Regex [GeneratedRegex] over UTF-16")]
public string Regex() => Baselines.Regex(_json);
private void ResetOutput()
{
#if NET8_0_OR_GREATER
_output.ResetWrittenCount();
#else
_output.Clear();
#endif
}
}
[Config(typeof(AuditConfig))]
public class BuildBenchmarks
{
[Params(3, 8)]
public int Rules { get; set; }
[Benchmark(Description = "Build JsonObserver (relative rules)")]
public JsonObserver Build() => Baselines.BuildObserver(Rules);
}