Skip to content

Commit 7fafda4

Browse files
committed
Stop UTF8 decoding/reencoding in Fortunes platform
1 parent 9074110 commit 7fafda4

File tree

6 files changed

+71
-15
lines changed

6 files changed

+71
-15
lines changed

frameworks/CSharp/aspnetcore/PlatformBenchmarks/BenchmarkApplication.Fortunes.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#if DATABASE
55

66
using System.Collections.Generic;
7+
using System.Diagnostics;
78
using System.IO.Pipelines;
89
using System.Text.Encodings.Web;
910
using System.Threading.Tasks;
@@ -23,7 +24,38 @@ private async Task Fortunes(PipeWriter pipeWriter)
2324
OutputFortunes(pipeWriter, await Db.LoadFortunesRows());
2425
}
2526

26-
private void OutputFortunes(PipeWriter pipeWriter, List<Fortune> model)
27+
private void OutputFortunes(PipeWriter pipeWriter, List<FortuneUtf8> model)
28+
{
29+
var writer = GetWriter(pipeWriter, sizeHint: 1600); // in reality it's 1361
30+
31+
writer.Write(_fortunesPreamble);
32+
33+
var lengthWriter = writer;
34+
writer.Write(_contentLengthGap);
35+
36+
// Date header
37+
writer.Write(DateHeader.HeaderBytes);
38+
39+
var bodyStart = writer.Buffered;
40+
// Body
41+
writer.Write(_fortunesTableStart);
42+
foreach (var item in model)
43+
{
44+
writer.Write(_fortunesRowStart);
45+
writer.WriteNumeric((uint)item.Id);
46+
writer.Write(_fortunesColumn);
47+
HtmlEncoder.EncodeUtf8(item.Message.AsSpan(), writer.Span, out var bytesConsumed, out var bytesWritten, isFinalBlock: true);
48+
Debug.Assert(bytesConsumed == item.Message.Length, "Not enough remaining space in the buffer");
49+
writer.Advance(bytesWritten);
50+
writer.Write(_fortunesRowEnd);
51+
}
52+
writer.Write(_fortunesTableEnd);
53+
lengthWriter.WriteNumeric((uint)(writer.Buffered - bodyStart));
54+
55+
writer.Commit();
56+
}
57+
58+
private void OutputFortunes(PipeWriter pipeWriter, List<FortuneUtf16> model)
2759
{
2860
var writer = GetWriter(pipeWriter, sizeHint: 1600); // in reality it's 1361
2961

frameworks/CSharp/aspnetcore/PlatformBenchmarks/BufferWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void Advance(int count)
4444
}
4545

4646
[MethodImpl(MethodImplOptions.AggressiveInlining)]
47-
public void Write(ReadOnlySpan<byte> source)
47+
public void Write(scoped ReadOnlySpan<byte> source)
4848
{
4949
if (_span.Length >= source.Length)
5050
{
@@ -77,7 +77,7 @@ private void EnsureMore(int count = 0)
7777
_span = _output.GetSpan(count);
7878
}
7979

80-
private void WriteMultiBuffer(ReadOnlySpan<byte> source)
80+
private void WriteMultiBuffer(scoped ReadOnlySpan<byte> source)
8181
{
8282
while (source.Length > 0)
8383
{

frameworks/CSharp/aspnetcore/PlatformBenchmarks/Data/Fortune.cs renamed to frameworks/CSharp/aspnetcore/PlatformBenchmarks/Data/FortuneUtf16.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
namespace PlatformBenchmarks;
55

6-
public readonly struct Fortune : IComparable<Fortune>, IComparable
6+
public readonly struct FortuneUtf16 : IComparable<FortuneUtf16>, IComparable
77
{
8-
public Fortune(int id, string message)
8+
public FortuneUtf16(int id, string message)
99
{
1010
Id = id;
1111
Message = message;
@@ -18,5 +18,5 @@ public Fortune(int id, string message)
1818
public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
1919

2020
// Performance critical, using culture insensitive comparison
21-
public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
21+
public int CompareTo(FortuneUtf16 other) => string.CompareOrdinal(Message, other.Message);
2222
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace PlatformBenchmarks;
5+
6+
public readonly struct FortuneUtf8 : IComparable<FortuneUtf8>, IComparable
7+
{
8+
public FortuneUtf8(int id, byte[] message)
9+
{
10+
Id = id;
11+
Message = message;
12+
}
13+
14+
public int Id { get; }
15+
16+
public byte[] Message { get; }
17+
18+
public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
19+
20+
// Performance critical, using culture insensitive comparison
21+
public int CompareTo(FortuneUtf8 other) => Message.AsSpan().SequenceCompareTo(other.Message.AsSpan());
22+
}

frameworks/CSharp/aspnetcore/PlatformBenchmarks/Data/Providers/RawDbMySqlConnector.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ public async Task<World[]> LoadMultipleUpdatesRows(int count)
185185
return results;
186186
}
187187

188-
public async Task<List<Fortune>> LoadFortunesRows()
188+
public async Task<List<FortuneUtf16>> LoadFortunesRows()
189189
{
190-
var result = new List<Fortune>();
190+
var result = new List<FortuneUtf16>();
191191

192192
using (var db = new MySqlConnection(_connectionString))
193193
{
@@ -202,7 +202,7 @@ public async Task<List<Fortune>> LoadFortunesRows()
202202
while (await rdr.ReadAsync())
203203
{
204204
result.Add(
205-
new Fortune
205+
new FortuneUtf16
206206
(
207207
id: rdr.GetInt32(0),
208208
message: rdr.GetString(1)
@@ -212,7 +212,7 @@ public async Task<List<Fortune>> LoadFortunesRows()
212212
}
213213
}
214214

215-
result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
215+
result.Add(new FortuneUtf16(id: 0, message: "Additional fortune added at request time." ));
216216
result.Sort();
217217

218218
return result;

frameworks/CSharp/aspnetcore/PlatformBenchmarks/Data/Providers/RawDbNpgsql.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ public async Task<World[]> LoadMultipleUpdatesRows(int count)
194194
return results;
195195
}
196196

197-
public async Task<List<Fortune>> LoadFortunesRows()
197+
public async Task<List<FortuneUtf8>> LoadFortunesRows()
198198
{
199-
var result = new List<Fortune>();
199+
var result = new List<FortuneUtf8>();
200200

201201
using (var db = _dataSource.CreateConnection())
202202
{
@@ -206,20 +206,22 @@ public async Task<List<Fortune>> LoadFortunesRows()
206206
using var rdr = await cmd.ExecuteReaderAsync();
207207
while (await rdr.ReadAsync())
208208
{
209-
result.Add(new Fortune
209+
result.Add(new FortuneUtf8
210210
(
211211
id:rdr.GetInt32(0),
212-
message: rdr.GetString(1)
212+
message: rdr.GetFieldValue<byte[]>(1)
213213
));
214214
}
215215
}
216216

217-
result.Add(new Fortune(id: 0, message: "Additional fortune added at request time." ));
217+
result.Add(new FortuneUtf8(id: 0, AdditionalFortune));
218218
result.Sort();
219219

220220
return result;
221221
}
222222

223+
private readonly byte[] AdditionalFortune = "Additional fortune added at request time."u8.ToArray();
224+
223225
private (NpgsqlCommand readCmd, NpgsqlParameter<int> idParameter) CreateReadCommand(NpgsqlConnection connection)
224226
{
225227
var cmd = new NpgsqlCommand("SELECT id, randomnumber FROM world WHERE id = $1", connection);

0 commit comments

Comments
 (0)