Skip to content

Use generic math for floating point formatting #102683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 3, 2024

Conversation

huoyaoyuan
Copy link
Member

Extracted from #98643

@ghost ghost added the area-System.Numerics label May 25, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label May 25, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

@tannergooding
Copy link
Member

Can you do some performance testing on Mono? The general process for doing so is documented here: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md

CC. @SamMonoRT

@SamMonoRT
Copy link
Member

adding @steveisok and @vitek-karas for Mono scenario followup.

@steveisok
Copy link
Member

steveisok commented May 29, 2024

Can you do some performance testing on Mono? The general process for doing so is documented here: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md

@fanyang-mono can you please help with this?

@huoyaoyuan
Copy link
Member Author

@EgorBot -arm64 -intel -mono

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace BenchmarkGround
{
    public class Program
    {
        public IEnumerable<object> Values => [0.0, 1.0, 1234.5678, 1.09876543211234567890e30];

        public IEnumerable<object> Formats => ["G", "F0", "F10", "E"];

        public IEnumerable<object[]> ValueAndFormats => Values.SelectMany(v => Formats.Select(f => new object[] { v, f }));

        [Benchmark]
        [ArgumentsSource(nameof(Values))]
        public string DefaultToString(double value) => value.ToString();

        [Benchmark]
        [ArgumentsSource(nameof(ValueAndFormats))]
        public string ToStringFormat(double value, string format) => value.ToString(format);
    }
}

@EgorBot
Copy link

EgorBot commented May 30, 2024

Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 16 logical and 8 physical cores
  Job-UEIGGG : .NET 9.0.0 (42.42.42.42424) using MonoVM, X64 X86Base
  Job-ALWOTV : .NET 9.0.0 (42.42.42.42424) using MonoVM, X64 X86Base
Method Toolchain value format Mean Error Ratio
ToStringFormat Main 0 E 226.90 ns 0.921 ns 1.00
ToStringFormat PR 0 E 225.29 ns 0.690 ns 0.99
ToStringFormat Main 0 F0 128.09 ns 0.156 ns 1.00
ToStringFormat PR 0 F0 124.79 ns 0.195 ns 0.97
ToStringFormat Main 0 F10 177.88 ns 0.332 ns 1.00
ToStringFormat PR 0 F10 185.54 ns 0.418 ns 1.04
ToStringFormat Main 0 G 108.44 ns 0.102 ns 1.00
ToStringFormat PR 0 G 104.68 ns 0.126 ns 0.97
DefaultToString Main 0 ? 98.84 ns 0.434 ns 1.00
DefaultToString PR 0 ? 99.34 ns 0.121 ns 1.01
ToStringFormat Main 1 E 650.64 ns 0.610 ns 1.00
ToStringFormat PR 1 E 637.64 ns 0.816 ns 0.98
ToStringFormat Main 1 F0 446.54 ns 0.388 ns 1.00
ToStringFormat PR 1 F0 444.08 ns 0.256 ns 0.99
ToStringFormat Main 1 F10 507.16 ns 0.898 ns 1.00
ToStringFormat PR 1 F10 534.59 ns 1.173 ns 1.05
ToStringFormat Main 1 G 285.54 ns 0.299 ns 1.00
ToStringFormat PR 1 G 296.65 ns 0.443 ns 1.04
DefaultToString Main 1 ? 287.81 ns 0.387 ns 1.00
DefaultToString PR 1 ? 287.23 ns 0.211 ns 1.00
ToStringFormat Main 1234.5678 E 413.64 ns 1.110 ns 1.00
ToStringFormat PR 1234.5678 E 405.81 ns 0.469 ns 0.98
ToStringFormat Main 1234.5678 F0 612.96 ns 0.747 ns 1.00
ToStringFormat PR 1234.5678 F0 604.78 ns 0.446 ns 0.99
ToStringFormat Main 1234.5678 F10 886.58 ns 0.385 ns 1.00
ToStringFormat PR 1234.5678 F10 915.23 ns 0.973 ns 1.03
ToStringFormat Main 1234.5678 G 395.59 ns 0.486 ns 1.00
ToStringFormat PR 1234.5678 G 414.06 ns 0.583 ns 1.05
DefaultToString Main 1234.5678 ? 393.30 ns 0.892 ns 1.00
DefaultToString PR 1234.5678 ? 421.81 ns 0.678 ns 1.07
ToStringFormat Main 1.098(...)6E+30 [22] E 379.21 ns 1.033 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] E 374.62 ns 0.361 ns 0.99
ToStringFormat Main 1.098(...)6E+30 [22] F0 2,005.17 ns 0.582 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F0 2,051.34 ns 1.010 ns 1.02
ToStringFormat Main 1.098(...)6E+30 [22] F10 2,215.33 ns 1.449 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F10 2,177.53 ns 0.712 ns 0.98
ToStringFormat Main 1.098(...)6E+30 [22] G 525.76 ns 0.795 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] G 527.60 ns 0.502 ns 1.00
DefaultToString Main 1.098(...)6E+30 [22] ? 540.69 ns 0.365 ns 1.00
DefaultToString PR 1.098(...)6E+30 [22] ? 545.84 ns 1.387 ns 1.01

BDN_Artifacts.zip

@EgorBot
Copy link

EgorBot commented May 30, 2024

Benchmark results on Arm64
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
  Job-MOGBOV : .NET 9.0.0 (42.42.42.42424) using MonoVM, Arm64 ArmBase
  Job-TLGOAU : .NET 9.0.0 (42.42.42.42424) using MonoVM, Arm64 ArmBase
Method Toolchain value format Mean Error Ratio
ToStringFormat Main 0 E 411.9 ns 0.84 ns 1.00
ToStringFormat PR 0 E 407.3 ns 0.71 ns 0.99
ToStringFormat Main 0 F0 239.0 ns 0.19 ns 1.00
ToStringFormat PR 0 F0 236.3 ns 0.20 ns 0.99
ToStringFormat Main 0 F10 360.0 ns 0.30 ns 1.00
ToStringFormat PR 0 F10 373.4 ns 0.29 ns 1.04
ToStringFormat Main 0 G 196.7 ns 0.24 ns 1.00
ToStringFormat PR 0 G 191.8 ns 0.27 ns 0.97
DefaultToString Main 0 ? 179.5 ns 0.71 ns 1.00
DefaultToString PR 0 ? 180.7 ns 0.21 ns 1.01
ToStringFormat Main 1 E 1,100.3 ns 1.91 ns 1.00
ToStringFormat PR 1 E 1,096.5 ns 2.15 ns 1.00
ToStringFormat Main 1 F0 789.5 ns 2.75 ns 1.00
ToStringFormat PR 1 F0 787.5 ns 1.53 ns 1.00
ToStringFormat Main 1 F10 897.8 ns 1.34 ns 1.00
ToStringFormat PR 1 F10 915.2 ns 1.33 ns 1.02
ToStringFormat Main 1 G 502.9 ns 0.48 ns 1.00
ToStringFormat PR 1 G 528.1 ns 0.46 ns 1.05
DefaultToString Main 1 ? 487.4 ns 0.53 ns 1.00
DefaultToString PR 1 ? 496.6 ns 0.43 ns 1.02
ToStringFormat Main 1234.5678 E 658.4 ns 2.42 ns 1.00
ToStringFormat PR 1234.5678 E 687.5 ns 0.91 ns 1.04
ToStringFormat Main 1234.5678 F0 1,027.5 ns 3.39 ns 1.00
ToStringFormat PR 1234.5678 F0 1,042.5 ns 3.28 ns 1.01
ToStringFormat Main 1234.5678 F10 1,454.8 ns 1.09 ns 1.00
ToStringFormat PR 1234.5678 F10 1,474.2 ns 2.98 ns 1.01
ToStringFormat Main 1234.5678 G 655.8 ns 0.60 ns 1.00
ToStringFormat PR 1234.5678 G 702.7 ns 1.65 ns 1.07
DefaultToString Main 1234.5678 ? 661.3 ns 0.90 ns 1.00
DefaultToString PR 1234.5678 ? 665.1 ns 1.21 ns 1.01
ToStringFormat Main 1.098(...)6E+30 [22] E 634.6 ns 0.54 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] E 648.8 ns 0.95 ns 1.02
ToStringFormat Main 1.098(...)6E+30 [22] F0 3,159.8 ns 1.75 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F0 3,170.7 ns 2.16 ns 1.00
ToStringFormat Main 1.098(...)6E+30 [22] F10 3,478.2 ns 2.68 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F10 3,481.7 ns 2.03 ns 1.00
ToStringFormat Main 1.098(...)6E+30 [22] G 849.2 ns 2.74 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] G 888.5 ns 1.70 ns 1.05
DefaultToString Main 1.098(...)6E+30 [22] ? 849.9 ns 1.25 ns 1.00
DefaultToString PR 1.098(...)6E+30 [22] ? 870.9 ns 0.92 ns 1.02

BDN_Artifacts.zip

@EgorBo
Copy link
Member

EgorBo commented May 30, 2024

@EgorBot -intel -mono --envvars MONO_ENV_OPTIONS:"--interpreter"

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace BenchmarkGround
{
    public class Program
    {
        public IEnumerable<object> Values => [0.0, 1.0, 1234.5678, 1.09876543211234567890e30];

        public IEnumerable<object> Formats => ["G", "F0", "F10", "E"];

        public IEnumerable<object[]> ValueAndFormats => Values.SelectMany(v => Formats.Select(f => new object[] { v, f }));

        [Benchmark]
        [ArgumentsSource(nameof(Values))]
        public string DefaultToString(double value) => value.ToString();

        [Benchmark]
        [ArgumentsSource(nameof(ValueAndFormats))]
        public string ToStringFormat(double value, string format) => value.ToString(format);
    }
}

@EgorBot
Copy link

EgorBot commented May 30, 2024

Benchmark results on Intel
BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 16 logical and 8 physical cores
  Job-DKBEJC : .NET 9.0.0 (42.42.42.42424) using MonoVM, X64 AOT
  Job-XMTINK : .NET 9.0.0 (42.42.42.42424) using MonoVM, X64 AOT
EnvironmentVariables=MONO_ENV_OPTIONS=--interpreter
Method Toolchain value format Mean Error Ratio
ToStringFormat Main 0 E 814.4 ns 5.47 ns 1.00
ToStringFormat PR 0 E 824.9 ns 4.90 ns 1.01
ToStringFormat Main 0 F0 471.6 ns 0.49 ns 1.00
ToStringFormat PR 0 F0 478.8 ns 0.42 ns 1.02
ToStringFormat Main 0 F10 700.3 ns 1.23 ns 1.00
ToStringFormat PR 0 F10 728.0 ns 1.31 ns 1.04
ToStringFormat Main 0 G 444.2 ns 0.98 ns 1.00
ToStringFormat PR 0 G 465.2 ns 0.78 ns 1.05
DefaultToString Main 0 ? 432.1 ns 0.44 ns 1.00
DefaultToString PR 0 ? 432.9 ns 0.44 ns 1.00
ToStringFormat Main 1 E 1,677.3 ns 9.97 ns 1.00
ToStringFormat PR 1 E 1,624.5 ns 5.47 ns 0.97
ToStringFormat Main 1 F0 980.4 ns 1.71 ns 1.00
ToStringFormat PR 1 F0 980.7 ns 0.32 ns 1.00
ToStringFormat Main 1 F10 1,230.3 ns 1.37 ns 1.00
ToStringFormat PR 1 F10 1,206.7 ns 2.92 ns 0.98
ToStringFormat Main 1 G 1,090.4 ns 1.39 ns 1.00
ToStringFormat PR 1 G 1,107.4 ns 0.36 ns 1.02
DefaultToString Main 1 ? 1,061.6 ns 1.55 ns 1.00
DefaultToString PR 1 ? 1,076.2 ns 0.69 ns 1.01
ToStringFormat Main 1234.5678 E 1,490.5 ns 2.60 ns 1.00
ToStringFormat PR 1234.5678 E 1,518.8 ns 6.51 ns 1.02
ToStringFormat Main 1234.5678 F0 1,870.1 ns 2.70 ns 1.00
ToStringFormat PR 1234.5678 F0 1,818.1 ns 1.35 ns 0.97
ToStringFormat Main 1234.5678 F10 3,643.4 ns 7.74 ns 1.00
ToStringFormat PR 1234.5678 F10 3,718.5 ns 7.97 ns 1.02
ToStringFormat Main 1234.5678 G 1,561.4 ns 2.22 ns 1.00
ToStringFormat PR 1234.5678 G 1,566.1 ns 2.62 ns 1.00
DefaultToString Main 1234.5678 ? 1,563.0 ns 6.31 ns 1.00
DefaultToString PR 1234.5678 ? 1,557.3 ns 3.53 ns 1.00
ToStringFormat Main 1.098(...)6E+30 [22] E 1,471.1 ns 4.17 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] E 1,466.4 ns 6.96 ns 1.00
ToStringFormat Main 1.098(...)6E+30 [22] F0 11,301.9 ns 5.31 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F0 11,344.2 ns 7.14 ns 1.00
ToStringFormat Main 1.098(...)6E+30 [22] F10 13,623.8 ns 9.97 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] F10 13,813.5 ns 8.96 ns 1.01
ToStringFormat Main 1.098(...)6E+30 [22] G 2,260.0 ns 2.17 ns 1.00
ToStringFormat PR 1.098(...)6E+30 [22] G 2,229.2 ns 2.34 ns 0.99
DefaultToString Main 1.098(...)6E+30 [22] ? 2,212.1 ns 2.66 ns 1.00
DefaultToString PR 1.098(...)6E+30 [22] ? 2,191.9 ns 4.82 ns 0.99

BDN_Artifacts.zip

@EgorBo
Copy link
Member

EgorBo commented May 30, 2024

Yeah looks like Mono-JIT and Mono interp are fine with the changes in this PR, at least for the provided benchmark. Unfortunately the bot doesn't yet support LLVM-AOT to check that as well.

@fanyang-mono
Copy link
Member

There are instructions here to run microbenchmarks with Mono AOT. (https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md#run-the-benchmarks-with-aot)

Please test it before merge. Usually, AOT takes the most hit from library changes.

@huoyaoyuan
Copy link
Member Author

The instruction I used for benchmark:

dotnet run -c Release -f net8.0 --affinity 65535  --runtimes monoaotllvm  --aotcompilerpath "C:\Users\Meow\source\dotnet\BenchmarkTestHost\monoAOT_PR\sgen\mini\mono-sgen.exe" --customruntimepack "C:\Users\Meow\source\dotnet\BenchmarkTestHost\monoAOT_PR\pack"  --aotcompilermode llvm

Results:

PR:

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3593/23H2/2023Update/SunValley3)
13th Gen Intel Core i9-13900K, 1 CPU, 32 logical and 24 physical cores
.NET SDK 9.0.100-preview.4.24267.66
  [Host]     : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2
  Job-FQWQJE : .NET 9.0.0 (42.42.42.42424) using MonoVM, X64 X86Base

Affinity=00000000000000001111111111111111  Runtime=MonoAOTLLVM  Toolchain=MonoAOTLLVM
Method value format Mean Error StdDev
ToStringFormat 0 E 126.13 ns 0.378 ns 0.354 ns
ToStringFormat 0 F0 71.27 ns 0.440 ns 0.412 ns
ToStringFormat 0 F10 104.93 ns 0.545 ns 0.510 ns
ToStringFormat 0 G 59.68 ns 0.524 ns 0.490 ns
DefaultToString 0 ? 54.44 ns 0.567 ns 0.530 ns
ToStringFormat 1 E 359.80 ns 1.770 ns 1.655 ns
ToStringFormat 1 F0 258.08 ns 1.218 ns 1.080 ns
ToStringFormat 1 F10 298.01 ns 1.273 ns 1.191 ns
ToStringFormat 1 G 154.68 ns 0.510 ns 0.477 ns
DefaultToString 1 ? 148.94 ns 0.666 ns 0.623 ns
ToStringFormat 1234.5678 E 214.97 ns 1.181 ns 1.105 ns
ToStringFormat 1234.5678 F0 338.89 ns 1.577 ns 1.475 ns
ToStringFormat 1234.5678 F10 501.09 ns 2.710 ns 2.535 ns
ToStringFormat 1234.5678 G 212.78 ns 1.048 ns 0.929 ns
DefaultToString 1234.5678 ? 209.32 ns 1.529 ns 1.430 ns
ToStringFormat 1.098(...)6E+30 [22] E 196.33 ns 1.254 ns 1.112 ns
ToStringFormat 1.098(...)6E+30 [22] F0 1,053.69 ns 5.062 ns 4.487 ns
ToStringFormat 1.098(...)6E+30 [22] F10 1,189.30 ns 5.812 ns 5.436 ns
ToStringFormat 1.098(...)6E+30 [22] G 287.90 ns 2.102 ns 1.966 ns
DefaultToString 1.098(...)6E+30 [22] ? 282.25 ns 1.599 ns 1.496 ns

main:

Method value format Mean Error StdDev
ToStringFormat 0 E 120.53 ns 1.899 ns 1.776 ns
ToStringFormat 0 F0 70.56 ns 0.576 ns 0.481 ns
ToStringFormat 0 F10 103.54 ns 1.347 ns 1.260 ns
ToStringFormat 0 G 57.83 ns 0.352 ns 0.329 ns
DefaultToString 0 ? 54.52 ns 0.466 ns 0.413 ns
ToStringFormat 1 E 345.41 ns 5.116 ns 4.786 ns
ToStringFormat 1 F0 260.28 ns 0.619 ns 0.549 ns
ToStringFormat 1 F10 305.23 ns 3.058 ns 2.860 ns
ToStringFormat 1 G 149.78 ns 1.623 ns 1.518 ns
DefaultToString 1 ? 146.37 ns 1.783 ns 1.668 ns
ToStringFormat 1234.5678 E 220.22 ns 2.379 ns 2.109 ns
ToStringFormat 1234.5678 F0 351.67 ns 2.425 ns 2.150 ns
ToStringFormat 1234.5678 F10 512.52 ns 8.060 ns 7.539 ns
ToStringFormat 1234.5678 G 208.87 ns 0.888 ns 0.831 ns
DefaultToString 1234.5678 ? 204.73 ns 0.748 ns 0.699 ns
ToStringFormat 1.098(...)6E+30 [22] E 194.07 ns 2.028 ns 1.798 ns
ToStringFormat 1.098(...)6E+30 [22] F0 1,090.41 ns 13.084 ns 10.925 ns
ToStringFormat 1.098(...)6E+30 [22] F10 1,208.15 ns 14.228 ns 13.309 ns
ToStringFormat 1.098(...)6E+30 [22] G 290.50 ns 2.323 ns 2.173 ns
DefaultToString 1.098(...)6E+30 [22] ? 286.22 ns 2.756 ns 2.578 ns

Looks no significant regression.

@tannergooding
Copy link
Member

I can get this merged after the Mono team signs off and n the perf numbers, just want to make sure we’re not missing any important scenarios

@tannergooding
Copy link
Member

@fanyang-mono, are there any other areas you want to see tested or does this look good for the Mono side?

@fanyang-mono
Copy link
Member

These tests should be sufficient for mono. :)

@@ -354,33 +354,33 @@ public override int GetHashCode()

public override string ToString()
{
return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo);
return Number.FormatFloat(m_value, null, NumberFormatInfo.CurrentInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not going to block on this, but we should probably call this FormatFloatingPoint instead and it would be good to fix it in a follow up PR.

Float is going to be confused with the C# keyword float, which should be called Single in the BCL API surface area

private static ulong ExtractFractionAndBiasedExponent<TNumber>(TNumber value, out int exponent)
where TNumber : unmanaged, IBinaryFloatParseAndFormatInfo<TNumber>
{
ulong bits = TNumber.FloatToBits(value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar here, this should probably be FloatingPointToBits

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Numerics community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants