-
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
399 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[*] | ||
charset = utf-8 | ||
end_of_line = lf | ||
|
||
csharp_style_namespace_declarations=file_scoped:suggestion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<Project> | ||
|
||
<PropertyGroup> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<Solution> | ||
<Properties Name="Visual Studio"> | ||
<Property Name="OpenWith" Value="17" /> | ||
</Properties> | ||
<Project Path="DistinctBenchmark\DistinctBenchmark.csproj" Type="C#" /> | ||
<Project Path="DistinctTest\DistinctTest.csproj" Type="C#" /> | ||
<Project Path="Distinct\Distinct.csproj" /> | ||
<Folder Name="/solution items/"> | ||
<File Path=".editorconfig" /> | ||
<File Path="Directory.Build.props" /> | ||
<File Path="readme.md" /> | ||
</Folder> | ||
</Solution> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
namespace Distinct; | ||
|
||
public class Binary | ||
{ | ||
public static ReadOnlySpan<T> Distinct<T>(IEnumerable<T> source, Span<T> buffer) | ||
where T : IComparable<T> | ||
{ | ||
var count = 0; | ||
|
||
foreach (var item in source) | ||
{ | ||
var i = buffer[..count].BinarySearch(item); | ||
if (i >= 0) continue; | ||
InsertAt(buffer, ~i, item); | ||
++count; | ||
} | ||
|
||
return buffer[..count]; | ||
} | ||
|
||
private static void InsertAt<T>(Span<T> span, int index, T value) | ||
{ | ||
span[index..^1].CopyTo(span[(index + 1)..]); | ||
span[index] = value; | ||
} | ||
} | ||
|
||
/* | ||
public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer) | ||
{ | ||
ThrowHelper.ThrowIfNull(list); | ||
ThrowHelper.ThrowIfNull(comparer); | ||
int lower = 0; | ||
int upper = list.Count - 1; | ||
while (lower <= upper) | ||
{ | ||
int middle = lower + (upper - lower) / 2; | ||
int comparisonResult = comparer(value, list[middle]); | ||
if (comparisonResult < 0) | ||
{ | ||
upper = middle - 1; | ||
} | ||
else if (comparisonResult > 0) | ||
{ | ||
lower = middle + 1; | ||
} | ||
else | ||
{ | ||
return middle; | ||
} | ||
} | ||
return ~lower; | ||
} | ||
* */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
namespace Distinct; | ||
|
||
public class Hash | ||
{ | ||
/// <summary> | ||
/// かなり癖の強い実装。 | ||
/// </summary> | ||
/// <remarks> | ||
/// * buffer がデフォルト値で初期化されてないと動作しない | ||
/// * source にデフォルト値が入ってくると動作しない | ||
/// * buffer サイズが source の倍程度の長さがないと衝突多くて遅い | ||
/// </remarks> | ||
public static Span<T> Distinct<T, TKey>(IEnumerable<T> source, Span<T> buffer, Func<T, TKey> getKey, Func<T, bool> isDefault) | ||
where TKey : notnull | ||
{ | ||
SpanHashSet<T, TKey> set = new(buffer, getKey, isDefault); | ||
|
||
foreach (var x in source) set.Add(x); | ||
|
||
var len = set.Compact(); | ||
return buffer[..len]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace Distinct; | ||
|
||
public class Linear | ||
{ | ||
public static ReadOnlySpan<T> Distinct<T>(IEnumerable<T> source, Span<T> buffer) | ||
where T : IEquatable<T> | ||
{ | ||
var count = 0; | ||
|
||
foreach (var item in source) | ||
{ | ||
if (buffer[..count].Contains(item)) continue; | ||
buffer[count++] = item; | ||
} | ||
|
||
return buffer[..count]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Distinct; | ||
|
||
public readonly ref struct SpanHashSet<T, TKey>(Span<T> buffer, Func<T, TKey> getKey, Func<T, bool> isDefault) | ||
where TKey : notnull | ||
{ | ||
private readonly Span<T> _buffer = buffer; | ||
|
||
public bool Add(T item) | ||
{ | ||
ref var r = ref GetOrAddValueRef(getKey(item)); | ||
if (!isDefault(r)) return false; | ||
|
||
r = item; | ||
return true; | ||
} | ||
|
||
public ref T GetOrAddValueRef(TKey key) | ||
{ | ||
var buffer = _buffer; | ||
var len = buffer.Length; | ||
|
||
int collisionCount = 0; | ||
int bucketIndex = key.GetHashCode() % len; | ||
for (int i = bucketIndex; i < len; i = (i + 1) % len) | ||
{ | ||
ref var item = ref buffer[i]; | ||
if (key.Equals(getKey(item)) | ||
|| isDefault(item)) | ||
return ref item!; | ||
|
||
if (collisionCount == buffer.Length) Throw(); | ||
|
||
collisionCount++; | ||
} | ||
|
||
Throw(); | ||
return ref Unsafe.NullRef<T>(); | ||
} | ||
|
||
private static void Throw() => throw new InvalidOperationException(); | ||
|
||
public int Compact() | ||
{ | ||
var buffer = _buffer; | ||
var len = buffer.Length; | ||
|
||
var count = 0; | ||
for (int i = 0; i < len; i++) | ||
{ | ||
ref var item = ref buffer[i]; | ||
if (!isDefault(item)) | ||
{ | ||
if (count != i) | ||
{ | ||
buffer[count] = item; | ||
item = default!; | ||
} | ||
|
||
count++; | ||
} | ||
} | ||
|
||
return count; | ||
} | ||
} | ||
|
16 changes: 16 additions & 0 deletions
16
Demo/2024/Distinct/DistinctBenchmark/DistinctBenchmark.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Distinct\Distinct.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using BenchmarkDotNet.Attributes; | ||
using BenchmarkDotNet.Running; | ||
|
||
BenchmarkRunner.Run<DistinctBenchmark>(); | ||
|
||
public class DistinctBenchmark | ||
{ | ||
[Params(10, 20, 50, 100, 200, 500, 1000)] | ||
public int N { get; set; } | ||
|
||
private int[] _data = null!; | ||
|
||
[GlobalSetup] | ||
public void Setup() | ||
{ | ||
var r = new Random(); | ||
_data = new int[1000]; | ||
foreach (ref var x in _data.AsSpan()) x = r.Next(1, 1000); | ||
} | ||
|
||
[Benchmark(Baseline = true)] | ||
public void Linq() | ||
{ | ||
foreach (var _ in _data.Take(N).Distinct()) ; | ||
} | ||
|
||
[Benchmark] | ||
public void Linear() | ||
{ | ||
foreach (var _ in Distinct.Linear.Distinct(_data.Take(N), stackalloc int[N])) ; | ||
} | ||
|
||
[Benchmark] | ||
public void Binary() | ||
{ | ||
foreach (var _ in Distinct.Binary.Distinct(_data.Take(N), stackalloc int[N])) ; | ||
} | ||
|
||
[Benchmark] | ||
public void Hash() | ||
{ | ||
foreach (var _ in Distinct.Hash.Distinct(_data.Take(N), stackalloc int[N], x => x, x => x == 0)) ; | ||
} | ||
|
||
[Benchmark] | ||
public void Hash2N() | ||
{ | ||
foreach (var _ in Distinct.Hash.Distinct(_data.Take(N), stackalloc int[2 * N], x => x, x => x == 0)) ; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
|
||
namespace DistinctTest; | ||
|
||
public class BinaryTest | ||
{ | ||
[Fact] | ||
public void EqualToLinqDistictOrder() | ||
{ | ||
var data = TestData.Data; | ||
var expected = data.Distinct().Order().ToArray(); | ||
var actual = Distinct.Binary.Distinct(data, stackalloc int[data.Length]); | ||
|
||
Assert.Equal(expected, actual); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<IsPackable>false</IsPackable> | ||
<IsTestProject>true</IsTestProject> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" /> | ||
<PackageReference Include="xunit" Version="2.8.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="6.0.2"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Distinct\Distinct.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
global using Xunit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace DistinctTest; | ||
|
||
public class HashTest | ||
{ | ||
[Fact] | ||
public void EqualToLinqDistictOrder() | ||
{ | ||
var data = TestData.Data; | ||
var expected = data.Distinct().Order().ToArray(); | ||
var actual = Distinct.Hash.Distinct(data, stackalloc int[data.Length], x => x, x => x == 0); | ||
MemoryExtensions.Sort(actual); | ||
|
||
Assert.Equal(expected, actual); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
|
||
namespace DistinctTest; | ||
|
||
public class LinearTest | ||
{ | ||
[Fact] | ||
public void EqualToLinqDistict() | ||
{ | ||
var data = TestData.Data; | ||
var expected = data.Distinct().ToArray(); | ||
var actual = Distinct.Linear.Distinct(data, stackalloc int[data.Length]); | ||
|
||
Assert.Equal(expected, actual); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
namespace DistinctTest; | ||
|
||
public class TestData | ||
{ | ||
public static int[] Data => _data ??= CreateData(); | ||
private static int[]? _data; | ||
|
||
private static int[] CreateData() | ||
{ | ||
var r = new Random(); | ||
var data = new int[1000]; | ||
foreach (ref var x in data.AsSpan()) x = r.Next(1, 1000); | ||
return data; | ||
} | ||
} |
Oops, something went wrong.