Add ExtractToImmutable#180
Conversation
|
Regarding your addition of the |
|
@AArnott I do think having an API which is dedicated to the no copy pattern is worthwhile. It's a decidedly different pattern than a dynamic builder and as a result has different creation semantics. I disagree that it would introduce bugs, I think it is far more likely to prevent them. Given only the APIs of today I think the following pattern will be a potential pitfall for anyone who first tries a no copy approach. var builder = ImmutableArray.CreateBuilder<string>(2);
builder[0] = "a"; // Throws because capacity != count That being said I'm fine with removing it for now and looking at adding it back at a later time. It's a purely additive change and hence easy to add later if we agree it needs to happen. |
|
Thanks for being fine to remove it. If you want to send another pull request after this one is taken that adds |
|
👍 Looks good to me. Hopefully the API review process will be much quicker on this pull request than your last since the API impact is much smaller. |
There was a problem hiding this comment.
IIRC, these Count > 1 checks were previously added to avoid allocating the Comparer wrapper. Now that the wrapper isn't needed, I wonder if the checks are still beneficial.
|
👍 Overall, looks good to me. I'd like to get a bit more API reviewer's eyes on this before merging. |
There was a problem hiding this comment.
I would just call it reverse to be consistent with List.
This is an alternate implementation idea for having a zero copy ImmutableArray<T> construction mechanism. This reuses the existing builder and adds an ExtractToImmutable method which wraps the internal array with an ImmutableArray<T> and replaces it with an empty array. As a part of this change the RefAsValueType optimization is removed from the Builder. It's a requirement to have such a method because ImmutableArray<T> wraps a T[] not a RefAsValueType<T>[]
a7aa64d to
f51ba88
Compare
As per the results of the design review this changes the Builder API around extraction of immutable arrays in the following ways: 1. Renamed method to MoveToImmutable 2. Throw when Count != Capacity in that method to avoid potential user errors around extraction.
As discussed making two minor changes to make Builder line up closer to List<T> - Capacity now has a get and set. - Make EnsureCapacity a private method. It is superfluous in the public API with the addition of a set accessor on Capacity.
|
Can one of the admins verify this patch? |
|
Thanks @jaradpar :) In e034c93 you add the check to MoveToImmutable that throws an InvalidOperationException if Count is not equal to Capacity. I believe Immo is going to add more details about the convention we're advising for this method to the API review. Since this is a potentially risky app-compat burden and potentially easy to break, may I recommend some tests around the positive and negative case there? I think it would make sense to test: |
|
The API review results are now available here. |
|
@FiveTimesTheFun most of those tests already exist. I think I'm missing Remove and possibly AddRange but the others should be there. Will add those. |
|
ok to test |
There was a problem hiding this comment.
Can you please explicitly type temp here to T[]?
|
@FiveTimesTheFun and @weshaggard Latest contains the requested changes. |
|
@jaredpar I'm interested in your feedback regarding the following usability improvement. How difficult would it be to implement the following after adding The
|
|
We discussed a copy on write design which optimized
The builder is meant to be a lean type which is efficient as possible for building up The good news though is that if you want a copy on write design, it can easily be built on top of the builder as is. |
|
I strongly opposed having If you really want these semantics though, and we agreed in design that it's possible customers would, it can easily be added on top of the change with an extension method. static ImmutableArray<T> ToImmutableEfficient(this ImmutableArray<T>.Builder builder)
{
if (builder.Count == builder.Capacity)
{
return builder.MoveToImmutable();
}
else
{
var array = builder.ToImmutable();
builder.Clear();
return array;
}
} |
|
Edit: This was a reply to the message about optimizing @jaredpar That makes sense. How do you feel about the following?
This would provide the performance of |
|
This solution still allocates memory in the cases where Note: In general I perfer the terminology |
|
@jaredpar I believe that |
|
In general I prefer core types like I do agree that some customers will have use cases that don't align with the specific goals here. But by providing the low level methods we give them the tools to build the solutions they want. It also shifts the cost of these solutions, even if minor, to the developers actually using them. |
|
@jaredpar You reply too fast. ⏩ For code like the .NET Compiler Platform and within the .NET Core, it's clear why you would want the code to throw early for this type of performance regression. However, for other applications where memory allocations are not the primary concern, the usability characteristics of the current implementation seem unfavorable. A more friendly approach would be providing public static ImmutableArray<T> MoveToImmutable(this ImmutableArray<T>.Builder builder)
{
if (builder.Count < builder.Capacity)
throw ...
return builder.ToImmutableAndReset();
}Summary: I'm not convinced that the driving concern for the current semantics when |
|
BTW, I believe the posts above represent my complete thoughts on the matter, so I won't be going back-and-forth on this forever. 😜 I do believe the discussion is beneficial for future readers looking at this. 👍 |
|
@sharwell One last little thought 😄 I agree that both of these methods do make sense and that one is naturally implemented in terms of the other. However I would do it in the opposite direction: implement The reason why is Implementing it in terms of On the other hand |
There was a problem hiding this comment.
Why not just use the following for this block? EDIT: This is suboptimal
if (value > 0)
{
Array.Resize(ref _elements, value);
}|
Anything blocking merge? |
|
LGTM. |
|
@jaredpar No blockers from me. |
…o as it's CoreRT change only (dotnet#180) Relates to mono/mono#11577
This is an API which allows for a zero-copy creation of an
ImmutableArray<T>value.This is an alternative approach to the PR #154EDITS