dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.29k stars 4.74k forks source link

JIT: Optimize LINQ style fast path casts #109077

Open EgorBo opened 3 weeks ago

EgorBo commented 3 weeks ago

LINQ has many "fast path" casts to avoid O(n) when it sees the input is a known collection. It seems like there are many opportunities around such casts for JIT. Example:

object _obj = new MyClass[2];

[Benchmark]
public int IsCollection() => _obj is ICollection<MyClass> coll ? coll.Count : 0;

[Benchmark]
public int IsCollection_expected()
{
    var obj = _obj;
    if (obj is MyClass[])
        return Unsafe.As<object, MyClass[]>(ref obj).Length;
    return 0;
}

Dynamic PGO should make both benchmark to be equally fast, unfortunately, it's not the case currently:

Method Mean Error StdDev
IsCollection 6.3864 ns 0.0990 ns 0.0926 ns
IsCollection_expected 0.0000 ns 0.0000 ns 0.0000 ns
EgorBo commented 3 weeks ago

@EgorBot -arm64 -amd --runtimes net8.0 net9.0

using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Collections;

public class MyClass {}

public class Bench
{
    object _obj = new MyClass[2];

    [Benchmark]
    public int IsCollection() => _obj is ICollection<MyClass> coll ? coll.Count : 0;

    [Benchmark]
    public int IsCollection_expected()
    {
        var obj = _obj;
        if (obj is MyClass[])
            return Unsafe.As<object, MyClass[]>(ref obj).Length;
        return 0;
    }
}
dotnet-policy-service[bot] commented 3 weeks ago

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