-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Closed
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issuePerformance related issue
Milestone
Description
Runnable benchmark https://github.com/kerams/oh-dear.
The test is very simple - we do a single remapping of ValueSome, Some and Ok. (MethodImpl probably has no effect here.)
[<MemoryDiagnoser>]
type Current () =
[<MethodImpl(MethodImplOptions.NoInlining)>]
let o = Some 10
[<MethodImpl(MethodImplOptions.NoInlining)>]
let vo = ValueSome 10
[<MethodImpl(MethodImplOptions.NoInlining)>]
let r = Result<int, string>.Ok 10
[<Benchmark>]
member _.Option() =
match o with
| Some x -> Some (x + 3)
[<Benchmark>]
member _.Voption() =
match vo with
| ValueSome x -> ValueSome (x + 3)
[<Benchmark>]
member _.Result() =
match r with
| Ok x -> Result<int, string>.Ok (x + 3)Decompiled IL looks as expected.
[Benchmark(19, "C:\\code\\Test\\Bench\\Program.fs")]
public FSharpOption<int> Option()
{
FSharpOption<int> fsharpOption = this.o;
if (fsharpOption != null)
{
FSharpOption<int> fsharpOption2 = fsharpOption;
int x = fsharpOption2.Value;
return FSharpOption<int>.Some(x + 3);
}
throw new MatchFailureException("C:\\code\\Test\\Bench\\Program.fs", 21, 14);
}
[Benchmark(24, "C:\\code\\Test\\Bench\\Program.fs")]
public FSharpValueOption<int> Voption()
{
FSharpValueOption<int> fsharpValueOption = this.vo;
if (fsharpValueOption.Tag == 1)
{
int x = fsharpValueOption.Item;
return FSharpValueOption<int>.NewValueSome(x + 3);
}
throw new MatchFailureException("C:\\code\\Test\\Bench\\Program.fs", 26, 14);
}
[Benchmark(29, "C:\\code\\Test\\Bench\\Program.fs")]
public FSharpResult<int, string> Result()
{
FSharpResult<int, string> fsharpResult = this.r;
if (fsharpResult.Tag == 0)
{
int x = fsharpResult.ResultValue;
return FSharpResult<int, string>.NewOk(x + 3);
}
throw new MatchFailureException("C:\\code\\Test\\Bench\\Program.fs", 31, 14);
}Jitted on .NET 7 on Sharplab.
Benchmark results look like this, reproducible across multiple runtimes and CPUs (all results here).
BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1413/22H2/2022Update/SunValley2)
AMD Ryzen 9 7900, 1 CPU, 24 logical and 12 physical cores
.NET SDK=8.0.100-preview.2.23157.25
[Host] : .NET 8.0.0 (8.0.23.12803), X64 RyuJIT AVX2 DEBUG
DefaultJob : .NET 8.0.0 (8.0.23.12803), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|---|---|---|---|---|---|
| Option | 2.0639 ns | 0.0204 ns | 0.0171 ns | 0.0014 | 24 B |
| Voption | 3.0861 ns | 0.0381 ns | 0.0337 ns | - | - |
| Result | 0.2330 ns | 0.0071 ns | 0.0063 ns | - | - |
ValueOption remapping is slower than Option despite the fact that Option needs to allocate. I'd expect performance to be at least as good as in the Result case (since the struct size of ValueOption is smaller than Result).
rstm-sf
Metadata
Metadata
Assignees
Labels
area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMICLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issuePerformance related issue