Skip to content

Commit b2fc73b

Browse files
authored
Merge pull request #96 from xin9le/perf/ToNumberString
Improve `.ToNumberString()` performance
2 parents 23f5a91 + dd885fc commit b2fc73b

6 files changed

Lines changed: 147 additions & 45 deletions

File tree

src/insights/FastEnum.UnitTests/Cases/Generators/BasicTests.cs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,12 @@ public void TryParseIgnoreCase()
196196
[TestMethod]
197197
public void FastToString()
198198
{
199-
const SByteEnum undefined = (SByteEnum)123;
200-
var values = Enum.GetValues<SByteEnum>().Append(undefined);
199+
const SByteEnum undefined1 = (SByteEnum)123;
200+
const SByteEnum undefined2 = (SByteEnum)(-123);
201+
var values
202+
= Enum.GetValues<SByteEnum>()
203+
.Append(undefined1)
204+
.Append(undefined2);
201205
foreach (var x in values)
202206
{
203207
var expect = x.ToString();
@@ -384,8 +388,8 @@ public void TryParseIgnoreCase()
384388
[TestMethod]
385389
public void FastToString()
386390
{
387-
const ByteEnum undefined = (ByteEnum)123;
388-
var values = Enum.GetValues<ByteEnum>().Append(undefined);
391+
const ByteEnum undefined1 = (ByteEnum)123;
392+
var values = Enum.GetValues<ByteEnum>().Append(undefined1);
389393
foreach (var x in values)
390394
{
391395
var expect = x.ToString();
@@ -578,8 +582,12 @@ public void TryParseIgnoreCase()
578582
[TestMethod]
579583
public void FastToString()
580584
{
581-
const Int16Enum undefined = (Int16Enum)123;
582-
var values = Enum.GetValues<Int16Enum>().Append(undefined);
585+
const Int16Enum undefined1 = (Int16Enum)123;
586+
const Int16Enum undefined2 = (Int16Enum)(-123);
587+
var values
588+
= Enum.GetValues<Int16Enum>()
589+
.Append(undefined1)
590+
.Append(undefined2);
583591
foreach (var x in values)
584592
{
585593
var expect = x.ToString();
@@ -766,8 +774,8 @@ public void TryParseIgnoreCase()
766774
[TestMethod]
767775
public void FastToString()
768776
{
769-
const UInt16Enum undefined = (UInt16Enum)123;
770-
var values = Enum.GetValues<UInt16Enum>().Append(undefined);
777+
const UInt16Enum undefined1 = (UInt16Enum)123;
778+
var values = Enum.GetValues<UInt16Enum>().Append(undefined1);
771779
foreach (var x in values)
772780
{
773781
var expect = x.ToString();
@@ -960,8 +968,12 @@ public void TryParseIgnoreCase()
960968
[TestMethod]
961969
public void FastToString()
962970
{
963-
const Int32Enum undefined = (Int32Enum)123;
964-
var values = Enum.GetValues<Int32Enum>().Append(undefined);
971+
const Int32Enum undefined1 = (Int32Enum)123;
972+
const Int32Enum undefined2 = (Int32Enum)(-123);
973+
var values
974+
= Enum.GetValues<Int32Enum>()
975+
.Append(undefined1)
976+
.Append(undefined2);
965977
foreach (var x in values)
966978
{
967979
var expect = x.ToString();
@@ -1148,8 +1160,8 @@ public void TryParseIgnoreCase()
11481160
[TestMethod]
11491161
public void FastToString()
11501162
{
1151-
const UInt32Enum undefined = (UInt32Enum)123;
1152-
var values = Enum.GetValues<UInt32Enum>().Append(undefined);
1163+
const UInt32Enum undefined1 = (UInt32Enum)123;
1164+
var values = Enum.GetValues<UInt32Enum>().Append(undefined1);
11531165
foreach (var x in values)
11541166
{
11551167
var expect = x.ToString();
@@ -1342,8 +1354,12 @@ public void TryParseIgnoreCase()
13421354
[TestMethod]
13431355
public void FastToString()
13441356
{
1345-
const Int64Enum undefined = (Int64Enum)123;
1346-
var values = Enum.GetValues<Int64Enum>().Append(undefined);
1357+
const Int64Enum undefined1 = (Int64Enum)123;
1358+
const Int64Enum undefined2 = (Int64Enum)(-123);
1359+
var values
1360+
= Enum.GetValues<Int64Enum>()
1361+
.Append(undefined1)
1362+
.Append(undefined2);
13471363
foreach (var x in values)
13481364
{
13491365
var expect = x.ToString();
@@ -1530,8 +1546,8 @@ public void TryParseIgnoreCase()
15301546
[TestMethod]
15311547
public void FastToString()
15321548
{
1533-
const UInt64Enum undefined = (UInt64Enum)123;
1534-
var values = Enum.GetValues<UInt64Enum>().Append(undefined);
1549+
const UInt64Enum undefined1 = (UInt64Enum)123;
1550+
var values = Enum.GetValues<UInt64Enum>().Append(undefined1);
15351551
foreach (var x in values)
15361552
{
15371553
var expect = x.ToString();

src/insights/FastEnum.UnitTests/Cases/Generators/BasicTests.tt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,16 @@ public sealed class Basic<#= x.AliasType #>Tests
227227
[TestMethod]
228228
public void FastToString()
229229
{
230-
const <#= x.EnumType #> undefined = (<#= x.EnumType #>)123;
231-
var values = Enum.GetValues<<#= x.EnumType #>>().Append(undefined);
230+
const <#= x.EnumType #> undefined1 = (<#= x.EnumType #>)123;
231+
<# if (x.IsSignedType) { #>
232+
const <#= x.EnumType #> undefined2 = (<#= x.EnumType #>)(-123);
233+
var values
234+
= Enum.GetValues<<#= x.EnumType #>>()
235+
.Append(undefined1)
236+
.Append(undefined2);
237+
<# } else { #>
238+
var values = Enum.GetValues<<#= x.EnumType #>>().Append(undefined1);
239+
<# } #>
232240
foreach (var x in values)
233241
{
234242
var expect = x.ToString();

src/insights/FastEnum.UnitTests/Cases/Reflections/BasicTests.cs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,12 @@ public void ToUnderlying()
372372
[TestMethod]
373373
public void FastToString()
374374
{
375-
const SByteEnum undefined = (SByteEnum)123;
376-
var values = Enum.GetValues<SByteEnum>().Append(undefined);
375+
const SByteEnum undefined1 = (SByteEnum)123;
376+
const SByteEnum undefined2 = (SByteEnum)(-123);
377+
var values
378+
= Enum.GetValues<SByteEnum>()
379+
.Append(undefined1)
380+
.Append(undefined2);
377381
foreach (var x in values)
378382
{
379383
var expect = x.ToString();
@@ -733,8 +737,8 @@ public void ToUnderlying()
733737
[TestMethod]
734738
public void FastToString()
735739
{
736-
const ByteEnum undefined = (ByteEnum)123;
737-
var values = Enum.GetValues<ByteEnum>().Append(undefined);
740+
const ByteEnum undefined1 = (ByteEnum)123;
741+
var values = Enum.GetValues<ByteEnum>().Append(undefined1);
738742
foreach (var x in values)
739743
{
740744
var expect = x.ToString();
@@ -1102,8 +1106,12 @@ public void ToUnderlying()
11021106
[TestMethod]
11031107
public void FastToString()
11041108
{
1105-
const Int16Enum undefined = (Int16Enum)123;
1106-
var values = Enum.GetValues<Int16Enum>().Append(undefined);
1109+
const Int16Enum undefined1 = (Int16Enum)123;
1110+
const Int16Enum undefined2 = (Int16Enum)(-123);
1111+
var values
1112+
= Enum.GetValues<Int16Enum>()
1113+
.Append(undefined1)
1114+
.Append(undefined2);
11071115
foreach (var x in values)
11081116
{
11091117
var expect = x.ToString();
@@ -1463,8 +1471,8 @@ public void ToUnderlying()
14631471
[TestMethod]
14641472
public void FastToString()
14651473
{
1466-
const UInt16Enum undefined = (UInt16Enum)123;
1467-
var values = Enum.GetValues<UInt16Enum>().Append(undefined);
1474+
const UInt16Enum undefined1 = (UInt16Enum)123;
1475+
var values = Enum.GetValues<UInt16Enum>().Append(undefined1);
14681476
foreach (var x in values)
14691477
{
14701478
var expect = x.ToString();
@@ -1832,8 +1840,12 @@ public void ToUnderlying()
18321840
[TestMethod]
18331841
public void FastToString()
18341842
{
1835-
const Int32Enum undefined = (Int32Enum)123;
1836-
var values = Enum.GetValues<Int32Enum>().Append(undefined);
1843+
const Int32Enum undefined1 = (Int32Enum)123;
1844+
const Int32Enum undefined2 = (Int32Enum)(-123);
1845+
var values
1846+
= Enum.GetValues<Int32Enum>()
1847+
.Append(undefined1)
1848+
.Append(undefined2);
18371849
foreach (var x in values)
18381850
{
18391851
var expect = x.ToString();
@@ -2193,8 +2205,8 @@ public void ToUnderlying()
21932205
[TestMethod]
21942206
public void FastToString()
21952207
{
2196-
const UInt32Enum undefined = (UInt32Enum)123;
2197-
var values = Enum.GetValues<UInt32Enum>().Append(undefined);
2208+
const UInt32Enum undefined1 = (UInt32Enum)123;
2209+
var values = Enum.GetValues<UInt32Enum>().Append(undefined1);
21982210
foreach (var x in values)
21992211
{
22002212
var expect = x.ToString();
@@ -2562,8 +2574,12 @@ public void ToUnderlying()
25622574
[TestMethod]
25632575
public void FastToString()
25642576
{
2565-
const Int64Enum undefined = (Int64Enum)123;
2566-
var values = Enum.GetValues<Int64Enum>().Append(undefined);
2577+
const Int64Enum undefined1 = (Int64Enum)123;
2578+
const Int64Enum undefined2 = (Int64Enum)(-123);
2579+
var values
2580+
= Enum.GetValues<Int64Enum>()
2581+
.Append(undefined1)
2582+
.Append(undefined2);
25672583
foreach (var x in values)
25682584
{
25692585
var expect = x.ToString();
@@ -2923,8 +2939,8 @@ public void ToUnderlying()
29232939
[TestMethod]
29242940
public void FastToString()
29252941
{
2926-
const UInt64Enum undefined = (UInt64Enum)123;
2927-
var values = Enum.GetValues<UInt64Enum>().Append(undefined);
2942+
const UInt64Enum undefined1 = (UInt64Enum)123;
2943+
var values = Enum.GetValues<UInt64Enum>().Append(undefined1);
29282944
foreach (var x in values)
29292945
{
29302946
var expect = x.ToString();

src/insights/FastEnum.UnitTests/Cases/Reflections/BasicTests.tt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,16 @@ public sealed class Basic<#= x.AliasType #>Tests
406406
[TestMethod]
407407
public void FastToString()
408408
{
409-
const <#= x.EnumType #> undefined = (<#= x.EnumType #>)123;
410-
var values = Enum.GetValues<<#= x.EnumType #>>().Append(undefined);
409+
const <#= x.EnumType #> undefined1 = (<#= x.EnumType #>)123;
410+
<# if (x.IsSignedType) { #>
411+
const <#= x.EnumType #> undefined2 = (<#= x.EnumType #>)(-123);
412+
var values
413+
= Enum.GetValues<<#= x.EnumType #>>()
414+
.Append(undefined1)
415+
.Append(undefined2);
416+
<# } else { #>
417+
var values = Enum.GetValues<<#= x.EnumType #>>().Append(undefined1);
418+
<# } #>
411419
foreach (var x in values)
412420
{
413421
var expect = x.ToString();

src/libs/FastEnum.Core/Internals/UnderlyingOperation.cs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,14 @@ static sbyte toNumber(T value)
191191
[MethodImpl(MethodImplOptions.AggressiveInlining)]
192192
public static string ToNumberString(T value)
193193
{
194+
// note:
195+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
196+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
197+
194198
var x = Unsafe.BitCast<T, sbyte>(value);
195-
return x.ToString(null, CultureInfo.InvariantCulture);
199+
#pragma warning disable CA1305
200+
return x.ToString();
201+
#pragma warning restore CA1305
196202
}
197203

198204

@@ -289,8 +295,14 @@ static byte toNumber(T value)
289295
[MethodImpl(MethodImplOptions.AggressiveInlining)]
290296
public static string ToNumberString(T value)
291297
{
298+
// note:
299+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
300+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
301+
292302
var x = Unsafe.BitCast<T, byte>(value);
293-
return x.ToString(null, CultureInfo.InvariantCulture);
303+
#pragma warning disable CA1305
304+
return x.ToString();
305+
#pragma warning restore CA1305
294306
}
295307

296308

@@ -387,8 +399,14 @@ static short toNumber(T value)
387399
[MethodImpl(MethodImplOptions.AggressiveInlining)]
388400
public static string ToNumberString(T value)
389401
{
402+
// note:
403+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
404+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
405+
390406
var x = Unsafe.BitCast<T, short>(value);
391-
return x.ToString(null, CultureInfo.InvariantCulture);
407+
#pragma warning disable CA1305
408+
return x.ToString();
409+
#pragma warning restore CA1305
392410
}
393411

394412

@@ -485,8 +503,14 @@ static ushort toNumber(T value)
485503
[MethodImpl(MethodImplOptions.AggressiveInlining)]
486504
public static string ToNumberString(T value)
487505
{
506+
// note:
507+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
508+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
509+
488510
var x = Unsafe.BitCast<T, ushort>(value);
489-
return x.ToString(null, CultureInfo.InvariantCulture);
511+
#pragma warning disable CA1305
512+
return x.ToString();
513+
#pragma warning restore CA1305
490514
}
491515

492516

@@ -583,8 +607,14 @@ static int toNumber(T value)
583607
[MethodImpl(MethodImplOptions.AggressiveInlining)]
584608
public static string ToNumberString(T value)
585609
{
610+
// note:
611+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
612+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
613+
586614
var x = Unsafe.BitCast<T, int>(value);
587-
return x.ToString(null, CultureInfo.InvariantCulture);
615+
#pragma warning disable CA1305
616+
return x.ToString();
617+
#pragma warning restore CA1305
588618
}
589619

590620

@@ -681,8 +711,14 @@ static uint toNumber(T value)
681711
[MethodImpl(MethodImplOptions.AggressiveInlining)]
682712
public static string ToNumberString(T value)
683713
{
714+
// note:
715+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
716+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
717+
684718
var x = Unsafe.BitCast<T, uint>(value);
685-
return x.ToString(null, CultureInfo.InvariantCulture);
719+
#pragma warning disable CA1305
720+
return x.ToString();
721+
#pragma warning restore CA1305
686722
}
687723

688724

@@ -779,8 +815,14 @@ static long toNumber(T value)
779815
[MethodImpl(MethodImplOptions.AggressiveInlining)]
780816
public static string ToNumberString(T value)
781817
{
818+
// note:
819+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
820+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
821+
782822
var x = Unsafe.BitCast<T, long>(value);
783-
return x.ToString(null, CultureInfo.InvariantCulture);
823+
#pragma warning disable CA1305
824+
return x.ToString();
825+
#pragma warning restore CA1305
784826
}
785827

786828

@@ -877,8 +919,14 @@ static ulong toNumber(T value)
877919
[MethodImpl(MethodImplOptions.AggressiveInlining)]
878920
public static string ToNumberString(T value)
879921
{
922+
// note:
923+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
924+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
925+
880926
var x = Unsafe.BitCast<T, ulong>(value);
881-
return x.ToString(null, CultureInfo.InvariantCulture);
927+
#pragma warning disable CA1305
928+
return x.ToString();
929+
#pragma warning restore CA1305
882930
}
883931

884932

src/libs/FastEnum.Core/Internals/UnderlyingOperation.tt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,14 @@ internal static class UnderlyingOperation<T>
157157
[MethodImpl(MethodImplOptions.AggressiveInlining)]
158158
public static string ToNumberString(T value)
159159
{
160+
// note:
161+
// - ToString() without IFormatProvider is faster because it goes straight to the digit-formatting fast path and returns cached strings for small values.
162+
// - Integer formatting depends on the culture only for the negative sign, which is the same behavior as System.Enum.ToString().
163+
160164
var x = Unsafe.BitCast<T, <#= x.CompatibleName #>>(value);
161-
return x.ToString(null, CultureInfo.InvariantCulture);
165+
#pragma warning disable CA1305
166+
return x.ToString();
167+
#pragma warning restore CA1305
162168
}
163169

164170

0 commit comments

Comments
 (0)