-
Notifications
You must be signed in to change notification settings - Fork 825
Labels
Area-Compiler-OptimizationThe F# optimizer, release code gen etc.The F# optimizer, release code gen etc.BugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.(Internal MS Team use only) Describes an issue with moderate impact on existing code.
Milestone
Description
Repro steps
We noticed one of our F# tests over in dotnet/runtime got slow enough to time out when we updated to a preview 7 SDK (issue: dotnet/runtime#106603)
- Download the .NET 9 preview6 and preview7 SDK zips from https://dotnet.microsoft.com/en-us/download/dotnet/9.0
- Add the following to an F# project targetting .NET 9:
open System
open System.Diagnostics
// 16 byte struct
[<Struct>]
type Point2D(x: double, y: double) =
member _.X = x
member _.Y = y
// Will create a tail il instruction and force a tail call. This is will become
// a fast tail call on unix x64 as the caller and callee have equal stack size
let fifth() =
let rec fifthMethodFirstCallee(iterationCount, firstArg: Point2D, secondArg: Point2D, thirdArg: Point2D, fourthArg: Point2D, fifthArg: Point2D) =
if firstArg.X <> 10.0 then -100
else if firstArg.Y <> 20.0 then -101
else if secondArg.X <> 30.0 then -102
else if secondArg.Y <> 40.0 then -103
else if thirdArg.X <> 10.0 then -104
else if thirdArg.Y <> 20.0 then -105
else if fourthArg.X <> 30.0 then -106
else if fourthArg.Y <> 40.0 then -107
else if fifthArg.X <> 10.0 then -108
else if fifthArg.Y <> 20.0 then -109
else if iterationCount = 0 then
100
else if iterationCount % 2 = 0 then
fifthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
else
fifthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
and fifthMethodSecondCallee(iterationCount, firstArg, secondArg, thirdArg, fourthArg, fifthArg) =
if firstArg.X <> 10.0 then -150
else if firstArg.Y <> 20.0 then -151
else if secondArg.X <> 30.0 then -152
else if secondArg.Y <> 40.0 then -153
else if thirdArg.X <> 10.0 then -154
else if thirdArg.Y <> 20.0 then -155
else if fourthArg.X <> 30.0 then -156
else if fourthArg.Y <> 40.0 then -157
else if fifthArg.X <> 10.0 then -158
else if fifthArg.Y <> 20.0 then -159
else if iterationCount = 0 then
101
else if iterationCount % 2 = 0 then
fifthMethodSecondCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
else
fifthMethodFirstCallee(iterationCount - 1, firstArg, secondArg, thirdArg, fourthArg, fifthArg)
let point = Point2D(10.0, 20.0)
let secondPoint = Point2D(30.0, 40.0)
let retVal = fifthMethodFirstCallee(1000000, point, secondPoint, point, secondPoint, point)
if retVal <> 100 && retVal <> 101 then
printfn "Method -- Failed, expected result: 100 or 101, calculated: %d" retVal
-5
else
0
[<EntryPoint>]
let main argv =
let startTime = Stopwatch.StartNew()
for i in 0..100 do
ignore (fifth ())
let elapsedTime = startTime.Elapsed.TotalMilliseconds
printfn "%fms" elapsedTime
0
- Compare
p6\dotnet.exe run -c Release
andp7\dotnet.exe run -c Release
. On my machine:
❯ C:\dev\temp\sdks\p6\dotnet.exe run -c Release
330.724700ms
❯ C:\dev\temp\sdks\p7\dotnet.exe run -c Release
7513.202800ms
Looking at the C# decompilation of the method, I see the following for preview 6:
// Program
// Token: 0x06000003 RID: 3 RVA: 0x000022B4 File Offset: 0x000004B4
public static int fifth()
{
Program.Point2D point = new Program.Point2D(10.0, 20.0);
Program.Point2D secondPoint = new Program.Point2D(30.0, 40.0);
int retVal = Program.fifthMethodFirstCallee@13(1000000, point, secondPoint, point, secondPoint, point);
if (retVal != 100 && retVal != 101)
{
PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit> printfFormat = new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("Method -- Failed, expected result: 100 or 101, calculated: %d");
PrintfModule.PrintFormatLineToTextWriter<FSharpFunc<int, Unit>>(Console.Out, printfFormat).Invoke(retVal);
return -5;
}
return 0;
}
and the following fore preview 7:
// Program
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
public static int fifth()
{
FSharpFunc<int, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, int>>>>>> fsharpFunc2;
FSharpFunc<int, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, int>>>>>> fsharpFunc = new Program.fifthMethodFirstCallee@28(fsharpFunc2);
fsharpFunc2 = new Program.fifthMethodSecondCallee@46(fsharpFunc);
((Program.fifthMethodFirstCallee@28)fsharpFunc).fifthMethodSecondCallee@46 = fsharpFunc2;
Program.Point2D point = new Program.Point2D(10.0, 20.0);
Program.Point2D secondPoint = new Program.Point2D(30.0, 40.0);
FSharpFunc<int, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, FSharpFunc<Program.Point2D, int>>>>>> fsharpFunc3 = fsharpFunc;
int num = 1000000;
Program.Point2D point2D = point;
Program.Point2D point2D2 = secondPoint;
Program.Point2D point2D3 = point;
Program.Point2D point2D4 = secondPoint;
Program.Point2D point2D5 = point;
int retVal = FSharpFunc<int, Program.Point2D>.InvokeFast<Program.Point2D, Program.Point2D, Program.Point2D, FSharpFunc<Program.Point2D, int>>(fsharpFunc3, num, point2D, point2D2, point2D3, point2D4).Invoke(point2D5);
if (retVal != 100 && retVal != 101)
{
PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit> printfFormat = new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("Method -- Failed, expected result: 100 or 101, calculated: %d");
PrintfModule.PrintFormatLineToTextWriter<FSharpFunc<int, Unit>>(Console.Out, printfFormat).Invoke(retVal);
return -5;
}
return 0;
}
so seems like there are some quite significant differences in the IL codegen.
Expected behavior
Equivalent performance.
Actual behavior
Performance seems degraded.
T-Gro
Metadata
Metadata
Assignees
Labels
Area-Compiler-OptimizationThe F# optimizer, release code gen etc.The F# optimizer, release code gen etc.BugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.(Internal MS Team use only) Describes an issue with moderate impact on existing code.
Type
Projects
Status
In Progress