StringExtensions.ToString(IEnumerable<Rune>) stackalloc char buffer with StringBuilder fallback

This commit is contained in:
Tonttu
2025-03-15 14:15:55 +02:00
committed by Tig
parent b6a5ca1d4e
commit 5ab51fc08b
2 changed files with 68 additions and 24 deletions

View File

@@ -16,27 +16,28 @@ public class ToStringEnumerable
/// </summary>
[Benchmark]
[ArgumentsSource (nameof (DataSource))]
public string Previous (IEnumerable<Rune> runes, int size)
public string Previous (IEnumerable<Rune> runes, int len)
{
return StringAppendInLoop (runes);
return StringConcatInLoop (runes);
}
/// <summary>
/// Benchmark for current implementation with rune chars appending to StringBuilder.
/// Benchmark for current implementation with stackalloc char buffer and
/// fallback to rune chars appending to StringBuilder.
/// </summary>
/// <param name="runes"></param>
/// <returns></returns>
[Benchmark (Baseline = true)]
[ArgumentsSource (nameof (DataSource))]
public string Current (IEnumerable<Rune> runes, int size)
public string Current (IEnumerable<Rune> runes, int len)
{
return Tui.StringExtensions.ToString (runes);
}
/// <summary>
/// Previous implementation with string append in a loop.
/// Previous implementation with string concatenation in a loop.
/// </summary>
private static string StringAppendInLoop (IEnumerable<Rune> runes)
private static string StringConcatInLoop (IEnumerable<Rune> runes)
{
var str = string.Empty;
@@ -50,21 +51,34 @@ public class ToStringEnumerable
public IEnumerable<object []> DataSource ()
{
string textSource =
"""
Ĺόŕéḿ íśúḿ d́όĺόŕ śí áḿé, ćόńśéćt́ét́úŕ ád́íṕíśćíńǵ éĺí. ŕáéśéń q́úíś ĺúćt́úś éĺí. Íńt́éǵéŕ ú áŕćú éǵé d́όĺόŕ śćéĺéŕíśq́úé ḿát́t́íś áć é d́íáḿ.
éĺĺéńt́éśq́úé śé d́áṕíb́úś ḿáśśá, v́éĺ t́ŕíśt́íq́úé d́úí. Śéd́ v́ít́áé ńéq́úé éú v́éĺít́ όŕńáŕé áĺíq́úét́. Ú q́úíś όŕćí t́éḿṕόŕ, t́éḿṕόŕ t́úŕṕíś í, t́éḿṕúś ńéq́úé.
ŕáéśéń śáíéń t́úŕṕíś, όŕńáŕé v́éĺ ḿáúŕíś á, v́áŕíúś śúśćíí áńt́é. Ú úĺv́íńáŕ t́úŕṕíś ḿáśśá, q́úíś ćúŕśúś áŕćú f́áúćíb́úś íń.
Óŕćí v́áŕíúś ńát́όq́úé éńát́íb́úś é ḿáǵńíś d́íś áŕt́úŕíéńt́ ḿόńt́éś, ńáśćét́úŕ ŕíd́íćúĺúś ḿúś. F́úśćé á é b́ĺáńd́ít́, ćόńv́áĺĺíś q́úáḿ é, v́úĺṕút́át́é ĺáćúś.
Śúśṕéńd́íśśé śí áḿé áŕćú ú áŕćú f́áúćíb́úś v́áŕíúś. V́ív́áḿúś śí áḿé ḿáx́íḿúś d́íáḿ. Ńáḿ é ĺéό, h́áŕét́ŕá éú ĺόb́όŕt́íś á, t́ŕíśt́íq́úé ú f́éĺíś.
""";
// Extra argument as workaround for the summary grouping different length collections to same baseline making comparison difficult.
int[] sizes = [1, 10, 100, textSource.Length / 2, textSource.Length];
foreach (int size in sizes)
// Extra length argument as workaround for the summary grouping
// different length collections to same baseline making comparison difficult.
foreach (string text in GetTextData ())
{
yield return [textSource.EnumerateRunes ().Take (size).ToArray (), size];
Rune [] runes = [..text.EnumerateRunes ()];
yield return [runes, runes.Length];
}
}
private IEnumerable<string> GetTextData ()
{
string textSource =
"""
Ĺόŕéḿ íśúḿ d́όĺόŕ śí áḿé, ćόńśéćt́ét́úŕ ád́íṕíśćíńǵ éĺí. ŕáéśéń q́úíś ĺúćt́úś éĺí. Íńt́éǵéŕ ú áŕćú éǵé d́όĺόŕ śćéĺéŕíśq́úé ḿát́t́íś áć é d́íáḿ.
éĺĺéńt́éśq́úé śé d́áṕíb́úś ḿáśśá, v́éĺ t́ŕíśt́íq́úé d́úí. Śéd́ v́ít́áé ńéq́úé éú v́éĺít́ όŕńáŕé áĺíq́úét́. Ú q́úíś όŕćí t́éḿṕόŕ, t́éḿṕόŕ t́úŕṕíś í, t́éḿṕúś ńéq́úé.
ŕáéśéń śáíéń t́úŕṕíś, όŕńáŕé v́éĺ ḿáúŕíś á, v́áŕíúś śúśćíí áńt́é. Ú úĺv́íńáŕ t́úŕṕíś ḿáśśá, q́úíś ćúŕśúś áŕćú f́áúćíb́úś íń.
Óŕćí v́áŕíúś ńát́όq́úé éńát́íb́úś é ḿáǵńíś d́íś áŕt́úŕíéńt́ ḿόńt́éś, ńáśćét́úŕ ŕíd́íćúĺúś ḿúś. F́úśćé á é b́ĺáńd́ít́, ćόńv́áĺĺíś q́úáḿ é, v́úĺṕút́át́é ĺáćúś.
Śúśṕéńd́íśśé śí áḿé áŕćú ú áŕćú f́áúćíb́úś v́áŕíúś. V́ív́áḿúś śí áḿé ḿáx́íḿúś d́íáḿ. Ńáḿ é ĺéό, h́áŕét́ŕá éú ĺόb́όŕt́íś á, t́ŕíśt́íq́úé ú f́éĺíś.
""";
int[] lengths = [1, 10, 100, textSource.Length / 2, textSource.Length];
foreach (int length in lengths)
{
yield return textSource [..length];
}
string textLongerThanStackallocThreshold = string.Concat(Enumerable.Repeat(textSource, 10));
yield return textLongerThanStackallocThreshold;
}
}