Files
Terminal.Gui/Benchmarks/Text/RuneExtensions/Encode.cs
Tonttu e24bd67658 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>
2025-02-25 09:42:32 -07:00

73 lines
2.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };
}
}
}