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

Conversation

@ahsonkhan
Copy link

@ahsonkhan ahsonkhan commented Nov 9, 2018

Note: This performance test results are running on netstandard. I will retry on netcoreapp soon. I think we should change the perf test config to netcoreapp. Is that fine?

Edit: Keeping perf tests as netstandard. See #33367 (comment)

cc @adamsitnik (we should probably port these to BDN).

cc @bartonjs, @stephentoub @safern

@ahsonkhan ahsonkhan changed the title Use a custom BitArray implementation in Utf8JsonReader instead of a stack to be more memory efficient Optimize BitArray APIs using span and bitwise operations Nov 9, 2018
public bool Get(int index)
{
if (index < 0 || index >= Length)
if ((uint)index >= (uint)Length)
Copy link

Choose a reason for hiding this comment

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

Why can’t the JIT optimise cases like this? I honestly have seen so many code changes that do this that engineering effort should go to ensuring wins across the board with these sorts of checks!

Copy link

Choose a reason for hiding this comment

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

This particular one is tricky to handle in the JIT. If index < 0 is true then index >= Length is no longer evaluated. If this is null (the runtime does not guarantee that it is not) then a NullReferenceException is avoided. This means that combining the 2 comparisons will change the meaning of the code when this is null. Kind of sucks but it is what it is.

Perhaps we should assume that this cannot be null. C# tries to prevent that by using callvirt to invoke methods. Other languages.... dunno.

The JIT could handle cases like c >= '0' && c <= '9', I've actually done that in the past. But there weren't many of them in the framework so I didn't bother to finish it.

Copy link

@pentp pentp Nov 15, 2018

Choose a reason for hiding this comment

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

It could be argued that Roslyn should be doing such optimizations (and it actually does for switch cases, e.g., when you have case '0' to case '9' with fallthroughs, it generates the subtraction and unsigned compare).
But then again in a general comparison like here it could end up hurting performance if for example it's common for index to be negative...

@ViktorHofer
Copy link
Member

This performance test results are running on netstandard. I think we should change the perf test config to netcoreapp. Is that fine?

No. I recently migrated all possible performance projects to netstandard to make them runnable on both netcoreapp and netfx. What do you mean by "test results are running on netstandard"? You can't run on netstandard.

@ahsonkhan
Copy link
Author

No. I recently migrated all possible performance projects to netstandard to make them runnable on both netcoreapp and netfx.

OK. Good to know.

What do you mean by "test results are running on netstandard"? You can't run on netstandard.

Yep, you are correct. I made a mistake and hence my comment can be ignored. I thought I was running on netfx by default (which uses portable span). Thanks for clarifying. Edited the original post.

@ahsonkhan
Copy link
Author

Unrelated test failure: https://github.com/dotnet/corefx/issues/24355

Ubuntu.1604.Arm64.Open-arm64-Release

https://mc.dot.net/#/user/ahsonkhan/pr~2Fjenkins~2Fdotnet~2Fcorefx~2Fmaster~2F/test~2Ffunctional~2Fcli~2F/c57baf4ff9c0cf15b7a1bb1cb3eb55e1c76fb50e/workItem/System.Net.NameResolution.Functional.Tests/analysis/xunit/System.Net.NameResolution.Tests.GetHostByNameTest~2FDnsObsoleteBeginEndGetHostByName_EmptyString_ReturnsHostName

Unhandled Exception of Type System.Net.Internals.SocketExceptionFactory+ExtendedSocketException
Message :
System.Net.Internals.SocketExceptionFactory+ExtendedSocketException : Resource temporarily unavailable
Stack Trace :
   at System.Net.Dns.InternalGetHostByName(String hostName) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 65
   at System.Net.Dns.ResolveCallback(Object context) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 218
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source) in /root/coreclr/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs:line 137
   at System.Net.Dns.HostResolutionEndHelper(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 358
   at System.Net.Dns.EndGetHostByName(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 383
   at System.Net.NameResolution.Tests.GetHostByNameTest.DnsObsoleteBeginEndGetHostByName_EmptyString_ReturnsHostName() in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs:line 117

https://mc.dot.net/#/user/ahsonkhan/pr~2Fjenkins~2Fdotnet~2Fcorefx~2Fmaster~2F/test~2Ffunctional~2Fcli~2F/c57baf4ff9c0cf15b7a1bb1cb3eb55e1c76fb50e/workItem/System.Net.NameResolution.Functional.Tests/analysis/xunit/System.Net.NameResolution.Tests.GetHostEntryTest~2FDns_GetHostEntryAsync_HostString_Ok(hostName:%20%5C%22%5C%22)

Message :
System.AggregateException : One or more errors occurred. (One or more errors occurred. (Resource temporarily unavailable)) (One or more errors occurred. (Resource temporarily unavailable))
---- System.AggregateException : One or more errors occurred. (Resource temporarily unavailable)
-------- System.Net.Internals.SocketExceptionFactory+ExtendedSocketException : Resource temporarily unavailable
---- System.AggregateException : One or more errors occurred. (Resource temporarily unavailable)
-------- System.Net.Internals.SocketExceptionFactory+ExtendedSocketException : Resource temporarily unavailable
Stack Trace :
   at System.Threading.Tasks.TaskTimeoutExtensions.WhenAllOrAnyFailed(Task[] tasks) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs:line 103
   at System.Threading.Tasks.TaskTimeoutExtensions.WhenAllOrAnyFailed(Task[] tasks, Int32 millisecondsTimeout) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs:line 65
   at System.Net.NameResolution.Tests.GetHostEntryTest.TestGetHostEntryAsync(Func`1 getHostEntryFunc) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs:line 41
--- End of stack trace from previous location where exception was thrown ---
----- Inner Stack Trace #1 (System.AggregateException) -----

----- Inner Stack Trace -----
   at System.Net.Dns.InternalGetHostByName(String hostName) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 65
   at System.Net.Dns.ResolveCallback(Object context) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 218
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source) in /root/coreclr/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs:line 137
   at System.Net.Dns.HostResolutionEndHelper(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 358
   at System.Net.Dns.EndGetHostEntry(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 500
   at System.Net.Dns.<>c.<GetHostEntryAsync>b__27_1(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 590
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization) in /root/coreclr/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs:line 529
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.TaskTimeoutExtensions.WhenAllOrAnyFailed(Task[] tasks) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs:line 77
----- Inner Stack Trace #2 (System.AggregateException) -----

----- Inner Stack Trace -----
   at System.Net.Dns.InternalGetHostByName(String hostName) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 65
   at System.Net.Dns.ResolveCallback(Object context) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 218
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source) in /root/coreclr/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs:line 137
   at System.Net.Dns.HostResolutionEndHelper(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 358
   at System.Net.Dns.EndGetHostEntry(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 500
   at System.Net.Dns.<>c.<GetHostEntryAsync>b__27_1(IAsyncResult asyncResult) in /mnt/j/workspace/dotnet_corefx/master/linux-TGroup_netcoreapp+CGroup_Release+AGroup_arm64+TestOuter_false_prtest/src/System.Net.NameResolution/src/System/Net/DNS.cs:line 590
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization) in /root/coreclr/src/System.Private.CoreLib/src/System/Threading/Tasks/FutureFactory.cs:line 529

((bytes[j + 2] & 0xff) << 16) |
((bytes[j + 3] & 0xff) << 24);
j += 4;
m_array[i] = MemoryMarshal.Read<int>(byteSpan);
Copy link
Member

Choose a reason for hiding this comment

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

This will break Mono. It does not work on big-endian platforms. Use BinaryPrimitives.ReadInt32LittleEndian instead.
.

Copy link
Author

@ahsonkhan ahsonkhan Nov 15, 2018

Choose a reason for hiding this comment

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

Why did it work before when we were directly or'ing elements of the byte array?

Regardless of endianness, we had the following before, no?
byte array: [a, b, c, d]
resulting int: [a | b << 8 | c << 16 | d << 24] => [d c b a]

Why isn't the resulting int incorrect for big-endian? What am I missing here?

Don't we need to swap the ordering for big-endian?
resulting int: [d | c << 8 | b << 16 | a << 24] => [a b c d]

Edit: Never mind. The bytes in the byte array themselves are stored in reverse ordering when we are on big-endian platforms. That's why we didn't need to reverse the or'ing order ourselves.

Copy link
Member

Choose a reason for hiding this comment

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

Never mind. The bytes in the byte array themselves are stored in reverse ordering when we are on a big-endian platforms. That's why we didn't need to reverse the or'ing order ourselves.

Right.

Copy link
Author

@ahsonkhan ahsonkhan Nov 15, 2018

Choose a reason for hiding this comment

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

Is there a way to add tests or CI legs that simulate big-endian platforms so that we can catch such mistakes systematically?

Copy link
Member

Choose a reason for hiding this comment

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

There is not one today

Copy link
Author

Choose a reason for hiding this comment

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

@dotnet/dnceng, is it possible to add a big-endian CI leg, even if it is on demand only?

Copy link
Member

@jkotas jkotas Nov 15, 2018

Choose a reason for hiding this comment

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

@ahsonkhan This is not a problem for dnceng to solve. .NET Core or CoreFX does not run on any big-endian platform today directly. Bigendian systems are supported by Mono only. We would need to do work to build CoreFX for Mono first.

Copy link
Author

Choose a reason for hiding this comment

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

This is not a problem for dnceng to solve.

Pardon my ignorance (and the ping). Thanks for clarifying, @jkotas. Good to know.

@ahsonkhan
Copy link
Author

@dotnet-bot test OSX x64 Debug Build

@ahsonkhan ahsonkhan merged commit 92a697c into dotnet:master Nov 15, 2018
@ahsonkhan ahsonkhan deleted the UseBitArray branch November 15, 2018 11:19
@karelz karelz added this to the 3.0 milestone Nov 15, 2018
jlennox pushed a commit to jlennox/corefx that referenced this pull request Dec 16, 2018
* Use a custom BitArray implementation instead of a stack to be more
memory efficient

* Fix BitArray perf test

* Fix CopyTo tests - different exception thrown

* Add tests for custom bit array and add details to debug assert

* Revert JSON related changes to isolate BitArray changes

* Revert leftover changes to Json

* Address PR feedback

* Fix bug in CopyTo

* Avoid if check for lengths < 4.

* Remove unnecessary bit operations in CopyTo

* Revert back to using Array.Resize

* Fix nits and address PR feedback.

* Address PR feedback.

* Remove use of Span.CopyTo and revert back to Array.Copy

* Use BinaryPrimitives instead of MemoryMarshal to account for endianness.
@stephentoub stephentoub mentioned this pull request Jul 3, 2019
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…fx#33367)

* Use a custom BitArray implementation instead of a stack to be more
memory efficient

* Fix BitArray perf test

* Fix CopyTo tests - different exception thrown

* Add tests for custom bit array and add details to debug assert

* Revert JSON related changes to isolate BitArray changes

* Revert leftover changes to Json

* Address PR feedback

* Fix bug in CopyTo

* Avoid if check for lengths < 4.

* Remove unnecessary bit operations in CopyTo

* Revert back to using Array.Resize

* Fix nits and address PR feedback.

* Address PR feedback.

* Remove use of Span.CopyTo and revert back to Array.Copy

* Use BinaryPrimitives instead of MemoryMarshal to account for endianness.


Commit migrated from dotnet/corefx@92a697c
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.