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

Conversation

@jkotas
Copy link
Member

@jkotas jkotas commented Sep 13, 2017

Array.Reverse showed up as hot method in profiles of real workloads (e.g. it is called from Json.NET frequently via List.Reverse)

@jkotas
Copy link
Member Author

jkotas commented Sep 13, 2017

@ahsonkhan This implements the improvement we have discussed over email.

Array.Reverse showed up as hot method in profiles of real workloads (e.g. it is called from Json.NET frequently via List.Reverse)
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
Contract.EndContractBlock();

ref T p = ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData());

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 do something similar to replace TrySZReverse in the non-generic Reverse?
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Array.cs#L1656

Copy link
Member Author

Choose a reason for hiding this comment

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

Replacing TrySZReverse with C# implementation would be more profound change. I will leave it for a different day..

Copy link

@ahsonkhan ahsonkhan left a comment

Choose a reason for hiding this comment

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

LGTM

array[j] = temp;
T temp = Unsafe.Add(ref p, i);
Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j);
Unsafe.Add(ref p, j) = temp;
Copy link
Member

@stephentoub stephentoub Sep 13, 2017

Choose a reason for hiding this comment

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

@jkotas, for my edification, what's the explanation for why this is faster? It's avoiding bounds checks that the JIT is currently unable to elide?

Copy link
Member Author

@jkotas jkotas Sep 13, 2017

Choose a reason for hiding this comment

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

It is avoiding bounds checks and covariance checks (for arrays of object references). The covariance checks are the more expensive ones of the two.

In theory, the JIT can notice that the source of the value is the same array as what it is assigned to and use that information to ship the covariance check ...

Copy link
Member

Choose a reason for hiding this comment

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

Got it.

Copy link
Member

Choose a reason for hiding this comment

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

List<T> is non-covariant so this could be done for Add etc?

Copy link
Member Author

Choose a reason for hiding this comment

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

For List, you need to be careful about corrupted state that can lead to buffer overruns or type safety violations. Some of these bugs may be in the user code, but our general standard is that user code race should not turn into type or memory safety violations. I think a better way to deal with the covariance check overhead in general case like List is something like the covariance array wrapper (https://github.com/dotnet/corefx/issues/23689) - the unsafe code is just one line in the covariance array wrapper. The rest is safe.

Copy link
Member

@ViktorHofer ViktorHofer Apr 24, 2018

Choose a reason for hiding this comment

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

In theory, the JIT can notice that the source of the value is the same array as what it is assigned to and use that information to ship the covariance check ...

Is skipping the covariance check when source and destination are same, tracked somewhere?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

If you load and store to the same array directly the jit already does this optimization (see for instance #14580, which enhanced an existing optimization). But if load and store are disconnected the jit may miss out.

@erozenfeld maybe this is something we could do more generally, once we have built up dataflow?

Copy link
Member

Choose a reason for hiding this comment

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

mikedn commented on the current status of bounds check optimizations here: https://github.com/dotnet/coreclr/issues/15723

i++;
j--;
}
Array.Reverse<object>(objArray, index, length);

Choose a reason for hiding this comment

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

Nice.

@jkotas
Copy link
Member Author

jkotas commented Sep 14, 2017

@dotnet-bot test Windows_NT x64 corefx_baseline

@jkotas jkotas merged commit c96a827 into dotnet:master Sep 14, 2017
@jkotas jkotas deleted the faster-reverse branch September 14, 2017 07:34
jkotas added a commit to dotnet/corert that referenced this pull request Sep 14, 2017
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.

7 participants