-
Notifications
You must be signed in to change notification settings - Fork 286
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
Perf | Port PR #328 to improve performance in .NET Framework #1084
Conversation
Are you porting from the #328 diffs or from the existing netcore code? I'd advise using the existing netcore code if possible because some changes were made after #328 which improve performance, like moving the cached instances down to the sql internal connection. There is also #925 open which is worth looking at in case you want to do the same in netfx at the same time. |
Only the #328 diffs here, I see many more differences in async design which I will target to do later, thanks for checking in! I tried doing that too but ended up with a very large change-set. I'll try to bring few at a time so we can acknowledge all differences in portions and eventually merge these files (optimism 😌). The PR #328 brought perf benefits in 'NextResultAsync' flow and it's a considerably major improvement to port. |
Ok, the only really important thing is to make sure you port over #603 which fixed an error in this rework, everything else is just additional improvements. |
Thanks, I was stuck on exactly that test! |
You might want to include #925 (which I think is the source of conflicts). When the two versions of the Async context classes are the same I can put together a merge PR to share some of those files. |
Thanks @Wraith2
I think we should plan bringing Async design to parity after this PR gets fixed and merged. |
…328-netfx # Conflicts: # src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
What needs fixing? |
# Conflicts: # src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
The test "AsyncMultiPacketStreamRead" is giving me hard time as it was brought in with #603, but even if netcore changes from #603 were ported to NetFx, the test continues to fail. I'm looking at my last resort to do all the work from #925, #528, #499 in NetFx to bring code to parity, but do you know if there's anything else that NetFx is missing that might be contributing to this test failure? |
I looked through the history when I saw you open the PR which is when I flagged up #925 . Nothing else stood out is being needed. I'll reimplement this changeset on my local copy and see if I can reproduce the error and possibly help track it down. |
How are you running the net461 tests? when i try i get this error:
which is right that the deps file doesn't exist but then I don't think it's needed on netfx so I'm not sure what is looking for it. |
Run Manual Tests I could debug in Visual Studio, so maybe try again with a "Rebuild Solution" |
Tried that. Then I closed VS and did |
This set of commands should work to build and run the tests from what i can see in the docs:
but fails with:
So i don't know what's different between our setups but I'm using a clean checkout from main and things just don't work properly. If I can get to the point of being able to compile and debug the netfx tests then I'll be able to help but at the moment that isn't possible. |
I managed to get the test case working using Debugger.Launch(). netfx is failing to move from seek mode into read mode in the get bytes async call. It does this because the
you'll find that it is called very quickly on netfx and only when the operations has finished on netcore. We need to know why the Task that is being used by CopyStream from the stream reader is signalling completion early on netfx. Got to do my day job at the moment but I thought I'd leave these notes here so when either of us get back to it we have a place to start. |
Got it: In this block in Task<int> retryTask = ExecuteAsyncCall(context);
if (isContinuation)
{
// Let the caller handle cleanup\completing
return retryTask;
}
else
{
Debug.Assert(context._source != null, "context._source shuld not be null when continuing");
// setup for cleanup\completing
retryTask.ContinueWith(
continuationAction: AAsyncCallContext<int>.s_completeCallback,
state: context,
TaskScheduler.Default
);
+ return source.Task;
} Without it the first packet crossing read doesn't get waited for. It isn't waited for because falling out of that block returns null at the bottom of the method and that is treated as a synchronous completion by the caller. So you end up in a state where the caller thinks the call completed and returned 22 bytes and the call is actually still being worked on internally so when the caller then tries to initiate another call it hits the existing one and trips the exception. Not fun to debug. |
I would have liked a compile error like "Not all paths return value" but we return null in the end. 😞 |
Can you consider changing the test from using stream.CopyToAsync to a local version of the function? To debug I needed to lookup the implementation of CopyToAsync so I could single step through and add tracing. When I did that I found that it's is using I'd suggest something like the following which respect the buffersize: #if NETFRAMEWORK
private static async Task LocalCopyTo(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer,0, bufferSize, cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
#else
private static async Task LocalCopyTo(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
int bytesRead;
while ((bytesRead = await source.ReadAsync(new Memory<byte>(buffer,0, bufferSize), cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
#endif |
src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
Show resolved
Hide resolved
src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
Show resolved
Hide resolved
Co-authored-by: DavoudEshtehari <[email protected]>
@DavoudEshtehari I wouldn't worry too much about documentation at this point. Most of the async support functions are going to change names when the later PR's from netcore are ported on top of this. Once netfx and netcore are in sync I intend to share a lot of the code like the async call context classes between the projects to help keep everything in sync. |
+#603