fix: Optimizes Point2D by implementing ISpanFormattable (#1203)
Point2D implements a basic TryFormat function. Only a single format is supported: "(X, Y)" where X and Y are base 10 integers.
Uses recent improvements in string interpolation to write the characters to the destination without first allocating memory for boxed arguments or intermediate strings.
This is a partial solution to issue #1067. A similar solution well be implemented in other classes in Geometry if it looks promising.
Benchmarks
---
d6f8dabf3c/Program.cs
Before change:
```
BenchmarkDotNet=v0.13.2, OS=ubuntu 22.04
Intel Core i7-9700K CPU 3.60GHz (Coffee Lake), 1 CPU, 8 logical and 8 physical cores
.NET SDK=6.0.402
[Host] : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|--------------------------------- |----------:|---------:|---------:|-------:|----------:|
| CallToString | 44.27 ns | 0.068 ns | 0.063 ns | 0.0063 | 40 B |
| InterpolatedString | 110.64 ns | 0.316 ns | 0.295 ns | 0.0126 | 80 B |
| InterpolatedStringMultiplePoints | 211.36 ns | 0.349 ns | 0.326 ns | 0.0293 | 184 B |
```
After change:
```
BenchmarkDotNet=v0.13.2, OS=ubuntu 22.04
Intel Core i7-9700K CPU 3.60GHz (Coffee Lake), 1 CPU, 8 logical and 8 physical cores
.NET SDK=6.0.402
[Host] : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|--------------------------------- |----------:|---------:|---------:|-------:|----------:|
| CallToString | 55.13 ns | 0.826 ns | 0.772 ns | 0.0063 | 40 B |
| InterpolatedString | 55.73 ns | 0.954 ns | 0.892 ns | 0.0063 | 40 B |
| InterpolatedStringMultiplePoints | 105.42 ns | 0.151 ns | 0.134 ns | 0.0101 | 64 B |
```
This commit is contained in:
parent
5797c85d75
commit
be19d9aae6
2 changed files with 40 additions and 3 deletions
17
Projects/Server.Tests/Tests/Geometry/Point2DTests.cs
Normal file
17
Projects/Server.Tests/Tests/Geometry/Point2DTests.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Server.Tests
|
||||
{
|
||||
public class Point2DTests
|
||||
{
|
||||
[Fact]
|
||||
public void TestToString()
|
||||
{
|
||||
Assert.Equal("(0, 0)", new Point2D(0, 0).ToString());
|
||||
Assert.Equal("(1, 1)", new Point2D(1, 1).ToString());
|
||||
Assert.Equal($"({Int32.MaxValue}, {Int32.MaxValue})", new Point2D(Int32.MaxValue, Int32.MaxValue).ToString());
|
||||
Assert.Equal($"({Int32.MinValue}, {Int32.MinValue})", new Point2D(Int32.MinValue, Int32.MinValue).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ namespace Server;
|
|||
[Parsable]
|
||||
public struct Point2D
|
||||
: IPoint2D, IComparable<Point2D>, IComparable<IPoint2D>, IEquatable<object>, IEquatable<Point2D>,
|
||||
IEquatable<IPoint2D>
|
||||
IEquatable<IPoint2D>, ISpanFormattable
|
||||
{
|
||||
internal int m_X;
|
||||
internal int m_Y;
|
||||
|
|
@ -51,8 +51,6 @@ public struct Point2D
|
|||
{
|
||||
}
|
||||
|
||||
public override string ToString() => $"({m_X}, {m_Y})";
|
||||
|
||||
public static Point2D Parse(string value)
|
||||
{
|
||||
var start = value.IndexOfOrdinal('(');
|
||||
|
|
@ -112,4 +110,26 @@ public struct Point2D
|
|||
var xComparison = m_X.CompareTo(other.X);
|
||||
return xComparison != 0 ? xComparison : m_Y.CompareTo(other.Y);
|
||||
}
|
||||
|
||||
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
|
||||
{
|
||||
return destination.TryWrite(provider, $"({m_X}, {m_Y})", out charsWritten);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
// Maximum number of characters that are needed to represent this:
|
||||
// 4 characters for (, )
|
||||
// Up to 11 characters to represent each integer
|
||||
Span<char> span = stackalloc char[4+11*2];
|
||||
this.TryFormat(span, out var charsWritten, null, null);
|
||||
return span.Slice(0, charsWritten).ToString();
|
||||
}
|
||||
|
||||
public string ToString(string format, IFormatProvider formatProvider)
|
||||
{
|
||||
// format and formatProvider are not doing anything right now, so use the
|
||||
// default ToString implementation.
|
||||
return ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue