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

Add RuntimeHelpers.IsReferenceOrContainsReferences<T>() #19493

Closed
jkotas opened this issue Nov 28, 2016 · 31 comments
Closed

Add RuntimeHelpers.IsReferenceOrContainsReferences<T>() #19493

jkotas opened this issue Nov 28, 2016 · 31 comments
Labels
api-approved API was approved in API review, it can be implemented area-System.Runtime help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@jkotas
Copy link
Member

jkotas commented Nov 28, 2016

namespace System.Runtime.CompilerServices
{
    public static class RuntimeHelpers
    {
        public static bool IsReferenceOrContainsReferences<T>();
    }
}

This method will return true if T is reference type; or if T is value type that contains reference type fields. false otherwise.

This API is intended to be used primarily for optimizations:

  • Generic List calls Array.Clear on its underlying array in the implementation of Clear, RemoveAll, RemoveRange methods today. It has to do so because the generic argument can contain references which must be freed for GC. It can be skipped using this method when not required.

  • Algorithms for memory manipulations can have optimized paths for memory blocks without GC references. This method can be used to determine whether the optimized path is safe to use. Span<T> is example of such memory manipulation algorithms. It going to use this API internally in its implementation.

This API addition was tracked by CoreCLR issue. https://github.com/dotnet/coreclr/issues/1135 has a lot of discussion about the right name for this API. The alternative names considered include ContainsReferences, IsGCPointerOrContainsGCPointers.

@jkotas jkotas changed the title Add RuntimeHelpers.IsReferenceOrContainsReferences<T>() Add RuntimeHelpers.IsReferenceOrContainsReferences<T>() Nov 28, 2016
@jkotas
Copy link
Member Author

jkotas commented Nov 28, 2016

cc @omariom @nietras

@benaadams
Copy link
Member

In Jit terms this and its associated branch can always be elided? Shared code is always a reference; and value type code is always unique per value type?

@jkotas
Copy link
Member Author

jkotas commented Nov 28, 2016

In Jit terms this and its associated branch can always be elided?

Correct. This method will be intrinsic that the JIT can optimize out.

@nietras
Copy link
Contributor

nietras commented Nov 28, 2016

@jkotas should this employ the caching behaviour internally (e.g. via PerTypeValues<T> or TypeCache<T>) here (for the portable version)? Or are "clients" responsible for this?

I would think it best to have the cache as early as possible so there is only one cache.

@jkotas
Copy link
Member Author

jkotas commented Nov 28, 2016

There is no point in having the cache if it is JIT intrinsic. And even if it is not JIT intrinsic, it would be just fetching a few bits from MethodTable* - no point in caching it either.

Note that there won't be a public portable version of this method. The implementation using reflection will be internal to Span<T>.

@masonwheeler
Copy link
Contributor

@jkotas

Note that there won't be a public portable version of this method. The implementation using reflection will be internal to Span.

Huh? In your example, it's marked as public.

@jkotas
Copy link
Member Author

jkotas commented Nov 28, 2016

Huh? In your example, it's marked as public.

Right, this issue is about adding a new public .NET Core API implemented efficiently by the runtime, that may come to .NET Framework in future version. The slower portable Span implementation cannot use this public API because of it needs to run on existing runtimes - that's why it needs internal implementation of the API using reflection.

@masonwheeler
Copy link
Contributor

Ah, I see. That makes sense.

@terrajobst
Copy link
Member

terrajobst commented Nov 29, 2016

The API makes perfect sense.

@jkotas: should we add a non-generic version that takes Type? This might be useful for implementing serializers. Having to call a generic API would be quite painful.

The only thing is that we believe we should name this concept, e.g. blittable. There are likely other areas where we'd use this nomenclature (e.g. casting from Span<char> to Span<byte>). It would be very unfortunate if the naming doesn't align.

@MadsTorgersen @jaredpar, have you guys thought about naming this concept?

@benaadams
Copy link
Member

benaadams commented Nov 29, 2016

blittable is a bit different though? e.g. Decimal and DateTime are not blittable; but they also are not reference types

@jkotas
Copy link
Member Author

jkotas commented Nov 29, 2016

Right, blittable is existing concept, different from this one, specific to interop - msdn definition: https://msdn.microsoft.com/en-us/library/75dwhxf7(v=vs.110).aspx

@jkotas
Copy link
Member Author

jkotas commented Nov 29, 2016

The C# spec calls similar concept unmanaged-type, but that concept has a known ugly problems (spooky action at a distance, etc.). I do not think it makes sense to couple this property to it either.

@jaredpar
Copy link
Member

Explicit blittable support is one item we're confsidering for the next version of C#. It would solve the spooky action by making usages explicit and verified.

I don't think it would work for this feature though. The C# feature is a compile time artifact while the methods @jkotas listed need to make decisions at runtim.

@nietras
Copy link
Contributor

nietras commented Nov 29, 2016

@jkotas ha so much I am still not partial too, so have some questions.

no point in having the cache if it is JIT intrinsic. And even if it is not JIT intrinsic, it would be just fetching a few bits from MethodTable* - no point in caching it either.

I understand that for JIT intrinsic it does not matter, but when it is not, will it be implemented in coreclr? So there won't be a "portable" version of this, for others e.g. me ;) to use?

Note that there won't be a public portable version of this method. The implementation using reflection will be internal to Span.

So... this can't be used in Span<T>, what about List<T>, Dictionary<TKey, TValue> etc.?

blittable, reference free value type, unmanaged, primitive etc.

As I noted in another issue, I do believe there needs to be better qualification/clear naming of these "concepts" as these are often confused with eachother. F# uses unmanaged for the "pure" value type concept. Others suggest primitive but that is also wrong. And so forth. A document explaining these and vocabulary would be nice. And of course having this concept "coined" with a new term would be great too.

@jaredpar
Copy link
Member

As I noted in another issue, I do believe there needs to be better qualification/clear naming of these "concepts" as these are often confused with eachother.

Agree. Part of the reason there is no central doc is because a lot of these issues are in the very early stages. We haven't committed to doing them and hence haven't done a lot of work trying to get coherent docs in order.

F# uses unmanaged for the "pure" value type concept. Others suggest primitive but that is also wrong.

The term unmanaged is certainly more ubiquitous given it is also how the C# spec refers to such types: unmanaged types specifically. It's a strong possibility for what we'd call the C# feature if implemented.

Terms like blittable / primitive are just used today because there are existing implementations / presentations that we are looking at. It's how Midori implemented this feature and hence there is a deal of existing vocabulary that we are using.

@benaadams
Copy link
Member

unmanaged has connotations of data sourced external to the clr; rather than data that doesn't contain GC references. The type if it was a field in a class would also be managed in these terms. Might just be me though...

@jkotas
Copy link
Member Author

jkotas commented Nov 29, 2016

I agree with @benaadams

The early concepts have nothing to do with this API. It is pretty unlikely that any of them will match what this API does.

This API just checks whether the valuetype contains anything tracked by the GC, nothing else. It is meant to allow libraries to do certain operation more efficiently if it does not - the write up about intended use at the top has more details.

@nietras
Copy link
Contributor

nietras commented Nov 29, 2016

unmanaged has connotations of data sourced external to the clr

I completely agree, wasn't suggesting this was better than other. Just that we need a new unambiguous term, for future. I think naming of IsReferenceOrContainsReferences is fine for now, since it will probably take some time before a better term can be agreed upon.

@mattwarren
Copy link
Contributor

mattwarren commented Nov 29, 2016 via email

@masonwheeler
Copy link
Contributor

I've heard it said there are only two hard problems in computer science: Cache invalidation, naming things well, and off-by-one errors...

@terrajobst
Copy link
Member

terrajobst commented Nov 29, 2016

[@nietras] I think naming of IsReferenceOrContainsReferences is fine for now, since it will probably take some time before a better term can be agreed upon.

With APIs there is almost never a "for now". Once in, we can rarely change the APIs (unless it's a preview). Just going with a random name because we can't think of one "right now" isn't a good enough reason to accept breaking changes later, though.

@jkotas @jaredpar: To be clear, I wasn't suggesting to use an existing concept for the name. My desire is that we should get our terminology straight. As both of you pointed out, the space is confusing precisely because we never bothered to make these things first class concepts with a better name.

@nietras
Copy link
Contributor

nietras commented Nov 29, 2016

almost never a "for now"

@terrajobst of course and I agree that the best case would be to get these concepts named for once.

Perhaps I also misunderstood @jkotas when he said this would not be "public", since it was probably more related to not being "public" in a portable sense. Still juggling with these different terms too... what's .NET Core specific, what's .NET Standard i.e. portable API?, what's part of coreclr, whats part of corefx, what's part of both etc. And when are we getting all the cool stuff in .NET Framework? Lots to learn, which is great. Just wish there was a better introduction to this ;)

@jkotas
Copy link
Member Author

jkotas commented Nov 30, 2016

when it is not, will it be implemented in coreclr? So there won't be a "portable" version of this, for others e.g. me ;) to use?

@nietras Correct. It is consequence of adding it to existing type. It cannot be added to existing runtimes that have the type already.

Couldn't that be the name then, 'IsTrackedByGC()'?

@mattwarren I had similar observation earlier - my suggestion was ContainsGCPointers<T>(). Check the discussion about it at https://github.com/dotnet/coreclr/issues/1135#issuecomment-262616750.

@nietras
Copy link
Contributor

nietras commented Nov 30, 2016

It is consequence of adding it to existing type

@jkotas would it not make sense to add it somewhere else then? Not that I couldn't reimplement it in the places I would like to use it, but it would be good to have in a portable way, in the future at least...

@jkotas
Copy link
Member Author

jkotas commented Nov 30, 2016

These methods should come to other runtimes in future, and become part of the portable set that you can depend on.

There is a challenge with adding one-off methods, and making them available on existing runtimes: We would need to create a new type and likely even a new assembly for it. This approach is ok for large chunks like ValueType or Span<T>, but it does not scale for one-off methods. We would end up with a lot of very similar types that have just a few methods on them over time. Copy&paste the implementation to make it work on older runtimes is probably the best of the bad choices. Or having it in a small 3rd party nuget package that is not part of the platform.

@karelz
Copy link
Member

karelz commented Dec 6, 2016

API triage: Ideally we would like to see this method as internal. Span might need it public, we should just wait for its design to settle a bit more, then we can review it again.

@jkotas
Copy link
Member Author

jkotas commented Dec 6, 2016

Span might need it public

Span does not need it public. It is needed as public for everything else that can take advantage of it, like the optimization in generic collections mentioned at the top of this issue.

This API exits as internal in both CoreCLR and CoreFX already (under different names):

@karelz
Copy link
Member

karelz commented Dec 6, 2016

@jkotas you should come next week to API review meeting to close on this.

@terrajobst
Copy link
Member

API looks fine. We're not concerned about the language design because they are likely going down a different path. We also considered adding an overload taking Type but concluded we don't need it yet.

jkotas referenced this issue in jkotas/corefx Feb 12, 2017
- Rename IsReferenceFree to IsReferenceOrContainsReferences for consistency with #14047
- Remove IsReferenceFreeCore micro-optimization that is actually hurting. This code runs just once and
it is cheaper to do a check via reflection than to spend time in the JIT to optimize the extra code.
- Re-enabled disables test
jkotas referenced this issue in jkotas/coreclr Feb 12, 2017
Rename JitHelpers.ContainsReferences<T>() to RuntimeHelpers.IsReferenceOrContainsReferences<T>() and make it public.

Work towards https://github.com/dotnet/corefx/issues/14047
jkotas referenced this issue in dotnet/corefx Feb 12, 2017
- Rename IsReferenceFree to IsReferenceOrContainsReferences for consistency with #14047
- Remove IsReferenceFreeCore micro-optimization that is actually hurting. This code runs just once and
it is cheaper to do a check via reflection than to spend time in the JIT to optimize the extra code.
- Re-enabled disables test
jkotas referenced this issue in jkotas/coreclr Feb 12, 2017
Rename JitHelpers.ContainsReferences<T>() to RuntimeHelpers.IsReferenceOrContainsReferences<T>() and make it public.

Work towards https://github.com/dotnet/corefx/issues/14047
jkotas referenced this issue in dotnet/coreclr Feb 13, 2017
Rename JitHelpers.ContainsReferences<T>() to RuntimeHelpers.IsReferenceOrContainsReferences<T>() and make it public.

Work towards https://github.com/dotnet/corefx/issues/14047
jkotas referenced this issue in dotnet/corert Feb 13, 2017
@jamesqo
Copy link
Contributor

jamesqo commented Feb 16, 2017

Should this issue be closed?

@jkotas
Copy link
Member Author

jkotas commented Feb 16, 2017

It was not exposed in the contracts yet.

jorive referenced this issue in guhuro/coreclr May 4, 2017
Rename JitHelpers.ContainsReferences<T>() to RuntimeHelpers.IsReferenceOrContainsReferences<T>() and make it public.

Work towards https://github.com/dotnet/corefx/issues/14047
@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.0.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Runtime help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

10 participants