|
2 | 2 | {
|
3 | 3 | using BenchmarkDotNet.Attributes;
|
4 | 4 | using BenchmarkDotNet.Engines;
|
| 5 | + using DropoutCoder.PolylineAlgorithm.Validation; |
5 | 6 | using System;
|
6 | 7 |
|
7 | 8 | [MemoryDiagnoser]
|
8 | 9 | public class DecodePerformanceBenchmark
|
9 | 10 | {
|
10 | 11 | private Consumer _consumer = new Consumer();
|
11 |
| - public static IEnumerable<(int, char[])> Polylines() |
| 12 | + public static IEnumerable<(int, string)> Polylines() |
12 | 13 | {
|
13 |
| - yield return (1, "mz}lHssngJj`gqSnx~lEcovfTnms{Zdy~qQj_deI".ToCharArray()); |
14 |
| - yield return (2, "}vwdGjafcRsvjKi}pxUhsrtCngtcAjjgzEdqvtLrscbKj}nr@wetlUc`nq]}_kfCyrfaK~wluUl`u}|@wa{lUmmuap@va{lU~oihCu||bF`|era@wsnnIjny{DxamaScqxza@dklDf{}kb@mtpeCavfzGqhx`Wyzzkm@jm`d@dba~Pppkg@h}pxU|rtnHp|flA|~xaPuykyN}fhv[h}pxUx~p}Ymx`sZih~iB{edwB".ToCharArray()); |
15 |
| - yield return (3, "}adrJh}}cVazlw@uykyNhaqeE`vfzG_~kY}~`eTsr{~Cwn~aOty_g@thapJvvoqKxt{sStfahDmtvmIfmiqBhjq|HujpgComs{Z}dhdKcidPymnvBqmquE~qrfI`x{lPf|ftGn~}d_@q}saAurjmu@bwr_DxrfaK~{rO~bidPwfduXwlioFlpum@twvfFpmi~VzxcsOqyejYhh|i@pbnr[twvfF_ueUujvbSa_d~ZkcnjZla~f[pmquEebxo[j}nr@xnn|H{gyiKbh{yH`oenn@y{mpIrbd~EmipgH}fuov@hjqtTp|flAttvkFrym_d@|eyCwn~aOfvdNmeawM??{yxdUcidPca{}D_atqGenzcAlra{@trgWhn{aZ??tluqOgu~sH".ToCharArray()); |
| 14 | + yield return (1, "mz}lHssngJj`gqSnx~lEcovfTnms{Zdy~qQj_deI"); |
| 15 | + yield return (2, "}vwdGjafcRsvjKi}pxUhsrtCngtcAjjgzEdqvtLrscbKj}nr@wetlUc`nq]}_kfCyrfaK~wluUl`u}|@wa{lUmmuap@va{lU~oihCu||bF`|era@wsnnIjny{DxamaScqxza@dklDf{}kb@mtpeCavfzGqhx`Wyzzkm@jm`d@dba~Pppkg@h}pxU|rtnHp|flA|~xaPuykyN}fhv[h}pxUx~p}Ymx`sZih~iB{edwB"); |
| 16 | + yield return (3, "}adrJh}}cVazlw@uykyNhaqeE`vfzG_~kY}~`eTsr{~Cwn~aOty_g@thapJvvoqKxt{sStfahDmtvmIfmiqBhjq|HujpgComs{Z}dhdKcidPymnvBqmquE~qrfI`x{lPf|ftGn~}d_@q}saAurjmu@bwr_DxrfaK~{rO~bidPwfduXwlioFlpum@twvfFpmi~VzxcsOqyejYhh|i@pbnr[twvfF_ueUujvbSa_d~ZkcnjZla~f[pmquEebxo[j}nr@xnn|H{gyiKbh{yH`oenn@y{mpIrbd~EmipgH}fuov@hjqtTp|flAttvkFrym_d@|eyCwn~aOfvdNmeawM??{yxdUcidPca{}D_atqGenzcAlra{@trgWhn{aZ??tluqOgu~sH"); |
16 | 17 | }
|
17 | 18 |
|
18 | 19 | [Benchmark(Baseline = true)]
|
19 | 20 | [ArgumentsSource(nameof(Polylines))]
|
20 |
| - public void Decode_V1((int, char[]) arg) => V1.Decode(arg.Item2).Consume(_consumer); |
| 21 | + public void Decode_V1((int, string) arg) => For.Loop(1001, () => V1.Decode(arg.Item2).Consume(_consumer)); |
21 | 22 |
|
22 | 23 | [Benchmark]
|
23 | 24 | [ArgumentsSource(nameof(Polylines))]
|
24 |
| - public void Decode_V1_Parallel((int, char[]) arg) => Parallel.For(0, 100, (i) => V1.Decode(arg.Item2).Consume(_consumer)); |
| 25 | + public void Decode_V2((int, string) arg) => For.Loop(1001, () => V2.Decode(arg.Item2).Consume(_consumer)); |
25 | 26 |
|
26 | 27 | [Benchmark]
|
27 | 28 | [ArgumentsSource(nameof(Polylines))]
|
28 |
| - public void Decode_V2((int, char[]) arg) => V2.Decode(arg.Item2).Consume(_consumer); |
| 29 | + public void Decode_V3((int, string) arg) => For.Loop(1001, () => V3.Decode(arg.Item2).Consume(_consumer)); |
29 | 30 |
|
30 | 31 | [Benchmark]
|
31 | 32 | [ArgumentsSource(nameof(Polylines))]
|
32 |
| - public void Decode_V2_Parallel((int, char[]) arg) => Parallel.For(0, 100, (i) => V2.Decode(arg.Item2).Consume(_consumer)); |
| 33 | + public void Decode_V1_Parallel((int, string) arg) => Parallel.For(0, 1001, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (i) => V1.Decode(arg.Item2).Consume(_consumer)); |
| 34 | + |
| 35 | + [Benchmark] |
| 36 | + [ArgumentsSource(nameof(Polylines))] |
| 37 | + public void Decode_V2_Parallel((int, string) arg) => Parallel.For(0, 1001, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (i) => V2.Decode(arg.Item2).Consume(_consumer)); |
| 38 | + |
| 39 | + [Benchmark] |
| 40 | + [ArgumentsSource(nameof(Polylines))] |
| 41 | + public void Decode_V3_Parallel((int, string) arg) => Parallel.For(0, 1001, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (i) => V3.Decode(arg.Item2).Consume(_consumer)); |
| 42 | + |
33 | 43 |
|
34 | 44 | private class V1
|
35 | 45 | {
|
36 |
| - public static IEnumerable<(double Latitude, double Longitude)> Decode(char[] polyline) |
| 46 | + public static IEnumerable<(double Latitude, double Longitude)> Decode(string polyline) |
37 | 47 | {
|
38 | 48 | if (polyline is null || polyline.Length == 0)
|
39 | 49 | {
|
@@ -71,7 +81,7 @@ private class V1
|
71 | 81 | return result;
|
72 | 82 | }
|
73 | 83 |
|
74 |
| - private static bool TryCalculateNext(ref char[] polyline, ref int index, ref int value) |
| 84 | + private static bool TryCalculateNext(ref string polyline, ref int index, ref int value) |
75 | 85 | {
|
76 | 86 | int chunk;
|
77 | 87 | int sum = 0;
|
@@ -118,7 +128,7 @@ public static bool IsValidLongitude(double longitude)
|
118 | 128 |
|
119 | 129 | private class V2
|
120 | 130 | {
|
121 |
| - public static IEnumerable<(double Latitude, double Longitude)> Decode(char[] polyline) |
| 131 | + public static IEnumerable<(double Latitude, double Longitude)> Decode(string polyline) |
122 | 132 | {
|
123 | 133 | if (polyline is null || polyline.Length == 0)
|
124 | 134 | {
|
@@ -152,7 +162,7 @@ private class V2
|
152 | 162 | }
|
153 | 163 | }
|
154 | 164 |
|
155 |
| - private static bool TryCalculateNext(ref char[] polyline, ref int offset, ref int value) |
| 165 | + private static bool TryCalculateNext(ref string polyline, ref int offset, ref int value) |
156 | 166 | {
|
157 | 167 | int chunk;
|
158 | 168 | int sum = 0;
|
@@ -196,5 +206,90 @@ public static bool IsValidLongitude(double longitude)
|
196 | 206 | }
|
197 | 207 | }
|
198 | 208 | }
|
| 209 | + |
| 210 | + private class V3 |
| 211 | + { |
| 212 | + public static IEnumerable<(double Latitude, double Longitude)> Decode(string polyline) |
| 213 | + { |
| 214 | + // Checking null and at least one character |
| 215 | + if (polyline == null || polyline.Length == 0) |
| 216 | + { |
| 217 | + throw new ArgumentException(String.Empty, nameof(polyline)); |
| 218 | + } |
| 219 | + |
| 220 | + // Initialize local variables |
| 221 | + int index = 0; |
| 222 | + int latitude = 0; |
| 223 | + int longitude = 0; |
| 224 | + |
| 225 | + // Looping through encoded polyline char array |
| 226 | + while (index < polyline.Length) |
| 227 | + { |
| 228 | + // Attempting to calculate next latitude value. If failed exception is thrown |
| 229 | + if (!TryCalculateNext(polyline, ref index, ref latitude)) |
| 230 | + { |
| 231 | + throw new InvalidOperationException(String.Empty); |
| 232 | + } |
| 233 | + |
| 234 | + // Attempting to calculate next longitude value. If failed exception is thrown |
| 235 | + if (!TryCalculateNext(polyline, ref index, ref longitude)) |
| 236 | + { |
| 237 | + throw new InvalidOperationException(String.Empty); |
| 238 | + } |
| 239 | + |
| 240 | + var coordinate = (GetCoordinate(latitude), GetCoordinate(longitude)); |
| 241 | + |
| 242 | + // Validating decoded coordinate. If not valid exception is thrown |
| 243 | + if (!CoordinateValidator.IsValid(coordinate)) |
| 244 | + { |
| 245 | + throw new InvalidOperationException(String.Empty); |
| 246 | + } |
| 247 | + |
| 248 | + yield return coordinate; |
| 249 | + |
| 250 | + #region Local functions |
| 251 | + |
| 252 | + bool TryCalculateNext(string polyline, ref int index, ref int value) |
| 253 | + { |
| 254 | + // Local variable initialization |
| 255 | + int chunk; |
| 256 | + int sum = 0; |
| 257 | + int shifter = 0; |
| 258 | + |
| 259 | + do |
| 260 | + { |
| 261 | + chunk = polyline[index++] - Constants.ASCII.QuestionMark; |
| 262 | + sum |= (chunk & Constants.ASCII.UnitSeparator) << shifter; |
| 263 | + shifter += Constants.ShiftLength; |
| 264 | + } while (chunk >= Constants.ASCII.Space && index < polyline.Length); |
| 265 | + |
| 266 | + if (index >= polyline.Length && chunk >= Constants.ASCII.Space) |
| 267 | + return false; |
| 268 | + |
| 269 | + value += (sum & 1) == 1 ? ~(sum >> 1) : sum >> 1; |
| 270 | + |
| 271 | + return true; |
| 272 | + } |
| 273 | + |
| 274 | + double GetCoordinate(int value) |
| 275 | + { |
| 276 | + return Convert.ToDouble(value) / Constants.Precision; |
| 277 | + } |
| 278 | + |
| 279 | + #endregion |
| 280 | + } |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + internal class For |
| 285 | + { |
| 286 | + public static void Loop(int count, Action action) |
| 287 | + { |
| 288 | + for (int i = 0; i < count; i++) |
| 289 | + { |
| 290 | + action.Invoke(); |
| 291 | + } |
| 292 | + } |
| 293 | + } |
199 | 294 | }
|
200 | 295 | }
|
0 commit comments