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

Proposal: MemoryPool.RentExact() returns IMemoryOwner with pre-sliced Memory #30165

Open
shutdown256 opened this issue Jul 6, 2019 · 9 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Memory backlog-cleanup-candidate An inactive issue that has been marked for automated closure. no-recent-activity
Milestone

Comments

@shutdown256
Copy link
Contributor

I found it really disappointing that MemoryPool.Rent behaves as ArrayPool and can return Memory that is larger then what I want - it makes sense for arrays but Memory could be easily pre-sliced to required size - this will most importantly allow passing this IMemoryOwner to outside code without the need to pass along another variable indicating the actual length of the data and slicing the underling memory every time you want to access it. This could even allow to have more efficient MemoryPool implementation which could rent several IMemoryOwners from the same block of memory. It would also be a lot simpler to implement then #27869 (but it is not mutually exclusive).

@benaadams
Copy link
Member

/cc @GrabYourPitchforks

@GrabYourPitchforks
Copy link
Member

I'm a little confused. If you already have a variable for "actual length of the data", then doesn't that mean that you already have data in some Memory<T>, and you could pass that instance down to the code you're calling? MemoryPool, just like ArrayPool, returns a scratch buffer with no existing data inside it, so I'm having trouble wrapping my mind around what "actual length of the data" means in this context.

@benaadams
Copy link
Member

benaadams commented Jul 11, 2019

I assume it means; ArrayPool returns the requested size or larger; however since MemoryPool returns Memory<T> it could slice prior to the return to always return the requested size (rather than "or greater")

@shutdown256
Copy link
Contributor Author

@GrabYourPitchforks in my case I have a PipeReader and I know that I will read n continuous bytes then pass them upstream for further processing.

@FiniteReality
Copy link

I'd also like something like this. In my case, I'm reading a length-prefixed string from a SequenceReader, which is passed elsewhere for processing.
I'd really like if I could pool the memory and pass around ownership using IMemoryOwner<T>, but since the Memory<T> instance it contains is the length of the buffer returned, rather than the requested length, I'd have to pass around an extra int indicating the real length of the Memory<T> instance, which feels counter-intuitive to me, considering one of the major points behind Memory<T> and Span<T> is to avoid the whole mess of offset, length parameters we had with arrays.

@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@stephentoub stephentoub modified the milestones: 5.0, Future Feb 15, 2020
@john-h-k
Copy link
Contributor

john-h-k commented May 6, 2020

Yes please - it is so so so frustrating having to pass around extra data, half the point of memory is not having to do that :|

@GrabYourPitchforks
Copy link
Member

I looked through #27869 but I don't see how the RentExact API would help that scenario. Even if the initial call to IMemoryOwner<T>.Memory returns an exactly-sized buffer, the natural way of using that Memory<T> will still result in its being sliced. The particular example at #27869 (comment) discusses calling span-aware APIs to populate the buffer, then slicing the buffer to the actual number of elements written. So in this scenario you'd still need two locals hanging around: the original IMemoryOwner<T> (for disposal), and the sliced Memory<T> that holds your current data.

What am I missing? Is there a code sample that clearly shows the benefit of having this new proposed API?

@bjornen77
Copy link
Contributor

I also think the RentExact API would be a good addition. Also with an optional boolean parameter(if the returned pre-sliced memory should be cleared when disposed, only the pre-sliced range should be cleared)

Lets say you have a complete json object in a ReadOnlySequence. To parse this you could do something like this:

Code just for example:

    private void DoSomething(ReadOnlySequence<byte> sequence)
    {

        if (sequence.IsSingleSegment)
        {
            // Here you get the memory without need for slicing
            ReadOnlyMemory<byte> buffer = sequence.First;

            // Do something with buffer multiple times.
            // LogMessage(UTF8Encoding.UTF8.GetString(buffer.Span));
            // using var doc = JsonDocument.Parse(buffer);
            // Use the JsonDocument.
        }
        else
        {
            // Here you could rent some memory to copy the complete sequence to:
            int length = checked((int)sequence.Length);
            using IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent(length);

            // The rented buffer is probably larger than we requested, slice it first.
            Memory<byte> slicedBuffer = memoryOwner.Memory.Slice(0, length);

            try
            {
                sequence.CopyTo(slicedBuffer.Span);

                // Do something with the sliced buffer multiple times...
                // LogMessage(UTF8Encoding.UTF8.GetString(slicedBuffer.Span));
                // using var doc = JsonDocument.Parse(slicedBuffer);
                // use the JsonDocument... 
            }
            finally
            {
                // Perhaps you want to clear rented memory before returning to pool.
                // this should be handled by an optional parameter in RentExact method instead.
                slicedBuffer.Span.Clear();
            }
        }
    }

If the optional clearOnDispose parameter on RentExact is available the last try/finally would be unnecessary.

Same sample with RentExact API with clearOnDispose option.

    private void DoSomething(ReadOnlySequence<byte> sequence)
    {

        if (sequence.IsSingleSegment)
        {
            // Here you get the memory without need for slicing
            ReadOnlyMemory<byte> buffer = sequence.First;

            // Do something with buffer multiple times.
            // LogMessage(UTF8Encoding.UTF8.GetString(buffer.Span));
            // using var doc = JsonDocument.Parse(buffer);
            // Use the JsonDocument.
        }
        else
        {
            // Here you could rent some memory to copy the complete sequence to:
            int length = checked((int)sequence.Length);
            using IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.RentExact(length, clearOnDispose: true);

            sequence.CopyTo(memoryOwner.Memory.Span);

            // Do something with the sliced buffer multiple times...
            // LogMessage(UTF8Encoding.UTF8.GetString(memoryOwner.Memory.Span));
            //using var doc = JsonDocument.Parse(memoryOwner.Memory);
            // use the JsonDocument... 
        }
    }

Copy link
Contributor

Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.

This process is part of our issue cleanup automation.

@dotnet-policy-service dotnet-policy-service bot added backlog-cleanup-candidate An inactive issue that has been marked for automated closure. no-recent-activity labels Dec 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Memory backlog-cleanup-candidate An inactive issue that has been marked for automated closure. no-recent-activity
Projects
None yet
Development

No branches or pull requests

8 participants