Added Grisu3 algorithm support for double.ToString(). by mazong1123 · Pull Request #14646 · dotnet/coreclr

Overview:

Grisu3 is much faster (some numbers become almost 9 times faster, for instance, -1.79769313486232E+308's runtime change: 237.492 -> 28.660 ) than Dragon4. But when a number fails in Grisu3, we have to switch to Dragon4, obviously it is slower than the sole Dragon4 implementation in this case. For instance, comparing the test result of perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 250, innerIterations: 2000000). Because 250 in 17 digits precision fails in Grisu3, it becomes much slower in the new implementation.

Following are the benchmark:

Average runtime comparison:

Test Name Metric Before After
perftest.DoubleToStringTest.Decimal_ToString Duration 762.895 734.575
perftest.DoubleToStringTest.DefaultToString(number: -1.79769313486232E+308, innerIterations: 100000) Duration 237.492 28.660
perftest.DoubleToStringTest.DefaultToString(number: -8.98846567431158E+307, innerIterations: 100000) Duration 227.782 29.921
perftest.DoubleToStringTest.DefaultToString(number: ∞, innerIterations: 10000000) Duration 732.380 690.268
perftest.DoubleToStringTest.DefaultToString(number: 1.79769313486232E+308, innerIterations: 100000) Duration 245.580 30.721
perftest.DoubleToStringTest.DefaultToString(number: 104234.343, innerIterations: 1000000) Duration 620.308 298.880
perftest.DoubleToStringTest.DefaultToString(number: 2.2250738585072E-308, innerIterations: 100000) Duration 226.605 38.423
perftest.DoubleToStringTest.DefaultToString(number: NaN, innerIterations: 10000000) Duration 681.121 688.780
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: -1.79769313486232E+308, innerIterations: 100000) Duration 252.797 26.215
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: ∞, innerIterations: 20000000) Duration 1130.165 1072.641
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 0, innerIterations: 4000000) Duration 519.740 519.731
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 1.79769313486232E+308, innerIterations: 100000) Duration 239.098 27.831
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 104234.343, innerIterations: 1000000) Duration 626.809 269.358
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: NaN, innerIterations: 20000000) Duration 1042.436 1129.455
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: -1.79769313486232E+308, innerIterations: 100000) Duration 241.084 30.417
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 0, innerIterations: 2000000) Duration 332.165 316.559
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 1.79769313486232E+308, innerIterations: 100000) Duration 217.656 28.694
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 250, innerIterations: 2000000) Duration 706.236 813.597
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 4.94065645841247E-324, innerIterations: 100000) Duration 222.350 40.334
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: -1.79769313486232E+308, innerIterations: 100000) Duration 324.054 132.538
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 0, innerIterations: 2000000) Duration 535.282 538.055
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 1.79769313486232E+308, innerIterations: 100000) Duration 324.008 132.549
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 250, innerIterations: 2000000) Duration 862.649 1052.656
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 4.94065645841247E-324, innerIterations: 100000) Duration 220.087 49.086
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: -1.79769313486232E+308, innerIterations: 100000) Duration 218.129 30.110
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: ∞, innerIterations: 20000000) Duration 1373.222 1383.536
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 0, innerIterations: 2000000) Duration 293.404 278.052
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 1.79769313486232E+308, innerIterations: 100000) Duration 235.778 28.887
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 250, innerIterations: 2000000) Duration 733.715 746.424
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 4.94065645841247E-324, innerIterations: 100000) Duration 213.085 39.974
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: NaN, innerIterations: 20000000) Duration 1356.486 1285.336
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: -1.79769313486232E+308, innerIterations: 100000) Duration 239.188 29.196
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 0, innerIterations: 2000000) Duration 333.325 324.171
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 1.79769313486232E+308, innerIterations: 100000) Duration 239.025 29.986
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 250, innerIterations: 2000000) Duration 776.125 881.487
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 4.94065645841247E-324, innerIterations: 100000) Duration 233.128 41.974
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: -1.79769313486232E+308, innerIterations: 100000) Duration 443.718 45.578
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 0, innerIterations: 2000000) Duration 306.752 283.209
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 1.79769313486232E+308, innerIterations: 100000) Duration 506.462 46.712
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 250, innerIterations: 2000000) Duration 821.277 856.164
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 4.94065645841247E-324, innerIterations: 100000) Duration 231.865 49.403

Following are the details of each performance test result:

Before change (With only Dragon4):

Test Name Metric Iterations AVERAGE STDEV.S MIN MAX
perftest.DoubleToStringTest.Decimal_ToString Duration 14 762.895 86.520 682.385 882.800
perftest.DoubleToStringTest.DefaultToString(number: -1.79769313486232E+308, innerIterations: 100000) Duration 43 237.492 27.334 214.093 283.049
perftest.DoubleToStringTest.DefaultToString(number: -8.98846567431158E+307, innerIterations: 100000) Duration 44 227.782 25.729 197.547 261.339
perftest.DoubleToStringTest.DefaultToString(number: ∞, innerIterations: 10000000) Duration 14 732.380 98.418 622.394 882.606
perftest.DoubleToStringTest.DefaultToString(number: 1.79769313486232E+308, innerIterations: 100000) Duration 41 245.580 28.099 214.045 281.792
perftest.DoubleToStringTest.DefaultToString(number: 104234.343, innerIterations: 1000000) Duration 17 620.308 2.119 616.989 624.958
perftest.DoubleToStringTest.DefaultToString(number: 2.2250738585072E-308, innerIterations: 100000) Duration 45 226.605 26.536 203.087 270.209
perftest.DoubleToStringTest.DefaultToString(number: NaN, innerIterations: 10000000) Duration 15 681.121 76.504 595.638 783.120
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: -1.79769313486232E+308, innerIterations: 100000) Duration 40 252.797 31.236 215.753 299.873
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: ∞, innerIterations: 20000000) Duration 9 1130.165 98.503 985.048 1247.706
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 0, innerIterations: 4000000) Duration 20 519.740 58.589 455.855 596.709
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 1.79769313486232E+308, innerIterations: 100000) Duration 43 239.098 27.827 214.150 282.854
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 104234.343, innerIterations: 1000000) Duration 17 626.809 47.565 600.915 773.405
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: NaN, innerIterations: 20000000) Duration 10 1042.436 128.899 905.122 1227.495
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: -1.79769313486232E+308, innerIterations: 100000) Duration 42 241.084 28.454 216.058 289.477
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 0, innerIterations: 2000000) Duration 31 332.165 1.843 329.610 338.415
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 1.79769313486232E+308, innerIterations: 100000) Duration 46 217.656 1.671 214.852 221.147
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 250, innerIterations: 2000000) Duration 15 706.236 21.612 693.022 773.103
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 4.94065645841247E-324, innerIterations: 100000) Duration 45 222.350 18.677 209.732 263.866
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: -1.79769313486232E+308, innerIterations: 100000) Duration 31 324.054 1.322 322.386 327.643
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 0, innerIterations: 2000000) Duration 19 535.282 7.039 531.923 563.779
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 1.79769313486232E+308, innerIterations: 100000) Duration 31 324.008 2.466 322.167 335.947
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 250, innerIterations: 2000000) Duration 12 862.649 1.081 861.432 864.766
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 4.94065645841247E-324, innerIterations: 100000) Duration 46 220.087 1.112 218.273 223.328
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: -1.79769313486232E+308, innerIterations: 100000) Duration 46 218.129 1.078 215.268 221.593
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: ∞, innerIterations: 20000000) Duration 8 1373.222 5.147 1367.093 1380.559
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 0, innerIterations: 2000000) Duration 35 293.404 8.743 289.282 336.534
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 1.79769313486232E+308, innerIterations: 100000) Duration 43 235.778 24.380 214.380 271.813
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 250, innerIterations: 2000000) Duration 14 733.715 4.734 727.828 744.614
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 4.94065645841247E-324, innerIterations: 100000) Duration 47 213.085 4.213 210.354 240.162
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: NaN, innerIterations: 20000000) Duration 8 1356.486 4.812 1352.167 1366.296
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: -1.79769313486232E+308, innerIterations: 100000) Duration 42 239.188 1.206 237.702 244.364
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 0, innerIterations: 2000000) Duration 31 333.325 37.702 291.221 384.541
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 1.79769313486232E+308, innerIterations: 100000) Duration 42 239.025 1.668 237.056 246.427
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 250, innerIterations: 2000000) Duration 13 776.125 25.877 756.554 847.363
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 4.94065645841247E-324, innerIterations: 100000) Duration 43 233.128 1.049 231.429 236.670
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: -1.79769313486232E+308, innerIterations: 100000) Duration 23 443.718 2.183 441.390 450.867
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 0, innerIterations: 2000000) Duration 33 306.752 9.509 301.374 347.536
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 1.79769313486232E+308, innerIterations: 100000) Duration 20 506.462 55.926 441.193 575.617
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 250, innerIterations: 2000000) Duration 13 821.277 2.140 816.872 824.743
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 4.94065645841247E-324, innerIterations: 100000) Duration 44 231.865 19.151 220.482 276.559

After change (Grisu3 + Dragon4):

Test Name Metric Iterations AVERAGE STDEV.S MIN MAX
perftest.DoubleToStringTest.Decimal_ToString Duration 14 734.575 1.725 731.841 737.709
perftest.DoubleToStringTest.DefaultToString(number: -1.79769313486232E+308, innerIterations: 100000) Duration 349 28.660 2.491 26.927 35.211
perftest.DoubleToStringTest.DefaultToString(number: -8.98846567431158E+307, innerIterations: 100000) Duration 335 29.921 3.489 26.307 36.381
perftest.DoubleToStringTest.DefaultToString(number: ∞, innerIterations: 10000000) Duration 15 690.268 76.088 583.543 783.951
perftest.DoubleToStringTest.DefaultToString(number: 1.79769313486232E+308, innerIterations: 100000) Duration 326 30.721 3.561 26.674 37.145
perftest.DoubleToStringTest.DefaultToString(number: 104234.343, innerIterations: 1000000) Duration 34 298.880 32.274 275.273 360.823
perftest.DoubleToStringTest.DefaultToString(number: 2.2250738585072E-308, innerIterations: 100000) Duration 261 38.423 4.520 35.706 51.440
perftest.DoubleToStringTest.DefaultToString(number: NaN, innerIterations: 10000000) Duration 15 688.780 83.619 590.821 815.194
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: -1.79769313486232E+308, innerIterations: 100000) Duration 382 26.215 0.492 25.769 31.469
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: ∞, innerIterations: 20000000) Duration 10 1072.641 119.324 903.355 1224.387
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 0, innerIterations: 4000000) Duration 20 519.731 56.467 461.462 605.618
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 1.79769313486232E+308, innerIterations: 100000) Duration 360 27.831 2.824 25.603 34.895
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: 104234.343, innerIterations: 1000000) Duration 38 269.358 15.466 261.895 325.118
perftest.DoubleToStringTest.ToStringWithCultureInfo(cultureName: "zh", number: NaN, innerIterations: 20000000) Duration 9 1129.455 126.770 917.095 1257.702
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: -1.79769313486232E+308, innerIterations: 100000) Duration 329 30.417 3.810 26.543 37.772
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 0, innerIterations: 2000000) Duration 32 316.559 4.442 312.930 330.704
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 1.79769313486232E+308, innerIterations: 100000) Duration 349 28.694 3.437 26.043 48.303
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 250, innerIterations: 2000000) Duration 13 813.597 81.900 733.799 943.832
perftest.DoubleToStringTest.ToStringWithFormat(format: "E", number: 4.94065645841247E-324, innerIterations: 100000) Duration 248 40.334 3.457 38.120 49.285
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: -1.79769313486232E+308, innerIterations: 100000) Duration 76 132.538 0.801 131.197 134.821
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 0, innerIterations: 2000000) Duration 19 538.055 9.700 533.715 577.721
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 1.79769313486232E+308, innerIterations: 100000) Duration 76 132.549 0.862 131.257 135.052
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 250, innerIterations: 2000000) Duration 10 1052.656 106.536 987.218 1251.984
perftest.DoubleToStringTest.ToStringWithFormat(format: "F50", number: 4.94065645841247E-324, innerIterations: 100000) Duration 204 49.086 0.546 48.530 52.471
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: -1.79769313486232E+308, innerIterations: 100000) Duration 332 30.110 3.206 27.742 37.711
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: ∞, innerIterations: 20000000) Duration 8 1383.536 142.176 1270.398 1633.423
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 0, innerIterations: 2000000) Duration 37 278.052 19.509 270.044 338.078
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 1.79769313486232E+308, innerIterations: 100000) Duration 347 28.887 2.463 27.277 36.683
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 250, innerIterations: 2000000) Duration 14 746.424 2.525 743.420 753.535
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: 4.94065645841247E-324, innerIterations: 100000) Duration 251 39.974 0.662 39.270 43.060
perftest.DoubleToStringTest.ToStringWithFormat(format: "G", number: NaN, innerIterations: 20000000) Duration 8 1285.336 2.808 1282.338 1291.024
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: -1.79769313486232E+308, innerIterations: 100000) Duration 343 29.196 0.609 28.425 33.425
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 0, innerIterations: 2000000) Duration 31 324.171 37.880 273.118 376.826
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 1.79769313486232E+308, innerIterations: 100000) Duration 334 29.986 2.936 28.026 38.707
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 250, innerIterations: 2000000) Duration 12 881.487 116.209 761.810 1086.569
perftest.DoubleToStringTest.ToStringWithFormat(format: "G17", number: 4.94065645841247E-324, innerIterations: 100000) Duration 239 41.974 3.493 39.770 51.080
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: -1.79769313486232E+308, innerIterations: 100000) Duration 220 45.578 0.543 44.847 48.269
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 0, innerIterations: 2000000) Duration 36 283.209 1.340 281.582 287.001
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 1.79769313486232E+308, innerIterations: 100000) Duration 215 46.712 4.459 43.934 58.510
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 250, innerIterations: 2000000) Duration 12 856.164 26.094 841.487 937.493
perftest.DoubleToStringTest.ToStringWithFormat(format: "R", number: 4.94065645841247E-324, innerIterations: 100000) Duration 203 49.403 0.728 48.735 55.378

Above results were generated by the code: https://github.com/dotnet/corefx/blob/master/src/System.Runtime/tests/Performance/Perf.Double.cs