-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Nullable<T> interface check / dispatch is comparatively very slow #50915
Comments
cc @dotnet/jit-contrib. |
I can take a look, so basically some of our optimizations are just not
to be optimized to:
(same for box+isinst+ldnull+cgt.un). Another issue here is that we might want to optimize boxing for Nullables from this:
to something like this:
|
Would that really make a difference? IIRC, boxing a no-value |
It would allow us to avoid "call" overhead (by slightly regressing size and additional check for non-null case - so I agree that one is questionable and requires some analysis or how frequent we pass null there in a general case) branch: EgorBo@a0d8ee9 |
Thanks for looking at this, @EgorBo. Knowing whether these can and will (eventually) be improved will impact what overloads we choose to expose in #50635. This is a significant enough difference that if we don't think this can be improved meaningfully, we'll need to expose the T? overloads there. If we can make it work better, we can leave those out. What's particularly interesting to me is how much worse the nullable case is than just boxing it from the start:
[Benchmark]
public int NullableBoxed() => CallM((object)_ns); |
Yeah that's funny, you box it once while original |
I think this specific case definitely can be improved as part of 6.0, the first part is already ready: #50997 |
@EgorBo, you closed this, but that one PR didn't fully address this, did it? |
Oops, indeed, will use "contribute" next time 🙂 |
Moving to 7.0.0 for the second part where we need to optimize
It'd certainly doable but unlikely to make it into 6.0 😞 |
Ugh, forgot about this issue this milestone, I see locally improvements with the following patch: case CORINFO_HELP_BOX_NULLABLE:
{
GenTree* typeArg = call->gtArgs.GetUserArgByIndex(0)->GetNode();
CORINFO_CLASS_HANDLE nullableCls = gtGetHelperArgClassHandle(typeArg);
if (nullableCls != NO_CLASS_HANDLE)
{
assert(info.compCompHnd->getBoxHelper(nullableCls) == CorInfoHelpFunc::CORINFO_HELP_BOX_NULLABLE);
objClass = info.compCompHnd->getTypeForBox(nullableCls);
*pIsNonNull = false;
*pIsExact = false;
}
}
break; (in |
Made an improvement for this issue this release (#95764) but it's still not fixed, moved to next
|
This comment was marked as resolved.
This comment was marked as resolved.
@EgorBot -intel -arm64 --runtimes net6.0 net7.0 net8.0 net9.0 using BenchmarkDotNet.Attributes;
[MemoryDiagnoser]
[DisassemblyDiagnoser]
public class Bench
{
private S _s = default(S);
private C _c = new C();
private S? _ns = (S?)default(S);
[Benchmark] public int Struct() => CallM(_s);
[Benchmark] public int Class() => CallM(_c);
[Benchmark] public int Nullable() => CallM(_ns);
[Benchmark] public int NullableSpecialized() => CallMSpecial(_ns);
static int CallM<T>(T t)
{
if (t is IMyInterface)
{
return ((IMyInterface)t).M();
}
return 0;
}
static int CallMSpecial<T>(T? t) where T : struct
{
if (t.HasValue)
{
return CallM(t.GetValueOrDefault());
}
return 0;
}
}
interface IMyInterface
{
int M();
}
struct S : IMyInterface
{
public int M() => 42;
}
class C : IMyInterface
{
public int M() => 42;
} |
Repro:
Is there anything we can do to make the nullable case faster and less inclined to box without needing a special code path to handle it (e.g. my CallMSpecial, which in my current actual code is another overload so that a nullable binds to it more tightly).
Here's what the codegen for the nullable case looks like:
category:cq
theme:basic-cq
skill-level:intermediate
cost:medium
impact:small
The text was updated successfully, but these errors were encountered: