Skip to content
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

Eliminate tuple allocations when sequential expressions are involved #11296

Merged
merged 5 commits into from
Apr 10, 2021

Conversation

kerams
Copy link
Contributor

@kerams kerams commented Mar 23, 2021

[<System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)>]
let y () = 3

let z () =
    let a, b =
        "".ToString ()
        System.DateTime.Now
        "3".ToString ()
        2, y ()
    System.DateTime.Now
    a + b

becomes

[MethodImpl(MethodImplOptions.NoInlining)]
public static int y()
{
    return 3;
}

public static int z()
{
    "".ToString();
    DateTime now = DateTime.Now;
    "3".ToString();
    int num = y();
    DateTime now2 = DateTime.Now;
    return 2 + num;
}

instead of

[MethodImpl(MethodImplOptions.NoInlining)]
public static int y()
{
    return 3;
}

public static int z()
{
    "".ToString();
    DateTime now = DateTime.Now;
    "3".ToString();
    Tuple<int, int> tuple = new Tuple<int, int>(2, y());
    int item = tuple.Item2;
    int item2 = tuple.Item1;
    DateTime now2 = DateTime.Now;
    return item2 + item;
}

@kerams
Copy link
Contributor Author

kerams commented Mar 23, 2021

What shape would a test for this take and where would it go?

@kerams
Copy link
Contributor Author

kerams commented Mar 25, 2021

Ping, I really need this for a follow-up PR :).

@vzarytovskii
Copy link
Member

I think IL/CodeGen tests will be sufficient enough. @TIHan @KevinRansom what do you think?

@kerams
Copy link
Contributor Author

kerams commented Mar 26, 2021

So I've added a test on 3 simple functions that makes sure there are no tuples and the order of operations is preserved. Let me know if more complex tests are required (an example would be nice), otherwise this is ready for review.

@vzarytovskii
Copy link
Member

Some infrastructure issue with macOS build, gonna restart.

@kerams
Copy link
Contributor Author

kerams commented Mar 26, 2021

But net472 and net5 also apparently generate different IL for the System.Console.ReadKey () call.... Let me change that.

@vzarytovskii
Copy link
Member

@TIHan or @dsyme can you pls also take a look at it?

@kerams
Copy link
Contributor Author

kerams commented Apr 5, 2021

@cartermp, sorry for the ping. Just want to make sure this has not been forgotten, even if the PR has been ready for barely more than a week. If you're all simply busy, that's cool.

@vzarytovskii vzarytovskii requested a review from TIHan April 5, 2021 17:43
@TIHan
Copy link
Contributor

TIHan commented Apr 5, 2021

Apologies for the wait, I'll take a look at this.

|> shouldSucceed
|> verifyIL [

// public static int x()
Copy link
Contributor

@TIHan TIHan Apr 5, 2021

Choose a reason for hiding this comment

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

nit: commented code should be removed
Or explain why the commented code is there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The comment is there to show the C# representation of the IL below. Thought it would be helpful. Should I still remove it?

Copy link
Contributor

@TIHan TIHan left a comment

Choose a reason for hiding this comment

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

The change looks very straight-forward and very cool.

I would add a few more tests, specifically tests that actually require a tuple to be used.
example:

[<System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)>]
let y () = 3

type Test =
    [<System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)>]
    static member test(x: int32 * int32) = x

let z () =
    let a, b =
        "".ToString ()
        System.DateTime.Now
        "3".ToString ()
        Test.test(2, y ())
    System.DateTime.Now
    a + b
[<System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)>]
let y () = 3

type Test =
    [<System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)>]
    static member test(x: int32 * int32) = x

let z () =
    let (a, b) as t =
        "".ToString ()
        System.DateTime.Now
        "3".ToString ()
        2, y ()
    System.DateTime.Now
    let _ = Test.test(t)
    a + b

@kerams
Copy link
Contributor Author

kerams commented Apr 5, 2021

Ok, I've added those two as well as this one, which demonstrates there is no intermediate tuple

let v () =
    let a, b =
        "".ToString () |> ignore
        System.DateTime.Now |> ignore
        "3".ToString () |> ignore
        2L, f ()
    System.DateTime.Now |> ignore
    a, b

The optimization does not kick in in your first example. Since test is not inlined, Test.test(2, y ()) is considered first class tuple use. This is the case even when you remove all sequential expressions sharplab link, so no regression as far as I am concerned.

Please let me know if you want to keep the commented out C# code (see comment above), thanks.

Copy link
Contributor

@cartermp cartermp left a comment

Choose a reason for hiding this comment

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

Thanks for addressing all the feedback!

@cartermp cartermp merged commit fc334cd into dotnet:main Apr 10, 2021
@kerams
Copy link
Contributor Author

kerams commented Apr 10, 2021

My pleasure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants