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

JIT fails to escape simple usage of List<> and [] #111838

Open
ShreyasJejurkar opened this issue Jan 26, 2025 · 9 comments
Open

JIT fails to escape simple usage of List<> and [] #111838

ShreyasJejurkar opened this issue Jan 26, 2025 · 9 comments
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI untriaged New issue has not been triaged by the area owner

Comments

@ShreyasJejurkar
Copy link
Contributor

Was tinkering around JIT generated code for List<> and raw arrays for very simple below code.

    public static void ListUsage()
    {
        List<int> numbers = new(1);
        numbers.Add(1);
        Console.WriteLine(numbers[0]);
    }

    public static void ArrayUsage()
    {
        int[] numbers = new int[1] {1};
        Console.WriteLine(numbers[0]);
    }

    public static void Expected()
    {
        Console.WriteLine(1);
    }

The code is very self explanatory, nothing magical or complex as such. I was comparing the machine code generated by each method. I was expecting JIT to see the code flow and optimize the code gen generated for ListUsage and ArrayUsage to be similar as Expected method, but it does not.

Below are things JIT can see and I would expect it to optimize it further.

  1. JIT can see List<int> numbers (in method ListUsage) and int[] numbers (in method ArrayUsage) are declared inside the method and get destroyed in method itself at the end (does not get pass to any other method). It even sees the maximum size of those data structure which is 1 so ideally it should stack allocate those instead of doing heap allocation.
  2. The number that is getting added to collection is pure constant (1), so its not dynamic value. And on very next line we are even printing it directly by indexing and after that the method ends. So ideally JIT should see this flow and should optimize the code gen just like the code-gen generated by Expected method in above code.

Tested against main branch on CC => https://godbolt.org/z/eWv6Yaoaj

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jan 26, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 26, 2025
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@huoyaoyuan
Copy link
Member

It's not de-abstract, which is applied to virtual calls. Instead, it's about escape analysis to cancel object allocation.

De-allocation of struct was just merged recently (#104906). De-allocation of objects is much more complex and hasn't been done yet.

@ShreyasJejurkar
Copy link
Contributor Author

ShreyasJejurkar commented Jan 26, 2025

Instead, it's about escape analysis to cancel object allocation

Right, I was thinking of that exact word. I will update the issue.

De-allocation of objects is much more complex and hasn't been done yet.

Is there any existing issue which refers to it for tracking?

@ShreyasJejurkar ShreyasJejurkar changed the title JIT fails to De-abstract simple usage of List<> and [] JIT fails to escape simple usage of List<> and [] Jan 26, 2025
@huoyaoyuan
Copy link
Member

I don't know any actual work has started now. The topic of broader escape analysis is covered by #108931 .

/cc @AndyAyersMS

@AndyAyersMS
Copy link
Member

While they may seem similar, a list is quite a bit more complicated than an array and requires new capabilities to optimize. Some of the work in .NET 10 may chip away at this, but I would not expect the whole thing to be optimized.

@cptjazz
Copy link
Contributor

cptjazz commented Jan 26, 2025

Out of curiosity: why would one do an optimization as in the OPs example in the JIT, and not already as a lowering pass when generating MSIL?

@CyrusNajmabadi
Copy link
Member

Out of curiosity: why would one do an optimization as in the OPs example in the JIT, and not already as a lowering pass when generating MSIL?

The component that generates the msil does not have the information necessary to do these optimizations.

@huoyaoyuan
Copy link
Member

C# compiler can't see the implementation of List<T>. It doesn't even know that getting and setting at the same index are invariant.

In more common cases, the operations can spread in multiple methods that can be inlined together.

@cptjazz
Copy link
Contributor

cptjazz commented Jan 27, 2025

Thanks for explaining

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

5 participants