-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
I'm not sure whether this might be JIt imporvement or F#.. Basically I will duplicate this in fsharp repo (dotnet/fsharp#12138) too.
Consider these 2 methods:
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
let fold initial folder (enumerator: #IEnumerator<'i>) =
let folder = OptimizedClosures.FSharpFunc<_,_,_>.Adapt folder
let mutable enumerator = enumerator
let mutable result = initial
while enumerator.MoveNext() do
result <- folder.Invoke(result, enumerator.Current)
result
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Fold<TResult, TItem, TEnumerator>(TResult result, FSharpFunc<TResult, FSharpFunc<TItem, TResult>> folder, TEnumerator enumerator)
where TEnumerator : IEnumerator<TItem>
{
var fSharpFunc = OptimizedClosures.FSharpFunc<TResult, TItem, TResult>.Adapt(folder);
var enumerator2 = enumerator;
var result2 = result;
while (enumerator2.MoveNext())
result2 = fSharpFunc.Invoke(result2, enumerator2.Current);
return result2;
}
They look very similar but there is an importnat il emit difference:
C# method is compiled to this basically:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult FoldRoslynVersion<TResult, TItem, TEnumerator>(TResult result, FSharpFunc<TResult, FSharpFunc<TItem, TResult>> folder, TEnumerator enumerator)
where TEnumerator : IEnumerator<TItem>
{
var fSharpFunc = OptimizedClosures.FSharpFunc<TResult, TItem, TResult>.Adapt(folder);
var enumerator2 = enumerator;
var result2 = result;
goto movenext;
logic:
result2 = fSharpFunc.Invoke(result2, enumerator2.Current);
movenext:
if (!enumerator2.MoveNext())
return result2;
goto logic;
}
While F# is compiled to this basically:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult FoldFSharpVersion<TResult, TItem, TEnumerator>(TResult result, FSharpFunc<TResult, FSharpFunc<TItem, TResult>> folder, TEnumerator enumerator)
where TEnumerator : IEnumerator<TItem>
{
var fSharpFunc = OptimizedClosures.FSharpFunc<TResult, TItem, TResult>.Adapt(folder);
var enumerator2 = enumerator;
var result2 = result;
movenext:
if (!enumerator2.MoveNext())
goto exit;
result2 = fSharpFunc.Invoke(result2, enumerator2.Current);
goto movenext;
exit:
return result2;
}
While difference might be non obvious, C# version with condition at the end of the method results in 10-15% perf imporvement while having the same assembly size.
Can JIT compiler regonize these patterns better and ideally emit the same code for both variants?
category:cq
theme:loop-opt
skill-level:expert
cost:large
impact:large
SupinePandora43, Daniel-Svensson and colejohnson66
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