diff --git a/collections-arrays/HowToMergeArrays/HowToMergeArrays/HowToMergeArrays.csproj b/collections-arrays/HowToMergeArrays/HowToMergeArrays/HowToMergeArrays.csproj index 79e7148fd0..62be4f867f 100644 --- a/collections-arrays/HowToMergeArrays/HowToMergeArrays/HowToMergeArrays.csproj +++ b/collections-arrays/HowToMergeArrays/HowToMergeArrays/HowToMergeArrays.csproj @@ -2,13 +2,13 @@ Exe - net6.0 + net10.0 enable enable - + diff --git a/collections-arrays/HowToMergeArrays/HowToMergeArrays/MergeArrayBenchmark.cs b/collections-arrays/HowToMergeArrays/HowToMergeArrays/MergeArrayBenchmark.cs index 4fb2d0ba59..61bb39b2f2 100644 --- a/collections-arrays/HowToMergeArrays/HowToMergeArrays/MergeArrayBenchmark.cs +++ b/collections-arrays/HowToMergeArrays/HowToMergeArrays/MergeArrayBenchmark.cs @@ -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 { @@ -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(); + 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] @@ -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) @@ -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; } @@ -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; } @@ -112,8 +132,8 @@ public int[] MergeUsingNewArrayManually(int[] firstArray, int[] secondArray) public IEnumerable 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 }; } diff --git a/collections-arrays/HowToMergeArrays/Tests/Tests.cs b/collections-arrays/HowToMergeArrays/Tests/Tests.cs index 46f17378b6..0a45b180df 100644 --- a/collections-arrays/HowToMergeArrays/Tests/Tests.cs +++ b/collections-arrays/HowToMergeArrays/Tests/Tests.cs @@ -1,4 +1,5 @@ using HowToMergeArrays; +using System.Collections.Generic; using System.Linq; using Xunit; @@ -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(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); } } } diff --git a/collections-arrays/HowToMergeArrays/Tests/Tests.csproj b/collections-arrays/HowToMergeArrays/Tests/Tests.csproj index 1026b9d8dd..0cb7d55ae9 100644 --- a/collections-arrays/HowToMergeArrays/Tests/Tests.csproj +++ b/collections-arrays/HowToMergeArrays/Tests/Tests.csproj @@ -1,20 +1,20 @@ - net6.0 + net10.0 enable false - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all