-
Notifications
You must be signed in to change notification settings - Fork 346
OwnedMemory tidy and performance testing #1116
Conversation
@KrzysztofCwalina I made the System.Slices.Tests part of the performance harness. Should I create a separate project for System.Slices performance? @davidfowl I removed direct access to an OwnedMemory's reference count, and instead provide a method for checking if it has no references. This seemed to fit with your use case in the Pipelines. |
@@ -85,11 +85,11 @@ public BufferSegment(OwnedMemory<byte> buffer, int start, int end) | |||
|
|||
public void Dispose() | |||
{ | |||
Debug.Assert(_buffer.ReferenceCount >= 1); | |||
Debug.Assert(!_buffer.HasZeroReferences); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we make it to be HasReferences? HasZeroReferences when prefixed with '!' is hard to parse (double negation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be confusion with ContainsReferences
and ilk? Perhaps HasOutstandingReferences
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or better keep it in sync with usage HasReservations
or change DisposableReservation
to DisposableReference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DisposableReference
and HasOutstandingReferences
makes the most sense. As AddReference
exists on the API surface as well.
Would AutoReference
be better than DisposableReference
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I named it DisposableReference to highlight to users that they need to dispose it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AutoReference
sounds like it deals with disposal for you; would need something like "Destructible Types" dotnet/roslyn#161 for an auto
@@ -55,7 +55,7 @@ public Memory<T> Slice(int index, int length) | |||
|
|||
public Span<T> Span => _owner.GetSpanInternal(_id).Slice(_index, _length); | |||
|
|||
public DisposableReservation Reserve() => new DisposableReservation(_owner, _id); | |||
public DisposableReservation<T> Reserve() => new DisposableReservation<T>(_owner, _id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used to have this type as a generic type and it was a problem. Sometimes you want to store many reservations on a collection, and generic reservations mean you need many different types of collections. And more generally, the generic argument becomes viral.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if DisposableReservation is non-generic then it can't give you access to the Span directly. Could you elaborate a little on the use case, as storing reservations in a collection to me sounds dangerous, particularly with the ReferenceCounter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was that the pipeline would store tokens for all buffer segments,a nd then dispose them all when it's done with the buffer. But I just realized in such scenario, all tokens will be Disposable.
Let's just make (keep) it generic then and see how it feels to use it.
public enum Mode {RefCount, LocalRefCount, None}; | ||
|
||
public class OwnedMemorySettings { | ||
public static Mode s_mode = Mode.RefCount; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you make DisposableReservation non-generic, you could just add this to DisposableReservation, so there is no need for a new type. I know this type is probably just temporary (for experiments), but we try to minimize number of types in our APIs.
Also, s_mode does not follow our naming guidelines. Name it 'Mode'
@@ -4,6 +4,12 @@ | |||
|
|||
namespace System.Buffers | |||
{ | |||
public enum Mode {RefCount, LocalRefCount, None}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call these
public enum ReferenceCountingMethod {
Interlocked,
ReferenceCounter,
None
}
|
||
|
||
[MemberData(nameof(ReservationPerformanceData))] | ||
[Benchmark] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re your question about the project for the tests:
I think it's ok for these tests to be here or in the Benchmarks project. I would not create a separate test project for these.
_owner.AddReference(_id); | ||
switch(OwnedMemorySettings.s_mode) { | ||
case Mode.RefCount: | ||
((IKnown)_owner).AddReference(_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you keep the reservation generic, you don't need the cast here.
} | ||
|
||
public Span<T> Span => _owner.Span; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I just noticed this: Could you at least for now not remove this but instead rename it to DangerousGetSpan().
Even if we settle on refcounting option being the default, we want to have the ability to get span without refcounting in cases where the caller knows that it is safe, e.g. the caller owns the memory lifetime or it is GCed memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity which paths are considered Dangerous? For instance, I think Memory.Span is going to be Dangerous, should that also be renamed?
To me only
using(var res = Memory<T>.Reserve()) {
var s = res.Span;
...
}
Is the only way to get a Span from a Memory that doesn't have Dangerous
in the external API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we wanted to do the same rename on Memory
Do not allow direct access to the reference count, but instead query if it is zero or not. This is a precursor to allowing more efficient reference counting schemes.
This is no longer required according to the discussion in dotnet#952
We need DisposableReservations to be able to return Spans, so we can scope the access of the span.
There is no instance state for the ReferenceCounter, so make all its methods static.
To enable testing different strategies for managing reservations, introduce a static variable that determines how reservations should be checked.
Added a simple performance test that allows a simple parameter sweep for number of threads, objects, and amount of work. This is not a very realistic example as it does not do the pipelining that is the key use case currently.
b9d9fbc
to
32d79db
Compare
@dotnet-bot test this please |
BTW, let's merge it and then do the renames later. |
Sounds good. Thanks for the comments @KrzysztofCwalina and @benaadams. |
* Remove access to Reference Count Do not allow direct access to the reference count, but instead query if it is zero or not. This is a precursor to allowing more efficient reference counting schemes. * Removed Reserve with ReadOnlyMemory ref This is no longer required according to the discussion in dotnet#952 * Made DisposableReservation return Spans We need DisposableReservations to be able to return Spans, so we can scope the access of the span. * Split disposal from pooling in Memory test. * Made ReferenceCounter methods static There is no instance state for the ReferenceCounter, so make all its methods static. * Enable switching modes for Reservations To enable testing different strategies for managing reservations, introduce a static variable that determines how reservations should be checked. * Added performance test for Reservations Added a simple performance test that allows a simple parameter sweep for number of threads, objects, and amount of work. This is not a very realistic example as it does not do the pipelining that is the key use case currently.
Main changes
Minor tidying to