Skip to content
Merged
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 @@ -2,13 +2,13 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace HowToMergeArrays
[MemoryDiagnoser, Orderer(SummaryOrderPolicy.FastestToSlowest)]
public class MergeArrayBenchmark
{
private static readonly int _arraySize = 50000;
private static readonly int _arraySize = 1000;

public int ArraySize
{
Expand All @@ -29,12 +29,12 @@ public int[] MergeUsingArrayCopyWithNewArray(int[] firstArray, int[] secondArray
public int[] MergeUsingArrayCopyWithResize(int[] firstArray, int[] secondArray)
{
var originalFirstArrayLength = firstArray.Length;
int[] firstTempArray = Array.Empty<int>();
var combinedArray = firstArray;

Array.Resize(ref firstTempArray, firstArray.Length + secondArray.Length);
Array.Copy(secondArray, 0, firstTempArray, originalFirstArrayLength, secondArray.Length);
Array.Resize(ref combinedArray, firstArray.Length + secondArray.Length);
Array.Copy(secondArray, 0, combinedArray, originalFirstArrayLength, secondArray.Length);

return firstTempArray;
return combinedArray;
}

[Benchmark]
Expand All @@ -48,6 +48,17 @@ public int[] MergeUsingArrayCopyTo(int[] firstArray, int[] secondArray)
return combinedArray;
}

[Benchmark]
[ArgumentsSource(nameof(GetSourceArrayPopulatedWithNumbers))]
public int[] MergeUsingSpanCopyTo(int[] firstArray, int[] secondArray)
{
var combinedArray = new int[firstArray.Length + secondArray.Length];
firstArray.AsSpan().CopyTo(combinedArray);
secondArray.AsSpan().CopyTo(combinedArray.AsSpan(firstArray.Length));

return combinedArray;
}

[Benchmark]
[ArgumentsSource(nameof(GetSourceArrayPopulatedWithNumbers))]
public int[] MergeUsingLinqConcat(int[] firstArray, int[] secondArray)
Expand All @@ -71,7 +82,16 @@ public int[] MergeUsingLinqUnion(int[] firstArray, int[] secondArray)
public int[] MergeUsingLinqSelectMany(int[] firstArray, int[] secondArray)
{
var firstAndSecondArray = new[] { firstArray, secondArray };
var combinedArray = firstAndSecondArray.SelectMany(animal => animal).ToArray();
var combinedArray = firstAndSecondArray.SelectMany(array => array).ToArray();

return combinedArray;
}

[Benchmark]
[ArgumentsSource(nameof(GetSourceArrayPopulatedWithNumbers))]
public int[] MergeUsingCollectionExpression(int[] firstArray, int[] secondArray)
{
int[] combinedArray = [.. firstArray, .. secondArray];

return combinedArray;
}
Expand All @@ -82,8 +102,8 @@ public int[] MergeUsingBlockCopy(int[] firstArray, int[] secondArray)
{
var combinedArray = new int[firstArray.Length + secondArray.Length];

Buffer.BlockCopy(firstArray, 0, combinedArray, 0, firstArray.Length);
Buffer.BlockCopy(secondArray, 0, combinedArray, firstArray.Length, secondArray.Length);
Buffer.BlockCopy(firstArray, 0, combinedArray, 0, firstArray.Length * sizeof(int));
Buffer.BlockCopy(secondArray, 0, combinedArray, firstArray.Length * sizeof(int), secondArray.Length * sizeof(int));

return combinedArray;
}
Expand Down Expand Up @@ -112,8 +132,8 @@ public int[] MergeUsingNewArrayManually(int[] firstArray, int[] secondArray)

public IEnumerable<object[]> GetSourceArrayPopulatedWithNumbers()
{
var first = Enumerable.Repeat(1, _arraySize).ToArray();
var second = Enumerable.Repeat(2, _arraySize).ToArray();
var first = Enumerable.Range(0, _arraySize).ToArray();
var second = Enumerable.Range(_arraySize / 2, _arraySize).ToArray();

yield return new object[] { first, second };
}
Expand Down
81 changes: 62 additions & 19 deletions collections-arrays/HowToMergeArrays/Tests/Tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HowToMergeArrays;
using System.Collections.Generic;
using System.Linq;
using Xunit;

Expand All @@ -8,89 +9,131 @@ public class Tests
{
private readonly MergeArrayBenchmark _mergeArrayRunner;
private readonly int _combinedExpectedSize;
private readonly int _combinedExpectedSizeDistinct = 2;
private readonly int _combinedExpectedSizeDistinct;

private readonly int[] first;
private readonly int[] second;
private readonly int[] _expectedMerged;
private readonly int[] _expectedDistinct;

public Tests()
{
_mergeArrayRunner = new MergeArrayBenchmark();
var source = _mergeArrayRunner.GetSourceArrayPopulatedWithNumbers().Single();
first = (int[])source[0];
second = (int[])source[1];

_combinedExpectedSize = _mergeArrayRunner.ArraySize * 2;
first = Enumerable.Repeat(1, _mergeArrayRunner.ArraySize).ToArray();
second =Enumerable.Repeat(2, _mergeArrayRunner.ArraySize).ToArray();
_combinedExpectedSizeDistinct = _mergeArrayRunner.ArraySize + _mergeArrayRunner.ArraySize / 2;

// The expected results are built without any of the methods under test:
// every element of the first array, then every element of the second.
var expected = new List<int>(first);
expected.AddRange(second);
_expectedMerged = expected.ToArray();

// The second array starts halfway through the first, so the distinct values run
// from 0 to one and a half times the array size, in first-seen order.
_expectedDistinct = Enumerable.Range(0, _combinedExpectedSizeDistinct).ToArray();
}

[Fact]
public void WhenMergingWithArrayCopyAndNewArray_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithArrayCopyAndNewArray_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingArrayCopyWithNewArray(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithArrayCopyAndResize_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithArrayCopyAndResize_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingArrayCopyWithResize(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithArrayCopyTo_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithArrayCopyAndResize_ThenFirstArrayIsLeftUnchanged()
{
var combinedArray = _mergeArrayRunner.MergeUsingArrayCopyWithResize(first, second);

Assert.NotSame(first, combinedArray);
Assert.Equal(_mergeArrayRunner.ArraySize, first.Length);
}

[Fact]
public void WhenMergingWithArrayCopyTo_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingArrayCopyTo(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithLinqConcat_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithSpanCopyTo_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingSpanCopyTo(first, second);

Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithLinqConcat_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingLinqConcat(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithLinqUnion_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSizeDistinct()
public void WhenMergingWithLinqUnion_ThenCombinedArrayHoldsEachDistinctValueOnce()
{
var combinedArray = _mergeArrayRunner.MergeUsingLinqUnion(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSizeDistinct, combinedArray.Length);
Assert.Equal(_expectedDistinct, combinedArray);
}

[Fact]
public void WhenMergingWithLinqSelectMany_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithLinqSelectMany_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingLinqSelectMany(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithCollectionExpression_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingCollectionExpression(first, second);

Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithBlockCopy_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithBlockCopy_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingBlockCopy(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}

[Fact]
public void WhenMergingWithNewArrayManually_ThenCombinedArrayNotEmptyAndLengthEqualsDoubleSize()
public void WhenMergingWithNewArrayManually_ThenCombinedArrayHoldsFirstThenSecond()
{
var combinedArray = _mergeArrayRunner.MergeUsingNewArrayManually(first, second);

Assert.NotEmpty(combinedArray);
Assert.Equal(_combinedExpectedSize, combinedArray.Length);
Assert.Equal(_expectedMerged, combinedArray);
}
}
}
10 changes: 5 additions & 5 deletions collections-arrays/HowToMergeArrays/Tests/Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="10.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Loading