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