mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-29 09:18:01 +01:00
Rune extensions micro-optimizations (#3910)
* Add benchmarks for potentially optimizable RuneExtensions * Add new RuneExtensions.DecodeSurrogatePair benchmark implementation Avoids intermediate heap array allocations which is especially nice when the rune is not surrogate pair because then array heap allocations are completely avoided. * Enable nullable reference types in RuneExtensions * Make RuneExtensions.MaxUnicodeCodePoint readonly Makes sure no one can accidentally change the value. Ideally would be const value. * Optimize RuneExtensions.DecodeSurrogatePair * Remove duplicate Rune.GetUnicodeCategory call * Add new RuneExtensions.IsSurrogatePair benchmark implementation Avoids intermediate heap allocations by using stack allocated buffer. * Optimize RuneExtensions.IsSurrogatePair * Add RuneExtensions.GetEncodingLength tests * Optimize RuneExtensions.GetEncodingLength * Optimize RuneExtensions.Encode * Print encoding name in benchmark results * Rename variable to better match return description * Add RuneExtensions.EncodeSurrogatePair benchmark --------- Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
72
Benchmarks/Text/RuneExtensions/Encode.cs
Normal file
72
Benchmarks/Text/RuneExtensions/Encode.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Tui = Terminal.Gui;
|
||||
|
||||
namespace Terminal.Gui.Benchmarks.Text.RuneExtensions;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for <see cref="Tui.RuneExtensions.Encode"/> performance fine-tuning.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[BenchmarkCategory (nameof (Tui.RuneExtensions))]
|
||||
public class Encode
|
||||
{
|
||||
/// <summary>
|
||||
/// Benchmark for previous implementation.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[ArgumentsSource (nameof (DataSource))]
|
||||
public byte [] Previous (Rune rune, byte [] destination, int start, int count)
|
||||
{
|
||||
_ = StringEncodingGetBytes (rune, destination, start, count);
|
||||
return destination;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark for current implementation.
|
||||
///
|
||||
/// Avoids intermediate heap allocations with stack allocated intermediate buffer.
|
||||
/// </summary>
|
||||
[Benchmark (Baseline = true)]
|
||||
[ArgumentsSource (nameof (DataSource))]
|
||||
public byte [] Current (Rune rune, byte [] destination, int start, int count)
|
||||
{
|
||||
_ = Tui.RuneExtensions.Encode (rune, destination, start, count);
|
||||
return destination;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Previous implementation with intermediate byte array and string allocation.
|
||||
/// </summary>
|
||||
private static int StringEncodingGetBytes (Rune rune, byte [] dest, int start = 0, int count = -1)
|
||||
{
|
||||
byte [] bytes = Encoding.UTF8.GetBytes (rune.ToString ());
|
||||
var length = 0;
|
||||
|
||||
for (var i = 0; i < (count == -1 ? bytes.Length : count); i++)
|
||||
{
|
||||
if (bytes [i] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dest [start + i] = bytes [i];
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public static IEnumerable<object []> DataSource ()
|
||||
{
|
||||
Rune[] runes = [ new Rune ('a'),"𝔞".EnumerateRunes().Single() ];
|
||||
|
||||
foreach (var rune in runes)
|
||||
{
|
||||
yield return new object [] { rune, new byte [16], 0, -1 };
|
||||
yield return new object [] { rune, new byte [16], 8, -1 };
|
||||
// Does not work in original implementation
|
||||
//yield return new object [] { rune, new byte [16], 8, 8 };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user