Skip to content

Commit 952462e

Browse files
committed
Updated documentation and added more benchmarks
1 parent 3263825 commit 952462e

File tree

10 files changed

+163
-10
lines changed

10 files changed

+163
-10
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T
66

77
## [Unreleased]
88

9+
### Added
10+
- Added `Replace` methods which also tries to have the least amount of allocations.
11+
912
## [0.9.2] - 2022-04-06
1013

1114
### Added

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,28 @@ The blog goes a bit more in detail how it works with a simplistic version of the
2626

2727
## What it doesn't solve!
2828
The library is not meant as a general replacement for the `StringBuilder` shipped with the .net framework itself. You can head over to the documentation and read about the "Known limitations".
29-
The library works best for a small to medium amount of strings (not multiple 100'000 characters, even though it is still faster and uses less allocations). At anytime you can convert the `ValueStringBuilder` to a "normal" `StringBuilder`.
29+
The library works best for a small to medium amount of strings (not multiple 100'000 characters, even though it can be still faster and uses less allocations). At anytime you can convert the `ValueStringBuilder` to a "normal" `StringBuilder` and vice versa.
30+
31+
The normal use case is to add concatenate strings in a hot-path where the goal is to put as minimal pressure on the GC as possible.
3032

3133
## Documentation
3234
A more detailed documentation can be found [here](https://linkdotnet.github.io/StringBuilder/).
3335

3436
## Benchmark
3537

36-
The following table gives you a small comparison between the `StringBuilder` which is part of .NET and the `ValueStringBuilder`:
38+
The following table gives you a small comparison between the `StringBuilder` which is part of .NET, [`ZString`](https://github.com/Cysharp/ZString) and the `ValueStringBuilder`:
3739

3840
```no-class
39-
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
40-
| ------------------- | -------: | ------: | ------: | -----: | --------: |
41-
| DotNetStringBuilder | 430.7 ns | 8.52 ns | 7.55 ns | 0.3576 | 1,496 B |
42-
| ValueStringBuilder | 226.7 ns | 2.45 ns | 2.05 ns | 0.1395 | 584 B |
41+
| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Allocated |
42+
|-------------------- |-----------:|---------:|----------:|-----------:|------:|--------:|--------:|-------:|----------:|
43+
| DotNetStringBuilder | 401.7 ns | 29.15 ns | 84.56 ns | 373.4 ns | 1.00 | 0.00 | 0.3576 | - | 1,496 B |
44+
| ValueStringBuilder | 252.8 ns | 9.05 ns | 26.27 ns | 249.0 ns | 0.65 | 0.13 | 0.1583 | - | 664 B |
45+
| ZStringBuilderUtf8 | 1,239.0 ns | 44.93 ns | 131.06 ns | 1,192.0 ns | 3.18 | 0.56 | 15.6250 | - | 66,136 B |
46+
| ZStringBuilderUtf16 | 1,187.6 ns | 21.35 ns | 25.42 ns | 1,185.0 ns | 2.88 | 0.52 | 15.6250 | 0.0019 | 66,136 B |
4347
```
4448

49+
For more comparison check the documentation.
50+
4551
Another benchmark shows that this `ValueStringBuilder` uses less memory when it comes to appending `ValueTypes` such as `int`, `double`, ...
4652

4753
```no-class

docs/site/articles/comparison.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
uid: comparison
3+
---
4+
5+
# Comparison
6+
7+
The following document will show some key differences between the `ValueStringBuilder` and similar working string builder like the one from .NET itself.
8+
9+
## System.Text.StringBuilder
10+
11+
The `StringBuilder` shipped with the .NET Framework itself is a all-purpose string builder which allows a versatile use. `ValueStringBuilder` tries to mimic the API as much as possible so developers can adopt the `ValueStringBuilder` easily where it makes sense. In the following part `StringBuilder` refers to `System.Text.StringBuilder`.
12+
13+
**Key differences**:
14+
- `StringBuilder` is a class and does not have the restrictions coming with a `ref struct`. To know more head over to the [known limitations](xref:known_limitations) section.
15+
- `StringBuilder` works not on `Span<T>` but more on `string`s or `char`s. Sometimes even with pointers
16+
- `StringBuilder` uses chunks to represent the string, which the larger the string gets, the better it can perform. `ValueStringBuilder` only has one internal `Span` as representation which can cause fragmentation on very big strings.
17+
18+
## `ZString`
19+
Both string builder use similiar concepts to achieve. Both,`ValueStringBuilder` and `ZString`, are declared as `struct`s. `ValueStringBuilder` goes one step further and enforces its lifecycle to live on the **stack** and can never be put on the **heap**.
20+
21+
**Key differences**:
22+
* `ValueStringBuilder` is a `ref struct` which can never placed on the heap. `ZString` can be defined as a `class` field.
23+
* `ZString` has a very big initial buffer in its default (64kb) which can lead to more pressure on the GC.
24+
* `ZString` is more general purpose than `ValueStringBuilder` is.
25+
26+
27+
## Benchmark
28+
29+
The following table gives you a small comparison between the `StringBuilder` which is part of .NET, [`ZString`](https://github.com/Cysharp/ZString) and the `ValueStringBuilder`:
30+
31+
```no-class
32+
| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Allocated |
33+
|-------------------- |-----------:|---------:|----------:|-----------:|------:|--------:|--------:|-------:|----------:|
34+
| DotNetStringBuilder | 401.7 ns | 29.15 ns | 84.56 ns | 373.4 ns | 1.00 | 0.00 | 0.3576 | - | 1,496 B |
35+
| ValueStringBuilder | 252.8 ns | 9.05 ns | 26.27 ns | 249.0 ns | 0.65 | 0.13 | 0.1583 | - | 664 B |
36+
| ZStringBuilderUtf8 | 1,239.0 ns | 44.93 ns | 131.06 ns | 1,192.0 ns | 3.18 | 0.56 | 15.6250 | - | 66,136 B |
37+
| ZStringBuilderUtf16 | 1,187.6 ns | 21.35 ns | 25.42 ns | 1,185.0 ns | 2.88 | 0.52 | 15.6250 | 0.0019 | 66,136 B |
38+
```
39+
40+
For more comparison check the documentation.
41+
42+
Another benchmark shows that this `ValueStringBuilder` uses less memory when it comes to appending `ValueTypes` such as `int`, `double`, ...
43+
44+
```no-class
45+
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
46+
|-------------------- |---------:|---------:|---------:|--------:|-------:|----------:|
47+
| DotNetStringBuilder | 16.31 us | 0.414 us | 1.208 us | 1.5259 | - | 6 KB |
48+
| ValueStringBuilder | 14.61 us | 0.292 us | 0.480 us | 0.3357 | - | 1 KB |
49+
| ZStringBuilder | 15.47 us | 0.249 us | 0.323 us | 16.1285 | 0.0153 | 67 KB |
50+
51+
```
52+
53+
Checkout the [Benchmark](https://github.com/linkdotnet/StringBuilder/tree/main/tests/LinkDotNet.StringBuilder.Benchmarks) for more detailed comparison and setup.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
uid: getting_started
3+
---
4+
5+
# Getting started
6+
7+
The following section will show you how to use the [`ValueStringBuilder`](xref:LinkDotNet.StringBuilder.ValueStringBuilder).
8+
9+
For .NET 6 use the [nuget-package](https://www.nuget.org/packages/LinkDotNet.StringBuilder/):
10+
11+
> PM> Install-Package LinkDotNet.StringBuilder
12+
13+
Now that the package is installed the library can be used:
14+
15+
```csharp
16+
using System;
17+
18+
using LinkDotNet.StringBuilder; // Namespace of the library
19+
20+
public static class Program
21+
{
22+
public static void Main()
23+
{
24+
var stringBuilder = new ValueStringBuilder();
25+
26+
stringBuilder.AppendLine("Hello World!");
27+
stringBuilder.Append(0.3f);
28+
stringBuilder.Insert(6, "dear ");
29+
Console.WriteLine(stringBuilder.ToString());
30+
}
31+
}
32+
```
33+
34+
Prints:
35+
36+
> Hello dear World!
37+
0.3
38+
39+
[Here](https://dotnetfiddle.net/wM5r0q) an interactive example where you can fiddle around with the library. The example is hosted on [https://dotnetfiddle.net/](https://dotnetfiddle.net/) and already has the `ValueStringBuilder` nuget package included.

docs/site/articles/toc.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
- name: How does it work?
2-
href: concepts.md
1+
- name: Getting started
2+
href: getting_started.md
3+
items:
4+
- name: How does it work?
5+
href: concepts.md
6+
- name: Comparison
7+
href: comparison.md
38
- name: Known limitations
49
href: known_limitations.md

docs/site/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
- name: Api References
44
href: api/
55
homepage: api/index.md
6+
- name: GitHub
7+
href: https://github.com/linkdotnet/StringBuilder

src/LinkDotNet.StringBuilder/LinkDotNet.StringBuilder.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<PackageTags>string,stringbuilder,csharp,dotnet</PackageTags>
1717
<PackageReadmeFile>README.md</PackageReadmeFile>
1818
<PackageIcon>logo.png</PackageIcon>
19-
<PackageVersion>0.9.2</PackageVersion>
19+
<PackageVersion>0.9.3</PackageVersion>
2020
</PropertyGroup>
2121

2222
<ItemGroup>

tests/LinkDotNet.StringBuilder.Benchmarks/AppendBenchmarks.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using BenchmarkDotNet.Attributes;
2+
using Cysharp.Text;
23

34
namespace LinkDotNet.StringBuilder.Benchmarks;
45

56
[MemoryDiagnoser]
67
public class AppendBenchmarks
78
{
8-
[Benchmark]
9+
[Benchmark(Baseline = true)]
910
public string DotNetStringBuilder()
1011
{
1112
var builder = new System.Text.StringBuilder();
@@ -27,4 +28,28 @@ public string ValueStringBuilder()
2728
builder.AppendLine("We can also add other Append method if we want. But we keep it easy for now.");
2829
return builder.ToString();
2930
}
31+
32+
[Benchmark]
33+
public string ZStringBuilderUtf8()
34+
{
35+
var builder = ZString.CreateUtf8StringBuilder();
36+
builder.AppendLine("That is the first line of our benchmark.");
37+
builder.AppendLine("We can multiple stuff in here if want.");
38+
builder.AppendLine("We can multiple stuff in here if want.");
39+
builder.AppendLine("The idea is that we can resize the internal structure from time to time.");
40+
builder.AppendLine("We can also add other Append method if we want. But we keep it easy for now.");
41+
return builder.ToString();
42+
}
43+
44+
[Benchmark]
45+
public string ZStringBuilderUtf16()
46+
{
47+
var builder = ZString.CreateStringBuilder();
48+
builder.AppendLine("That is the first line of our benchmark.");
49+
builder.AppendLine("We can multiple stuff in here if want.");
50+
builder.AppendLine("We can multiple stuff in here if want.");
51+
builder.AppendLine("The idea is that we can resize the internal structure from time to time.");
52+
builder.AppendLine("We can also add other Append method if we want. But we keep it easy for now.");
53+
return builder.ToString();
54+
}
3055
}

tests/LinkDotNet.StringBuilder.Benchmarks/AppendValueTypesBenchmark.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using BenchmarkDotNet.Attributes;
2+
using Cysharp.Text;
23

34
namespace LinkDotNet.StringBuilder.Benchmarks;
45

@@ -40,4 +41,22 @@ public string ValueStringBuilder()
4041

4142
return builder.ToString();
4243
}
44+
45+
[Benchmark]
46+
public string ZStringBuilder()
47+
{
48+
var builder = ZString.CreateStringBuilder();
49+
50+
for (var i = 0; i < 25; i++)
51+
{
52+
builder.Append(true);
53+
builder.Append(int.MaxValue);
54+
builder.Append(decimal.MaxValue);
55+
builder.Append(byte.MinValue);
56+
builder.Append(float.Epsilon);
57+
builder.Append(double.Epsilon);
58+
}
59+
60+
return builder.ToString();
61+
}
4362
}

tests/LinkDotNet.StringBuilder.Benchmarks/LinkDotNet.StringBuilder.Benchmarks.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<ItemGroup>
1111
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
12+
<PackageReference Include="ZString" Version="2.4.4" />
1213
</ItemGroup>
1314

1415
<ItemGroup>

0 commit comments

Comments
 (0)