Repository navigation
Expand file tree
/
Copy pathStringAppenderBenchmark.cs
More file actions
89 lines (71 loc) · 3.1 KB
/
Copy pathStringAppenderBenchmark.cs
File metadata and controls
89 lines (71 loc) · 3.1 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
using BenchmarkDotNet.Attributes;
using DuckDB.NET.Data;
namespace DuckDB.NET.Benchmarks;
// Four VARCHAR columns per benchmark, written from string. What a write costs depends on the length in UTF-8:
// DuckDB keeps a string of up to 12 bytes inside its 16-byte value, and a longer one in a heap of the vector.
// 500,000 rows, not a million: four columns of 300 characters would make a table of more than a gigabyte.
[MemoryDiagnoser]
public class StringAppenderBenchmark
{
private const int ValueCount = 1024;
// Created once, so that the benchmarks do not measure the building of strings.
private readonly string[] shortAscii = new string[ValueCount]; // 5 to 8 characters
private readonly string[] mediumAscii = new string[ValueCount]; // 40 characters
private readonly string[] longAscii = new string[ValueCount]; // 300 characters
private readonly string[] shortGeorgian = new string[ValueCount]; // 4 characters, 12 bytes: inline
private readonly string[] mediumGeorgian = new string[ValueCount]; // 30 characters, 90 bytes
private DuckDBConnection connection = null!;
[Params(500_000)]
public int RowCount { get; set; }
[GlobalSetup]
public void Setup()
{
connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();
LoadedLibrary.Print(connection);
for (var i = 0; i < ValueCount; i++)
{
// The Georgian letters start at U+10D0 and take three bytes each.
var letter = (char)('ა' + i % 33);
shortAscii[i] = "item" + i;
mediumAscii[i] = "Customer number " + i.ToString("D4") + " of the eastern region";
longAscii[i] = new string((char)('a' + i % 26), 296) + i.ToString("D4");
shortGeorgian[i] = new string(letter, 4);
mediumGeorgian[i] = new string(letter, 30);
}
}
[GlobalCleanup]
public void Cleanup()
{
connection.Dispose();
}
[IterationSetup]
public void CreateTable()
{
using var command = connection.CreateCommand();
command.CommandText = "DROP TABLE IF EXISTS bench; CREATE TABLE bench (a VARCHAR, b VARCHAR, c VARCHAR, d VARCHAR);";
command.ExecuteNonQuery();
}
[Benchmark]
public void AppendShortAscii() => Append(shortAscii);
[Benchmark]
public void AppendMediumAscii() => Append(mediumAscii);
[Benchmark]
public void AppendLongAscii() => Append(longAscii);
// Inline like AppendShortAscii, but every character takes three bytes: 12 bytes, the most that is kept inline.
[Benchmark]
public void AppendShortGeorgian() => Append(shortGeorgian);
[Benchmark]
public void AppendMediumGeorgian() => Append(mediumGeorgian);
private void Append(string[] values)
{
using var appender = connection.CreateAppender("bench");
for (var i = 0; i < RowCount; i++)
{
appender.AppendRow(values[i % ValueCount], static (row, value) =>
{
row.AppendValue(value).AppendValue(value).AppendValue(value).AppendValue(value);
});
}
}
}