Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Concurrent\IProducerConsumerCollectionDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\DictionaryEntry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ArraySortHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\BitonicSort.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\Comparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\Dictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\EqualityComparer.cs" />
Expand All @@ -154,6 +155,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\List.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\NonRandomizedStringEqualityComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\ValueListBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\VectorizedSort.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\HashHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\HashHelpers.SerializationInfoTable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Hashtable.cs" />
Expand Down Expand Up @@ -1071,6 +1073,13 @@
<DesignTime>True</DesignTime>
<DependentUpon>ConstantHelper.tt</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\Generic\BitonicSort.Generated.cs">
<DependentUpon>BitonicSort.Generated.tt</DependentUpon>
</Compile>
<Content Include="$(MSBuildThisFileDirectory)System\Collections\Generic\BitonicSort.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>BitonicSort.Generated.cs</LastGenOutput>
</Content>
<Content Include="$(MSBuildThisFileDirectory)System\Numerics\ConstantHelper.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ConstantHelper.cs</LastGenOutput>
Expand Down Expand Up @@ -1495,8 +1504,8 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\WindowsRuntime\EventRegistrationToken.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)\System\Runtime\InteropServices\WindowsRuntime\Attributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\System\Runtime\InteropServices\WindowsRuntime\IActivationFactory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\System\Runtime\InteropServices\WindowsRuntime\Attributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)\System\Runtime\InteropServices\WindowsRuntime\IActivationFactory.cs" />
</ItemGroup>
<!-- CoreCLR uses PAL layer that emulates Windows API on Unix. This is bridge for that PAL layer. See issue dotnet/runtime/#31721. -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true' or '$(FeatureCoreCLR)'=='true'">
Expand Down Expand Up @@ -1546,7 +1555,7 @@
</Compile>
<Compile Include="$(CommonPath)System\IO\Win32Marshal.cs">
<Link>Common\System\IO\Win32Marshal.cs</Link>
</Compile>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Variables.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Mutex.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using Internal;
using Internal.Runtime.CompilerServices;

namespace System.Collections.Generic
{
Expand Down Expand Up @@ -321,12 +325,30 @@ internal partial class GenericArraySortHelper<T>

#region IArraySortHelper<T> Members

public void Sort(Span<T> keys, IComparer<T>? comparer)
public unsafe void Sort(Span<T> keys, IComparer<T>? comparer)
{
try
{
if (comparer == null || comparer == Comparer<T>.Default)
{

if (keys.Length == 0) {
return;
}

if (Avx2.IsSupported)
{
if (typeof(T) == typeof(int))
{
fixed (int* startPtr = &Unsafe.As<T, int>(ref keys[0]))
{
var sorter = new VectorizedSort.VectorizedUnstableSortInt32(startPtr, keys.Length);
Copy link
Member

Choose a reason for hiding this comment

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

Reading through the paper and blog, it looks like the algorithms for different sizes are basically the same, just with different lookup tables and increment sizes. Is that correct?

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 assume that by different sizes you mean different types?

If so, then:

  • unsigned sorting requires two lookup-tables (at least according to my plan), per size, but one of those tables is the one used by signed sorting.
    • I can get into this when we want to dive into my "plans" for unsigned sorting.
  • float would share the table with int
  • and so one per type/size in bits, so:
    • Two more tables to support long/ulong
    • an so on per 16, 8 bits

Does that answer your question?

Copy link
Member

Choose a reason for hiding this comment

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

The question was more around if the primary difference between int and short was just around the lookup table and not necessarily the algorithm used for sorting (other than the number of elements processes "per iteration")?

Trying to understand if supporting the other types would result in a code explosion or if most of the algorithm could be shared, just passing in different increment sizes and lookup tables (and calling a different instruction for the permutations).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case, I think all signed + floating types would:

  • definitely share the exact same principle
  • could potentially share one managed implementation
  • Would definitely blow up in native code size
  • Require a table per size (bytes and shorts are kind of annoying, TBH, but we'll get into that later)

Unsigned sorting has its own complexity, so it would definitely share 1/2 of the lookup tables and the same concepts, but perhaps different managed code.

Unfortunately, a lot of the code ends up being type-dependent because of the comparison intrinsic. The same blowup "problem" is unfortunately also true for BitonicSort.
My "issue" with code-size considerations and the potential blow-up is that there is no clear "formula" to what is acceptable. How much perf increase is worth how much blow-up in code-size? This is part of where I hope this discussion will take us...

The reality is that there are a few dozen working points in terms of various algorithm selection (for small array sorting) and unroll size tweaks that could substantially decrease the native code-size, while still providing reasonable speed-up.
It would be very helpful to try to come up with a some sort of a formula for thinking about this...

It is also probably a good time to mention that, quite surprisingly, a future AVX512 dotnet enabled runtime would lead to completely removing the look-up tables as VCOMPRESS* basically kills the need for the permutation primitive + table.

sorter.Sort();
}
}
}


IntrospectiveSort(keys);
}
else
Expand Down Expand Up @@ -434,12 +456,7 @@ private static void Swap(Span<T> a, int i, int j)
}

internal static void IntrospectiveSort(Span<T> keys)
{
if (keys.Length > 1)
{
IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));
}
}
=> IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));

private static void IntroSort(Span<T> keys, int depthLimit)
{
Expand Down
Loading