Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Conversation

@Wraith2
Copy link
Contributor

@Wraith2 Wraith2 commented Dec 12, 2018

When writing some primitive types to the tds packet stream small arrays are allocated and passed to the WriteByteArray function. I have changed WriteByteArray to WriteBytes and added a span parameter so that either a span or an array can be passed in (but not both, the priority is explained in the comments). The function will attempt to write using only a span unless it encounters an end of packet and needs to use an async continuation, in that case it will allocate an array and copy the remainder of the span into it. For primitive types this will rarely be needed.

These changes can be used to allow the new BitConverter.TryWriteBytes overload with stackalloced buffers to remove memory allocations for writing of 16 and 32 bit reals. This can be extended to guids at a later time.

This PR was split from #32811 and into smaller commits for easier review. It has passed the manual and efcore tests in native mode, the tests cannot be successfully run in managed mode due to #33930 . Performance improvements for this and related PR's are in the original discussion.
/cc @keeratsingh @afsanehr @saurabh500 @David-Engel

…ble path with fallback to array on continuation

add WriteByteSpan and WriteByteArray public methods
… which avoid byte array allocation in favour of span
@Wraith2 Wraith2 changed the title Optimize SqlClient primative type writes Optimize SqlClient primitive type writes Dec 12, 2018
{
byte[] tempArray = new byte[len];
Span<byte> copyTempTo = tempArray.AsSpan();
ReadOnlySpan<byte> copyTempFrom = b.Slice(remainder, len);
Copy link
Contributor

Choose a reason for hiding this comment

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

I am confused. Why do you need b.Slice again here? b.Slice(remainder) was already called on line 3062

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. The len needs to be there so that we only copy data not empty buffer in the case where a small part of an array is used (pool rental for example). The remainder should be changed to 0 though. I've changed it and I'm re-running the test suites.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

manual and efcore tests all pass with the push from earlier today.

Copy link
Contributor

@saurabh500 saurabh500 Dec 14, 2018

Choose a reason for hiding this comment

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

Is this testing status for Windows only or both Windows and Linux?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

windows/native, this bit is shared though. I can force it into managed mode and test that way as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK. I will run the Unix validations.

@saurabh500
Copy link
Contributor

@dotnet-bot Test Windows x86 Release Build Please
Failure in System.Runtime.Tests

01:00 != 00:00:00, dn:(UTC+01:00) Casablanca, sn:(UTC+01:00) Casablanca
Expected: True
Actual: False

   at System.Tests.TimeZoneInfoTests.TimeZoneInfo_DisplayNameStartsWithOffset() in D:\j\workspace\windows-TGrou---f8ac6754\src\System.Runtime\tests\System\TimeZoneInfoTests.cs:line 2227

https://mc.dot.net/#/user/Wraith2/pr~2Fjenkins~2Fdotnet~2Fcorefx~2Fmaster~2F/test~2Ffunctional~2Fcli~2F/46d98751a960f8363255da765ff76a58bdba68d9/workItem/System.Runtime.Tests/analysis/xunit/System.Tests.TimeZoneInfoTests~2FTimeZoneInfo_DisplayNameStartsWithOffset

{
byte[] tempArray = new byte[len];
Span<byte> copyTempTo = tempArray.AsSpan();
ReadOnlySpan<byte> copyTempFrom = b.Slice(0, len);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't you just get rid of the call at 3048. It doesn't seem to be doing anything useful. And call b.Slice(remainder, len) like you were doing earlier, at this location? if ( array == null) is the only location where you are going to get the rest of the content of b
Does this make sense ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, there is no guarantee that the WritePacket call will return a task so it's possible to go around the loop without allocating the array. This was a problem that occurred with one of the SplitPacket tests that was pointed out on the original PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, now I understand. In that case, what is the effect of just doing a b = b.Slice(remainder, len) at 3048 and eliminate this slicing at 3066. Would that work? Then this line would read ReadOnlySpan<byte> copyTempFrom = b;

Copy link
Contributor

@saurabh500 saurabh500 left a comment

Choose a reason for hiding this comment

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

Merge pending product team validations.

@saurabh500 saurabh500 added the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Dec 14, 2018
@saurabh500
Copy link
Contributor

@dotnet-bot Test Packaging All Configurations x64 Debug Build please

@saurabh500
Copy link
Contributor

I am facing some issues with EF tests failure with this change. I don't think this PR caused it but I wanna be sure.
I will keep you posted.

@Wraith2
Copy link
Contributor Author

Wraith2 commented Dec 15, 2018

Managed or native? and what are you seeing fail?

@saurabh500
Copy link
Contributor

LGTM. Tested Manual Tests on Ubuntu,
EFCore Ubuntu with MARS enabled
EFCore Ubuntu with Rando, MARS.

@saurabh500 saurabh500 removed the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Dec 17, 2018
@saurabh500 saurabh500 merged commit 245d532 into dotnet:master Dec 17, 2018
@Wraith2 Wraith2 deleted the sqlperf-writespan branch December 17, 2018 22:26
@karelz karelz added this to the 3.0 milestone Dec 21, 2018
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* change public WriteArrayBytes to private WriteBytes and add span capable path with fallback to array on continuation
add WriteByteSpan and WriteByteArray public methods

* add netcoreapp specific implementations of WriteFloat and WriteDouble which avoid byte array allocation in favour of span

* fixup array compy slice to not use remainder as start since it has already advanced

* remove additional span


Commit migrated from dotnet/corefx@245d532
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants