Skip to content

Commit 6a350d7

Browse files
sramekpetePetr Sramek
and
Petr Sramek
authored
memory allocation optimized (#17)
Co-authored-by: Petr Sramek <petr.sramek@outlook.cz>
1 parent 4339c10 commit 6a350d7

File tree

11 files changed

+601
-545
lines changed

11 files changed

+601
-545
lines changed

benchmarks/DropoutCoder.PolylineAlgorithm.Implementation.Benchmarks/DecodePerformanceBenchmark.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
{
33
using BenchmarkDotNet.Attributes;
44
using BenchmarkDotNet.Engines;
5+
using System;
56

67
[MemoryDiagnoser]
78
public class DecodePerformanceBenchmark
89
{
910
private Consumer _consumer = new Consumer();
10-
1111
public static IEnumerable<(int, char[])> Polylines()
1212
{
1313
yield return (1, "mz}lHssngJj`gqSnx~lEcovfTnms{Zdy~qQj_deI".ToCharArray());
@@ -21,15 +21,15 @@ public class DecodePerformanceBenchmark
2121

2222
[Benchmark]
2323
[ArgumentsSource(nameof(Polylines))]
24-
public void Decode_V2((int, char[]) arg) => V2.Decode(arg.Item2).Consume(_consumer);
24+
public void Decode_V1_Parallel((int, char[]) arg) => Parallel.For(0, 100, (i) => V1.Decode(arg.Item2).Consume(_consumer));
2525

2626
[Benchmark]
2727
[ArgumentsSource(nameof(Polylines))]
28-
public void Decode_V1_Parallel((int, char[]) arg) => Parallel.For(100, 200, (i) => V1.Decode(arg.Item2).Consume(_consumer));
28+
public void Decode_V2((int, char[]) arg) => V2.Decode(arg.Item2).Consume(_consumer);
2929

3030
[Benchmark]
3131
[ArgumentsSource(nameof(Polylines))]
32-
public void Decode_V2_Parallel((int, char[]) arg) => Parallel.For(100, 200, (i) => V2.Decode(arg.Item2).Consume(_consumer));
32+
public void Decode_V2_Parallel((int, char[]) arg) => Parallel.For(0, 100, (i) => V2.Decode(arg.Item2).Consume(_consumer));
3333

3434
private class V1
3535
{
@@ -125,18 +125,18 @@ private class V2
125125
throw new ArgumentException(nameof(polyline));
126126
}
127127

128-
int index = 0;
128+
int offset = 0;
129129
int latitude = 0;
130130
int longitude = 0;
131131

132-
while (index < polyline.Length)
132+
while (offset < polyline.Length)
133133
{
134-
if (!TryCalculateNext(ref polyline, ref index, ref latitude))
134+
if (!TryCalculateNext(ref polyline, ref offset, ref latitude))
135135
{
136136
throw new InvalidOperationException();
137137
}
138138

139-
if (!TryCalculateNext(ref polyline, ref index, ref longitude))
139+
if (!TryCalculateNext(ref polyline, ref offset, ref longitude))
140140
{
141141
throw new InvalidOperationException();
142142
}
@@ -152,20 +152,20 @@ private class V2
152152
}
153153
}
154154

155-
private static bool TryCalculateNext(ref char[] polyline, ref int index, ref int value)
155+
private static bool TryCalculateNext(ref char[] polyline, ref int offset, ref int value)
156156
{
157157
int chunk;
158158
int sum = 0;
159159
int shifter = 0;
160160

161161
do
162162
{
163-
chunk = polyline[index++] - Constants.ASCII.QuestionMark;
163+
chunk = polyline[offset++] - Constants.ASCII.QuestionMark;
164164
sum |= (chunk & Constants.ASCII.UnitSeparator) << shifter;
165165
shifter += Constants.ShiftLength;
166-
} while (chunk >= Constants.ASCII.Space && index < polyline.Length);
166+
} while (chunk >= Constants.ASCII.Space && offset < polyline.Length);
167167

168-
if (index >= polyline.Length && chunk >= Constants.ASCII.Space)
168+
if (offset >= polyline.Length && chunk >= Constants.ASCII.Space)
169169
return false;
170170

171171
value += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1;
@@ -175,7 +175,7 @@ private static bool TryCalculateNext(ref char[] polyline, ref int index, ref int
175175

176176
private static double GetDoubleRepresentation(int value)
177177
{
178-
return Convert.ToDouble(value) / Constants.Precision;
178+
return value / Constants.Precision;
179179
}
180180

181181
public static class CoordinateValidator

benchmarks/DropoutCoder.PolylineAlgorithm.Implementation.Benchmarks/EncodePerformanceBenchmark.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ public static string Encode(IEnumerable<(double Latitude, double Longitude)> coo
137137

138138
EnsureCoordinates(coordinates);
139139

140-
int lastLatitude = 0;
141-
int lastLongitude = 0;
140+
int previousLatitude = 0;
141+
int previousLongitude = 0;
142142

143143
var sb = _pool.Get();
144144

@@ -147,11 +147,11 @@ public static string Encode(IEnumerable<(double Latitude, double Longitude)> coo
147147
int latitude = GetIntegerRepresentation(coordinate.Latitude);
148148
int longitude = GetIntegerRepresentation(coordinate.Longitude);
149149

150-
sb.Append(GetEncodedCharacters(latitude - lastLatitude).ToArray());
151-
sb.Append(GetEncodedCharacters(longitude - lastLongitude).ToArray());
150+
sb.Append(GetEncodedCharacters(latitude - previousLatitude).ToArray());
151+
sb.Append(GetEncodedCharacters(longitude - previousLongitude).ToArray());
152152

153-
lastLatitude = latitude;
154-
lastLongitude = longitude;
153+
previousLatitude = latitude;
154+
previousLongitude = longitude;
155155
}
156156

157157
var result = sb.ToString();

src/DropoutCoder.PolylineAlgorithm.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<DocumentationFile>
1111
</DocumentationFile>
1212
</PropertyGroup>
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
15+
</ItemGroup>
1316
<ItemGroup>
1417
<Compile Update="ExceptionMessageResource.Designer.cs">
1518
<DesignTime>True</DesignTime>

src/Encoding/PolylineEncodingBase.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
44
//
55

6-
using DropoutCoder.PolylineAlgorithm;
7-
86
namespace DropoutCoder.PolylineAlgorithm.Encoding
97
{
108
using System;

src/PolylineAlgorithm.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace DropoutCoder.PolylineAlgorithm
77
{
88
using DropoutCoder.PolylineAlgorithm.Validation;
9+
using Microsoft.Extensions.ObjectPool;
910
using System;
1011
using System.Collections.Generic;
1112
using System.Linq;
@@ -16,6 +17,8 @@ namespace DropoutCoder.PolylineAlgorithm
1617
/// </summary>
1718
public static class PolylineAlgorithm
1819
{
20+
private static readonly ObjectPool<StringBuilder> _pool = new DefaultObjectPoolProvider().CreateStringBuilderPool(5, 250);
21+
1922
#region Methods
2023

2124
/// <summary>
@@ -37,7 +40,6 @@ public static class PolylineAlgorithm
3740
int index = 0;
3841
int latitude = 0;
3942
int longitude = 0;
40-
var result = new List<(double Latitude, double Longitude)>();
4143

4244
// Looping through encoded polyline char array
4345
while (index < polyline.Length)
@@ -61,10 +63,8 @@ public static class PolylineAlgorithm
6163
throw new InvalidOperationException(ExceptionMessageResource.PolylineCharArrayIsMalformed);
6264
}
6365

64-
result.Add(coordinate);
66+
yield return coordinate;
6567
}
66-
67-
return result;
6868
}
6969

7070
/// <summary>
@@ -85,24 +85,28 @@ public static string Encode(IEnumerable<(double Latitude, double Longitude)> coo
8585
EnsureCoordinates(coordinates);
8686

8787
// Initializing local variables
88-
int lastLat = 0;
89-
int lastLng = 0;
90-
var sb = new StringBuilder();
88+
int previousLatitude = 0;
89+
int previousLongitude = 0;
90+
var sb = _pool.Get();
9191

9292
// Looping over coordinates and building encoded result
9393
foreach (var coordinate in coordinates)
9494
{
9595
int latitude = GetIntegerRepresentation(coordinate.Latitude);
9696
int longitude = GetIntegerRepresentation(coordinate.Longitude);
9797

98-
sb.Append(GetEncodedCharacters(latitude - lastLat).ToArray());
99-
sb.Append(GetEncodedCharacters(longitude - lastLng).ToArray());
98+
sb.Append(GetEncodedCharacters(latitude - previousLatitude).ToArray());
99+
sb.Append(GetEncodedCharacters(longitude - previousLongitude).ToArray());
100100

101-
lastLat = latitude;
102-
lastLng = longitude;
101+
previousLatitude = latitude;
102+
previousLongitude = longitude;
103103
}
104104

105-
return sb.ToString();
105+
var result = sb.ToString();
106+
107+
_pool.Return(sb);
108+
109+
return result;
106110
}
107111

108112
/// <summary>

tests/Defaults.cs

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,75 @@
33
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
44
//
55

6-
namespace DropoutCoder.PolylineAlgorithm.Tests {
7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
6+
namespace DropoutCoder.PolylineAlgorithm.Tests
7+
{
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Linq;
1011

11-
/// <summary>
12-
/// Defines default values and objects used for testing purposes
13-
/// </summary>
14-
public static class Defaults {
15-
/// <summary>
16-
/// Defines default decoded values and objects udśed for testing purposes
17-
/// </summary>
18-
public static class Coordinate {
19-
#region Fields
12+
/// <summary>
13+
/// Defines default values and objects used for testing purposes
14+
/// </summary>
15+
public static class Defaults
16+
{
17+
/// <summary>
18+
/// Defines default decoded values and objects udśed for testing purposes
19+
/// </summary>
20+
public static class Coordinate
21+
{
22+
#region Fields
2023

21-
/// <summary>
22-
/// Defines empty range of coordinates. Equals to decoded <seealso cref="Polyline.Empty"/>
23-
/// </summary>
24-
public static readonly IEnumerable<(double Latitude, double Longitude)> Empty = Enumerable.Empty<(double Latitude, double Longitude)>();
24+
/// <summary>
25+
/// Defines empty range of coordinates. Equals to decoded <seealso cref="Polyline.Empty"/>
26+
/// </summary>
27+
public static readonly IEnumerable<(double Latitude, double Longitude)> Empty = Enumerable.Empty<(double Latitude, double Longitude)>();
2528

26-
/// <summary>
27-
/// Defines range of invalid coordinates. Equals to decoded <seealso cref="Polyline.Invalid"/>
28-
/// </summary>
29-
public static readonly IEnumerable<(double Latitude, double Longitude)> Invalid = new[] {
30-
(149.47383, 259.06250),
31-
(-158.37407, 225.31250),
32-
(152.99363, -220.93750),
33-
(-144.49024, -274.37500)
34-
};
29+
/// <summary>
30+
/// Defines range of invalid coordinates. Equals to decoded <seealso cref="Polyline.Invalid"/>
31+
/// </summary>
32+
public static readonly IEnumerable<(double Latitude, double Longitude)> Invalid = new[] {
33+
(149.47383, 259.06250),
34+
(-158.37407, 225.31250),
35+
(152.99363, -220.93750),
36+
(-144.49024, -274.37500)
37+
};
3538

36-
/// <summary>
37-
/// Defines range of valid coordinates. Equals to decoded <seealso cref="Polyline.Valid"/>
38-
/// </summary>
39-
public static readonly IEnumerable<(double Latitude, double Longitude)> Valid = new[] {
40-
(49.47383, 59.06250),
41-
(-58.37407, 25.31250),
42-
(52.99363, -120.93750),
43-
(-44.49024, -174.37500)
44-
};
39+
/// <summary>
40+
/// Defines range of valid coordinates. Equals to decoded <seealso cref="Polyline.Valid"/>
41+
/// </summary>
42+
public static readonly IEnumerable<(double Latitude, double Longitude)> Valid = new[] {
43+
(49.47383, 59.06250),
44+
(-58.37407, 25.31250),
45+
(52.99363, -120.93750),
46+
(-44.49024, -174.37500)
47+
};
4548

46-
#endregion
47-
}
49+
#endregion
50+
}
4851

49-
/// <summary>
50-
/// Defines default encoded values and objects udśed for testing purposes
51-
/// </summary>
52-
public static class Polyline {
53-
#region Fields
52+
/// <summary>
53+
/// Defines default encoded values and objects udśed for testing purposes
54+
/// </summary>
55+
public static class Polyline
56+
{
57+
#region Fields
5458

55-
/// <summary>
56-
/// Defines empty string of polyline encoded coordinates. Equals to encoded <seealso cref="Coordinate.Empty"/>
57-
/// </summary>
58-
public static readonly string Empty = String.Empty;
59+
/// <summary>
60+
/// Defines empty string of polyline encoded coordinates. Equals to encoded <seealso cref="Coordinate.Empty"/>
61+
/// </summary>
62+
public static readonly string Empty = String.Empty;
5963

60-
/// <summary>
61-
/// Defines polyline encoded range of invalid coordinates. Equals to encoded <seealso cref="Coordinate.Invalid"/>
62-
/// </summary>
63-
public static readonly string Invalid = "mnc~Qsm_ja@";
64+
/// <summary>
65+
/// Defines polyline encoded range of invalid coordinates. Equals to encoded <seealso cref="Coordinate.Invalid"/>
66+
/// </summary>
67+
public static readonly string Invalid = "mnc~Qsm_ja@";
6468

65-
/// <summary>
66-
/// Defines polyline encoded range of valid coordinates. Equals to encoded <seealso cref="Coordinate.Valid"/>
67-
/// </summary>
68-
public static readonly string Valid = "mz}lHssngJj`gqSnx~lEcovfTnms{Zdy~qQj_deI";
69+
/// <summary>
70+
/// Defines polyline encoded range of valid coordinates. Equals to encoded <seealso cref="Coordinate.Valid"/>
71+
/// </summary>
72+
public static readonly string Valid = "mz}lHssngJj`gqSnx~lEcovfTnms{Zdy~qQj_deI";
6973

70-
#endregion
71-
}
72-
}
74+
#endregion
75+
}
76+
}
7377
}

0 commit comments

Comments
 (0)