Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

Use System.Buffers + ValueTask in FormReading #556

Closed
wants to merge 4 commits into from

Conversation

benaadams
Copy link
Contributor

Built on #550
Addressing Task allocations in #553 (last commit in this PR)

ValueTask reduces Task allocation by FormReader to 1%
Post for 30,000 requests - byte[] allocations are as follows 0.5MB on rent vs 440MB on MemoryStream originally

/cc @rynowak @davidfowl @Tratcher @stephentoub

@stephentoub
Copy link

This is because ValueTask isn't supported in net451

Why? The main point of putting it in a new assembly was so it would be usable there:
https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj#L9

@benaadams
Copy link
Contributor Author

This is because ValueTask isn't supported in net451

Why? The main point of putting it in a new assembly was so it would be usable there:

Entirely my fault as I couldn't follow instructions; when it said it needed "System.Threading.Tasks": "4.0.0.0" it literally meant exactly that, not "System.Threading.Tasks": "4.0.0-*"

@benaadams benaadams force-pushed the bufferpool-valuetask branch 2 times, most recently from 8d79967 to 88ec9fa Compare February 11, 2016 10:18
@benaadams benaadams mentioned this pull request Feb 11, 2016
12 tasks
@benaadams
Copy link
Contributor Author

Work done

Current

current

Buffers + ValueTask

valuetask

@benaadams
Copy link
Contributor Author

benaadams commented Feb 11, 2016

ValueTask reduces Task allocation by FormReader to 1% which continues to be amortised over time downwards #553 (comment)

Revisiting the original allocation issue from 30,000 requests (x10 on original)

task-allocations

ValueTask dramatically reduces the Task allocations to around 1% of the current (7MB vs 750MB)

@benaadams
Copy link
Contributor Author

Pre-for 3,000 requests 44MB on MemoryStream

pre-alloc

Post for 30,000 requests - byte[] allocations are as follows 0.5MB on rent vs 440MB on MemoryStream originally

allocs

@rynowak
Copy link
Member

rynowak commented Feb 11, 2016

Nice 👍

@benaadams
Copy link
Contributor Author

Rebased; removed WebEncoders changes as its changed somewhat.

@sajayantony
Copy link
Member

Sweet 👍 /cc @DavidObando @mnltejaswini

@DavidObando
Copy link
Member

Love this.

@@ -43,6 +56,17 @@ public class FileBufferingReadStream : Stream
throw new ArgumentNullException(nameof(tempFileDirectoryAccessor));
}

_bytePool = bytePool;
if (memoryThreshold < 1024 * 1024)
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to put this in a constant.

@benaadams benaadams force-pushed the bufferpool-valuetask branch 2 times, most recently from 0a5c6b9 to f270c45 Compare March 7, 2016 22:08
@benaadams
Copy link
Contributor Author

Rebased, somewhat squashed, feedback changes made

using (reader)
{
var pair = await pairTask;
while (pair.HasValue)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

how does this work? Am I missing something that way over my head?

@rynowak do you mean the splitting of the async version from the non-async version (until it becomes async) or the end of read being indicated by a null?

Copy link
Member

Choose a reason for hiding this comment

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

I just hadn't read these two lines super well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I imagine a golden time in the future when the compiler automatically does these patterns for you...

@benaadams benaadams changed the title Use ValueTask in FormReading Use ValueTask in FormReading + System.Buffers Mar 8, 2016
@benaadams benaadams changed the title Use ValueTask in FormReading + System.Buffers Use System.Buffers + ValueTask in FormReading Mar 8, 2016
@davidfowl
Copy link
Member

@benaadams Can you rebase?

@benaadams
Copy link
Contributor Author

Rebased; note: I had to bump src/Microsoft.Net.Http.Headers/project.json from "netstandard1.0" to "netstandard1.1"

},
"frameworks": {
"net451": {
"frameworkAssemblies": {
"System.Runtime": { "type": "build" }
"System.Runtime": { "type": "build" },
"System.Threading.Tasks": "4.0.0.0"
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be type build like the other facades

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// Drain until close received
await ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
}
}
Copy link

Choose a reason for hiding this comment

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

Would this ever actually throw?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Potentially, could throw if socket already disconnected or cancellation token cancelled.

@Tratcher
Copy link
Member

This ValueTask pattern causes substantial code duplication and maintenance issues. I don't think it's piratical. Can we split this and just take the buffer pooling?

@Tratcher Tratcher self-assigned this Mar 22, 2016
@benaadams
Copy link
Contributor Author

This ValueTask pattern causes substantial code duplication and maintenance issues. I don't think it's piratical.

The ValueTask does cut the allocations on 30k requests from 750MB to 7MB though

/cc @rynowak

@Tratcher
Copy link
Member

The buffer pooling is ready to merge and unrelated to the ValueTask work. Let's get that merged.

We need to come up with a simpler pattern if we're going to use ValueTasks.

@benaadams
Copy link
Contributor Author

K, will split them later today

@benaadams
Copy link
Contributor Author

We need to come up with a simpler pattern if we're going to use ValueTasks.

Know what you mean, there are a bunch of higher performance patterns with async that do make the code more confusing as you need to split the code into multiple fragments.

I wonder if there is scope to push this into the compiler instead? As it already does that for general async.

Would rosyln be the correct repo to raise this? /cc @stephentoub

@stephentoub
Copy link

Would roslyn be the correct repo to raise this?

dotnet/roslyn#7169

@Tratcher
Copy link
Member

I recommend we close this pending the roslyn discussion. @benaadams @rynowak?

@rynowak
Copy link
Member

rynowak commented Mar 30, 2016

That seems fine, I don't think we're going to take this PR in it's current form

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.

None yet

9 participants