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: consider doing some type opts earlier #7815

Open
AndyAyersMS opened this issue Apr 5, 2017 · 6 comments
Open

JIT: consider doing some type opts earlier #7815

AndyAyersMS opened this issue Apr 5, 2017 · 6 comments
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI enhancement Product code improvement that does NOT require public API changes/additions JitThroughput CLR JIT issues regarding speed of JIT itself optimization tenet-performance Performance related issue
Milestone

Comments

@AndyAyersMS
Copy link
Member

The jit should now be able to optimize many of the common type specialization checks during importation. Since the jit can also do some rudimentary branch opts during importation, early type specialization check optimization would allow the jit to skip importing chunks of IL guarded by type checks that are known to fail.

For example consider any non-intrinsic vector method, eg Vector<T>:op_Multiply. SinceT is constrained to non-ref types this method's instantiation parameter is always a known class at jit time. So roughly 90% of the IL in this method is unreachable by any particular instantiation and doesn't need to be imported.

This should provide a throughput boost. It might also make it easier for the jit to consider inlining such methods, because the jit can more accurately assess the "true" size of the method.

category:throughput
theme:importer
skill-level:expert
cost:medium

@redknightlois
Copy link

redknightlois commented Apr 5, 2017

We abuse inlining + constrained non-ref types a lots. Would give us a huge boost if it able to catch some of the methods that are currently non inlineable!!

@AndyAyersMS
Copy link
Member Author

Did some initial prototyping to refactor the type equality optimizations so they can be run during importation (right now they're embedded in morph). There are two main optimizations:

  • Modify Type.op_Equality(a, b) to be simply a == b, when safe to do so (and likewise for inequality). This one is pretty easy to move forward since we have an intrinsic for Type.op_Equality and some facilities for identifing when a or b is known to be a RuntimeType.
  • Modify a == b when either a' or bis aRuntimeType` constructed from a compile-time handle. Here we can forego fetching the type object and just compare the handle.

See master...AndyAyersMS:EarlyTypeOpt

However during importation we're only opportunistically folding a small subset of trees as we build them, and the folding is done via gtFoldExpr which does not take a bottom-up top-down approach. Not sure why, perhaps it is too costly, or redundant/overlaps with aspects of morph.

At any rate, unless we arrange to aggressively or opportunistically fold subtrees, higher level folding may be blocked because subtrees haven't been simplified. In particular if we push both the folding optimizations above forward, we still miss early folding in cases like:

    static bool IsInt<T>()
    {
        if (typeof(T) == typeof(int))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
  
    IsInt<int>();

Because we only fold trees at conditional branches, we end up trying to fold a tree like the following:

 /--*  CNS_INT   int    0
-*  EQ        int   
 \--*  CALL      int    System.Type.op_Equality
    +--*  CALL help ref    HELPER.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE
    |  \--*  CNS_INT(h) long   0x7fff9d4477e0 class
    \--*  CALL help ref    HELPER.CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE
       \--*  CNS_INT(h) long   0x7fff9d4477e0 class

and top-down folding doesn't simplify this case, since it does not recurse down and simplify the call.

Also if we don't fold during importation in expression contexts we miss out on early folding for non-branch cases like

    static bool IsInt<T>()
    {
        return (typeof(T) == typeof(int));
    }

The upshot is that it might be interesting and worthwhile to invoke folding much more frequently during importation, to prune subtrees sooner and to enable knock-on effects.

@AndyAyersMS
Copy link
Member Author

The changes in dotnet/coreclr#14244 add some selective eager folding for type equality tests.

We still can't optimize cases where one of the types comes from a runtime lookup:

    static bool IsInt<T>()
    {
        return (typeof(T) == typeof(int));
    }

    static bool IsString<T>()
    {
        return (typeof(T) == typeof(string));
    }
  • IsInt<int>() and IsString<int>() clean up after JIT: run type equality optimizations earlier coreclr#14244.
  • IsInt<string>() is not yet getting cleaned up. This one seems like it ought to be doable; I just need to verify that runtime lookups cannot ever return value types, and add the appropriate checks.
  • IsString<string>() is not going to ever be cleaned up, given the runtime's current sharing strategy; it's the price we pay for aggressively sharing code over ref types.

@AndyAyersMS
Copy link
Member Author

dotnet/coreclr#14317 can optimize the IsInt<string> cases. It enables the jit to resolve some type comparisons even when the types involved require runtime lookups.

@benaadams
Copy link
Member

benaadams commented Oct 8, 2017

IsString<string>() is not going to ever be cleaned up, given the runtime's current sharing strategy; it's the price we pay for aggressively sharing code over ref types.

I've often wondered if there could be an option to configure that with a form of MethodImplOptions. Particular use case is Type dictionaries (for DI etc)

So code like Set<TFeature>(TFeature feature)/Get<TFeature>() would be collapsed to a single line per Type.

The example is more complex as its via an interface IFeature.Get<TFeature>() so that's probably an even bigger ask as it won't inline :-/

@AndyAyersMS
Copy link
Member Author

All this may be particularly relevant for improving the speed of a Tier0 jit. Presumably prejitting has punted on most generic cases and those are the ones amenable to fairly radical jit-time simplification via early type opts, so enabling those opts in Tier0 would seem to hit a sweet spot.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@BruceForstall BruceForstall added the JitUntriaged CLR JIT issues needing additional triage label Oct 28, 2020
@BruceForstall BruceForstall removed the JitUntriaged CLR JIT issues needing additional triage label Jan 24, 2023
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 enhancement Product code improvement that does NOT require public API changes/additions JitThroughput CLR JIT issues regarding speed of JIT itself optimization tenet-performance Performance related issue
Projects
None yet
Development

No branches or pull requests

5 participants