Skip to content

Adding public API for Pinned Object Heap allocations #33526

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

Merged
13 commits merged into from
Mar 18, 2020
Merged

Conversation

VSadov
Copy link
Member

@VSadov VSadov commented Mar 12, 2020

Exposing the following public API:

System.GC :
        public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false);
        public static T[] AllocateArray<T>(int length, bool pinned = false);

@AaronRobinsonMSFT
Copy link
Member

Did these APIs go through API review?

@VSadov
Copy link
Member Author

VSadov commented Mar 14, 2020

@AaronRobinsonMSFT - yes, twice. Once - long time ago. And again in november, to make sure and confirm.

@VSadov VSadov changed the title [WIP] Adding API for POH allocations Adding public API for Pinned Object Heap allocations Mar 15, 2020
@VSadov VSadov requested review from Maoni0 and jkotas March 15, 2020 05:11
@VSadov VSadov marked this pull request as ready for review March 15, 2020 05:11
@VSadov
Copy link
Member Author

VSadov commented Mar 15, 2020

I think the change is ready for review. Please take a look.

/// otherwise it's not pinned.
/// pinned requires that T does not contain object references.
/// </summary>
public static T[] AllocateArray<T>(int length, bool pinned = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't be better to have this as AllocatePinnedArray<T> only? What is the difference between new object[10] and AllocateArray<object>(10) ?

Copy link
Member Author

@VSadov VSadov Mar 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are plans to add more overloads to this API, possibly to control target generation or alignment.

Indeed, both times when this was reviewed, it was noticed that the trivial case is the same as 'new T[]`, but it seems to be ok and happens with other APIs that have optional parameters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to me that every time someone uses this API one has to type the explicit parameter to make the code readable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect that this API is going to be used very rarely. The more useful API build on top this is going to be a pool for the pinned arrays. The API for that was not designed yet. @VSadov Is it in your plans - what do you expect the pool API to be?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguments for going it this way were convincing. When there are more arguments, a new overload will be introduced with 2 optional parameters, while this one becomes not optional and so on...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the pool - I think from the usability point, it might be preferable to just add GetPinned to the current pool API.
Alternatively we could have a separate pool for pinned arrays, but then it puts a burden on the user to remember where to return.

@stephentoub
Copy link
Member

stephentoub commented Mar 16, 2020

@VSadov, can you link to the relevant API review issue? I see #27146 (comment), which has a different shape than is implemented here. Is that comment out-of-date and there was a newer discussion?

}
#endif

// kept outside of the small arrays hot path to have inlining without big size growth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the above else if block then also be moved into the AllocateNewUninitializedArray method, to keep it out of the inlinling path?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shape of this method was tweaked to make the case when we fallback to T[] fast. That is only relevant if pinned == false. (example: look at use in StringBuilder).
I expect that this entire block will be omitted in such case. That is assuming the method does get inlined, which it should.


In reply to: 392757230 [](ancestors = 392757230)

@@ -653,29 +661,63 @@ internal static void UnregisterMemoryLoadChangeNotification(Action notification)
/// <summary>
/// Skips zero-initialization of the array if possible.
/// If T contains object references, the array is always zero-initialized.
/// pinned: true means you want to pin this object that you are allocating
/// otherwise it's not pinned.
/// pinned requires that T does not contain object references.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the additional checks around the pinned argument get removed when the caller uses a const value at the call site / the default argument value, or might the optional parameter end up impacting the perf of existing call sites?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if AggressiveInlining works, then the pinned should be const-propagated and related blocks omitted. I did not check in debugger, but this seems a very easy case.


In reply to: 392758813 [](ancestors = 392758813)

@VSadov
Copy link
Member Author

VSadov commented Mar 16, 2020

@stephentoub - the goal is to have API as in: #27146 (comment) .

The main difference is that we are not doing all the features/parameters at once.
As more features are added we will be adding new overloads and making parameters in previous version not optional - for compat/versioning reasons.

@VSadov
Copy link
Member Author

VSadov commented Mar 16, 2020

Refactored AllocateArray as @stephentoub suggested

@VSadov
Copy link
Member Author

VSadov commented Mar 17, 2020

Any more comments on this?

@Maoni0
Copy link
Member

Maoni0 commented Mar 17, 2020

since you make the AllocateUninitializedArray public with this PR, please remember to have a doc PR to document it.

@VSadov
Copy link
Member Author

VSadov commented Mar 17, 2020

Yes. Need to update docs. I've entered a tracking issue for that - #33685

@@ -149,33 +149,17 @@ class Object
m_pMethTab = pMT;
}

VOID SetMethodTable(MethodTable *pMT
DEBUG_ARG(BOOL bAllowArray = FALSE))
VOID SetMethodTable(MethodTable *pMT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VOID SetMethodTable(MethodTable *pMT) [](start = 4, length = 37)

why get rid of the debug arg? its existence seems very intentional.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a remnant from the times when array's method tables could be shared for arrays of reference type. It was important to not call this API by accident.
There is no sharing any more and we use the same API, regardless of the type of the object and whether it is an array or not. The check was still there, but not validating or protecting anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see; makes sense

Copy link
Member

@Maoni0 Maoni0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@VSadov
Copy link
Member Author

VSadov commented Mar 18, 2020

Thanks!!

@VSadov VSadov removed the auto-merge label Mar 18, 2020
@ghost
Copy link

ghost commented Mar 18, 2020

Hello @VSadov!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@VSadov
Copy link
Member Author

VSadov commented Mar 18, 2020

formatting job was failing due to #33693

@ghost ghost merged commit c0ddd1c into dotnet:master Mar 18, 2020
@VSadov VSadov deleted the pohApi branch March 18, 2020 05:01
@ghost ghost locked as resolved and limited conversation to collaborators Dec 10, 2020
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants