Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

OwnedMemory tidy and performance testing #1116

Merged
merged 7 commits into from
Jan 19, 2017

Conversation

mjp41
Copy link
Member

@mjp41 mjp41 commented Jan 12, 2017

Main changes

  • Made Reference Count value not directly accessible, only whether it is zero or not.
  • Added a setting for whether reservations should use:
    • Interlocked Reference Counting (slow),
    • Reference Counter (almost safe), or
    • Nothing (unsafe).
  • Added a simple performance test for reservations that does parameter sweep.

Minor tidying to

  • Remove unused Reserve method.
  • Make DisposableReservations be able to return the Span, as in the design doc.

@mjp41
Copy link
Member Author

mjp41 commented Jan 12, 2017

@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);
Copy link
Member

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).

Copy link
Member

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?

Copy link
Member

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

Copy link
Member Author

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?

Copy link
Member

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.

Copy link
Member

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);
Copy link
Member

@KrzysztofCwalina KrzysztofCwalina Jan 12, 2017

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.

Copy link
Member Author

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.

Copy link
Member

@KrzysztofCwalina KrzysztofCwalina Jan 13, 2017

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;
Copy link
Member

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};
Copy link
Member

@KrzysztofCwalina KrzysztofCwalina Jan 12, 2017

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]
Copy link
Member

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);
Copy link
Member

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;
Copy link
Member

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.

Copy link
Member Author

@mjp41 mjp41 Jan 16, 2017

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?

Copy link
Member

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.
@KrzysztofCwalina
Copy link
Member

@dotnet-bot test this please

@KrzysztofCwalina
Copy link
Member

BTW, let's merge it and then do the renames later.

@KrzysztofCwalina KrzysztofCwalina merged commit 26903ca into dotnet:master Jan 19, 2017
@mjp41
Copy link
Member Author

mjp41 commented Jan 19, 2017

Sounds good. Thanks for the comments @KrzysztofCwalina and @benaadams.

@mjp41 mjp41 deleted the ReservationPerf branch January 19, 2017 08:29
ahsonkhan pushed a commit to ahsonkhan/corefxlab that referenced this pull request Jan 28, 2017
* 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.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants