Skip to content

Commit 1e71a4a

Browse files
committed
performance imporvements, extensions, AI code fixes
1 parent 2e30ba2 commit 1e71a4a

20 files changed

Lines changed: 184 additions & 122 deletions

benchmarks/PolylineAlgorithm.Benchmarks/PolylineAlgorithm.Benchmarks.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
6-
<LangVersion>13.0</LangVersion>
5+
<TargetFrameworks>net8.0;net9.0;net10.0;</TargetFrameworks>
6+
<LangVersion>14.0</LangVersion>
77
<Nullable>enable</Nullable>
88
<ImplicitUsings>enable</ImplicitUsings>
99
<InvariantGlobalization>true</InvariantGlobalization>

benchmarks/PolylineAlgorithm.Benchmarks/PolylineBenchmark.cs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ namespace PolylineAlgorithm.Benchmarks;
1414
/// Benchmarks for the <see cref="PolylineValue"/> struct.
1515
/// </summary>
1616
public class PolylineBenchmark {
17-
private static readonly Consumer consumer = new();
17+
private static readonly Consumer _consumer = new();
1818

1919
[Params(1, 100, 1_000)]
2020
public int Count;
2121

22+
[Params(1000, 10_000, 100_000)]
23+
public int Iterations;
24+
2225
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
2326
/// <summary>
2427
/// Gets the character array representing the encoded polyline.
@@ -69,47 +72,55 @@ public void SetupData() {
6972
/// </summary>
7073
/// <returns>The encoded polyline.</returns>
7174
[Benchmark]
72-
public Polyline Polyline_FromString() {
73-
var polyline = Polyline
75+
public void Polyline_FromString() {
76+
for (int i = 0; i < Count; i++) {
77+
var polyline = Polyline
7478
.FromString(StringValue);
7579

76-
return polyline;
80+
_consumer.Consume(polyline);
81+
}
7782
}
7883

7984
/// <summary>
8085
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
8186
/// </summary>
8287
/// <returns>The encoded polyline.</returns>
8388
[Benchmark]
84-
public Polyline Polyline_FromCharArray() {
85-
var polyline = Polyline
89+
public void Polyline_FromCharArray() {
90+
for (int i = 0; i < Iterations; i++) {
91+
var polyline = Polyline
8692
.FromCharArray(CharArrayValue);
8793

88-
return polyline;
94+
_consumer.Consume(polyline);
95+
}
8996
}
9097

9198
/// <summary>
9299
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
93100
/// </summary>
94101
/// <returns>The encoded polyline.</returns>
95102
[Benchmark]
96-
public Polyline Polyline_FromMemory() {
97-
var polyline = Polyline
103+
public void Polyline_FromMemory() {
104+
for (int i = 0; i < Iterations; i++) {
105+
var polyline = Polyline
98106
.FromMemory(MemoryValue);
99107

100-
return polyline;
108+
_consumer.Consume(polyline);
109+
}
101110
}
102111

103112
/// <summary>
104113
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
105114
/// </summary>
106115
/// <returns>The encoded polyline.</returns>
107116
[Benchmark]
108-
public string Polyline_ToString() {
109-
var stringValue = PolylineValue
117+
public void Polyline_ToString() {
118+
for (int i = 0; i < Iterations; i++) {
119+
var stringValue = PolylineValue
110120
.ToString();
111121

112-
return stringValue;
122+
_consumer.Consume(stringValue);
123+
}
113124
}
114125

115126

@@ -119,35 +130,41 @@ public string Polyline_ToString() {
119130
/// <returns>The encoded polyline.</returns>
120131
[Benchmark]
121132
public void Polyline_CopyTo() {
122-
PolylineValue
133+
for (int i = 0; i < Iterations; i++) {
134+
PolylineValue
123135
.CopyTo(CopyToDestination);
124136

125-
CopyToDestination
126-
.Consume(consumer);
137+
CopyToDestination
138+
.Consume(_consumer);
139+
}
127140
}
128141

129142
/// <summary>
130143
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
131144
/// </summary>
132145
/// <returns>The encoded polyline.</returns>
133146
[Benchmark]
134-
public bool Polyline_Equals_SameValue() {
135-
var equals = PolylineValue
147+
public void Polyline_Equals_SameValue() {
148+
for (int i = 0; i < Iterations; i++) {
149+
var equals = PolylineValue
136150
.Equals(PolylineValue);
137151

138-
return equals;
152+
_consumer.Consume(equals);
153+
}
139154
}
140155

141156
/// <summary>
142157
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
143158
/// </summary>
144159
/// <returns>The encoded polyline.</returns>
145160
[Benchmark]
146-
public bool Polyline_Equals_DifferentValue() {
147-
var equals = PolylineValue
161+
public void Polyline_Equals_DifferentValue() {
162+
for (int i = 0; i < Iterations; i++) {
163+
var equals = PolylineValue
148164
.Equals(PolylineNotEqualValue);
149165

150-
return equals;
166+
_consumer.Consume(equals);
167+
}
151168
}
152169

153170

@@ -156,10 +173,12 @@ public bool Polyline_Equals_DifferentValue() {
156173
/// </summary>
157174
/// <returns>The encoded polyline.</returns>
158175
[Benchmark]
159-
public bool Polyline_Equals_DifferentType() {
160-
var equals = PolylineValue
176+
public void Polyline_Equals_DifferentType() {
177+
for (int i = 0; i < Iterations; i++) {
178+
var equals = PolylineValue
161179
.Equals(StringValue);
162180

163-
return equals;
181+
_consumer.Consume(equals);
182+
}
164183
}
165184
}

benchmarks/PolylineAlgorithm.Benchmarks/PolylineDecoderBenchmark.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public class PolylineDecoderBenchmark {
1919
[Params(1, 100, 1_000)]
2020
public int Count;
2121

22+
[Params(1000, 10_000, 100_000)]
23+
public int Iterations;
24+
2225
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
2326
/// <summary>
2427
/// Gets the string value representing the encoded polyline.
@@ -30,7 +33,7 @@ public class PolylineDecoderBenchmark {
3033
/// <summary>
3134
/// The polyline decoder instance.
3235
/// </summary>
33-
public PolylineDecoder Decoder = new();
36+
public readonly PolylineDecoder Decoder = new();
3437

3538
/// <summary>
3639
/// Sets up the data for the benchmarks.
@@ -45,8 +48,10 @@ public void SetupData() {
4548
/// </summary>
4649
[Benchmark]
4750
public void PolylineDecoder_Decode() {
48-
Decoder
49-
.Decode(Polyline)
50-
.Consume(_consumer);
51+
for (int i = 0; i < Iterations; i++) {
52+
Decoder
53+
.Decode(Polyline)
54+
.Consume(_consumer);
55+
}
5156
}
5257
}

benchmarks/PolylineAlgorithm.Benchmarks/PolylineEncoderBenchmark.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@
66
namespace PolylineAlgorithm.Benchmarks;
77

88
using BenchmarkDotNet.Attributes;
9+
using BenchmarkDotNet.Engines;
910
using PolylineAlgorithm;
11+
using PolylineAlgorithm.Extensions;
1012
using PolylineAlgorithm.Utility;
1113
using System.Collections.Generic;
1214

1315
/// <summary>
1416
/// Benchmarks for the <see cref="PolylineEncoder"/> class.
1517
/// </summary>
1618
public class PolylineEncoderBenchmark {
19+
private readonly Consumer _consumer = new();
20+
1721
[Params(1, 100, 1_000)]
1822
public int Count;
1923

24+
[Params(1000, 10_000, 100_000)]
25+
public int Iterations;
26+
2027
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
2128
/// <summary>
2229
/// Gets the enumeration of coordinates to be encoded.
@@ -32,7 +39,7 @@ public class PolylineEncoderBenchmark {
3239
/// <summary>
3340
/// The polyline encoder instance.
3441
/// </summary>
35-
public PolylineEncoder Encoder = new();
42+
public readonly PolylineEncoder Encoder = new();
3643

3744
/// <summary>
3845
/// Sets up the data for the benchmarks.
@@ -48,22 +55,26 @@ public void SetupData() {
4855
/// </summary>
4956
/// <returns>The encoded polyline.</returns>
5057
[Benchmark]
51-
public Polyline PolylineEncoder_Encode_List() {
52-
var polyline = Encoder
53-
.Encode(List!);
58+
public void PolylineEncoder_Encode_List() {
59+
for (int i = 0; i < Iterations; i++) {
60+
var polyline = Encoder
61+
.Encode(List!);
5462

55-
return polyline;
63+
_consumer.Consume(polyline);
64+
}
5665
}
5766

5867
/// <summary>
5968
/// Benchmarks the encoding of an enumeration of coordinates into a polyline.
6069
/// </summary>
6170
/// <returns>The encoded polyline.</returns>
6271
[Benchmark]
63-
public Polyline PolylineEncoder_Encode_Enumerator() {
64-
var polyline = Encoder
72+
public void PolylineEncoder_Encode_Enumerator() {
73+
for (int i = 0; i < Iterations; i++) {
74+
var polyline = Encoder
6575
.Encode(Enumeration!);
6676

67-
return polyline;
77+
_consumer.Consume(polyline);
78+
}
6879
}
6980
}

src/PolylineAlgorithm/Abstraction/AbstractPolylineDecoder.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ namespace PolylineAlgorithm.Abstraction;
1111
using PolylineAlgorithm.Internal.Logging;
1212
using PolylineAlgorithm.Properties;
1313
using System;
14-
using System.Diagnostics.CodeAnalysis;
1514

1615
/// <summary>
1716
/// Decodes encoded polyline strings into sequences of geographic coordinates.
@@ -83,13 +82,13 @@ public IEnumerable<TCoordinate> Decode(TPolyline polyline) {
8382

8483
while (true) {
8584
// Check if we have reached the end of the sequence
86-
if (position < sequence.Length) {
85+
if (position >= sequence.Length) {
8786
break;
8887
}
8988

9089
// Read the next value from the polyline encoding
91-
if (!PolylineEncoding.TryReadValue(ref latitude, sequence, ref position)
92-
|| !PolylineEncoding.TryReadValue(ref longitude, sequence, ref position)
90+
if (!PolylineEncoding.TryReadValue(ref latitude, ref sequence, ref position)
91+
|| !PolylineEncoding.TryReadValue(ref longitude, ref sequence, ref position)
9392
) {
9493
logger
9594
.LogInvalidPolylineWarning(position);
@@ -118,11 +117,11 @@ static void ValidateNullPolyline(TPolyline polyline, ILogger logger) {
118117
static void ValidateEmptySequence(ILogger logger, ReadOnlyMemory<char> sequence) {
119118
if (sequence.Length < Defaults.Polyline.Block.Length.Min) {
120119
logger
121-
.LogPolylineCannotBeShorterThanWarning(nameof(sequence), sequence.Length, Defaults.Polyline.Block.Length.Min);
120+
.LogPolylineCannotBeShorterThanWarning(nameof(polyline), sequence.Length, Defaults.Polyline.Block.Length.Min);
122121
logger.
123122
LogOperationFailedInfo(nameof(Decode));
124123

125-
throw new ArgumentException(string.Format(ExceptionMessageResource.PolylineCannotBeShorterThanExceptionMessage, sequence.Length), nameof(sequence));
124+
throw new ArgumentException(string.Format(ExceptionMessageResource.PolylineCannotBeShorterThanExceptionMessage, sequence.Length), nameof(polyline));
126125
}
127126
}
128127
}

src/PolylineAlgorithm/Abstraction/AbstractPolylineEncoder.cs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ namespace PolylineAlgorithm.Abstraction;
1111
using PolylineAlgorithm.Internal.Logging;
1212
using PolylineAlgorithm.Properties;
1313
using System;
14-
using System.Collections;
1514
using System.Collections.Generic;
1615
using System.Diagnostics;
1716

@@ -61,39 +60,31 @@ protected AbstractPolylineEncoder(PolylineEncodingOptions options) {
6160
/// <exception cref="ArgumentException">
6261
/// Thrown when <paramref name="coordinates"/> is an empty enumeration.
6362
/// </exception>
64-
public TPolyline Encode(IEnumerable<TCoordinate> coordinates) {
63+
public TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates) {
6564
var logger = Options
6665
.LoggerFactory
6766
.CreateLogger<AbstractPolylineEncoder<TCoordinate, TPolyline>>();
6867

6968
logger
7069
.LogOperationStartedInfo(nameof(Encode));
7170

72-
Debug.Assert(coordinates is not null, "Coordinates cannot be null.");
71+
Debug.Assert(coordinates.Length >= 0, "Count must be non-negative.");
7372

74-
ValidateNullCoordinates(coordinates, logger);
75-
76-
int count = GetCount(coordinates);
77-
78-
Debug.Assert(count >= 0, "Count must be non-negative.");
79-
80-
ValidateEmptyCoordinates(logger, count);
73+
ValidateEmptyCoordinates(coordinates, logger);
8174

8275
CoordinateVariance variance = new();
8376

8477
int position = 0;
8578
int consumed = 0;
86-
int length = GetMaxBufferLength(count);
79+
int length = GetMaxBufferLength(coordinates.Length);
8780

8881
Span<char> buffer = stackalloc char[length];
8982

90-
using var enumerator = coordinates.GetEnumerator();
91-
92-
while (enumerator.MoveNext()) {
83+
for (var i = 0; i < coordinates.Length; i++) {
9384
variance
9485
.Next(
95-
PolylineEncoding.Normalize(GetLatitude(enumerator.Current), CoordinateValueType.Latitude),
96-
PolylineEncoding.Normalize(GetLongitude(enumerator.Current), CoordinateValueType.Longitude)
86+
PolylineEncoding.Normalize(GetLatitude(coordinates[i]), CoordinateValueType.Latitude),
87+
PolylineEncoding.Normalize(GetLongitude(coordinates[i]), CoordinateValueType.Longitude)
9788
);
9889

9990
ValidateBuffer(logger, variance, position, buffer);
@@ -118,11 +109,6 @@ public TPolyline Encode(IEnumerable<TCoordinate> coordinates) {
118109

119110
return CreatePolyline(buffer[..position].ToString().AsMemory());
120111

121-
static int GetCount(IEnumerable<TCoordinate> coordinates) => coordinates switch {
122-
ICollection collection => collection.Count,
123-
_ => coordinates.Count(),
124-
};
125-
126112
static int GetRequiredLength(CoordinateVariance variance) =>
127113
PolylineEncoding.GetCharCount(variance.Latitude) + PolylineEncoding.GetCharCount(variance.Longitude);
128114

@@ -164,8 +150,8 @@ static void ValidateNullCoordinates(IEnumerable<TCoordinate> coordinates, ILogge
164150
}
165151
}
166152

167-
static void ValidateEmptyCoordinates(ILogger logger, int count) {
168-
if (count < 1) {
153+
static void ValidateEmptyCoordinates(ReadOnlySpan<TCoordinate> coordinates, ILogger logger) {
154+
if (coordinates.Length < 1) {
169155
logger
170156
.LogEmptyArgumentWarning(nameof(coordinates));
171157
logger

src/PolylineAlgorithm/Abstraction/IPolylineEncoder.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
//
55

66
namespace PolylineAlgorithm.Abstraction;
7-
8-
using System.Collections.Generic;
9-
107
/// <summary>
118
/// Defines a contract for encoding a sequence of geographic coordinates into an encoded polyline string.
129
/// </summary>
@@ -20,5 +17,5 @@ public interface IPolylineEncoder<TCoordinate, TPolyline> {
2017
/// <returns>
2118
/// A <typeparamref name="TPolyline"/> containing the encoded polyline string that represents the input coordinates.
2219
/// </returns>
23-
TPolyline Encode(IEnumerable<TCoordinate> coordinates);
20+
TPolyline Encode(ReadOnlySpan<TCoordinate> coordinates);
2421
}

0 commit comments

Comments
 (0)