Skip to content

Commit c4d7689

Browse files
committed
[Zip] add extension.
1 parent d68528f commit c4d7689

File tree

7 files changed

+234
-0
lines changed

7 files changed

+234
-0
lines changed

Documents/BenchmarksResults/Zip.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Zip
2+
3+
### Source
4+
[Zip.cs](../../src/StructLinq.Benchmark/Zip.cs)
5+
6+
### Results:
7+
``` ini
8+
9+
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
10+
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
11+
.NET Core SDK=5.0.203
12+
[Host] : .NET Core 5.0.6 (CoreCLR 5.0.621.22011, CoreFX 5.0.621.22011), X64 RyuJIT
13+
DefaultJob : .NET Core 5.0.6 (CoreCLR 5.0.621.22011, CoreFX 5.0.621.22011), X64 RyuJIT
14+
15+
16+
```
17+
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
18+
|------------------- |---------:|---------:|---------:|------:|-------:|------:|------:|----------:|
19+
| Linq | 70.70 μs | 0.718 μs | 0.672 μs | 1.00 | - | - | - | 144 B |
20+
| StructLinq | 12.76 μs | 0.022 μs | 0.020 μs | 0.18 | 0.0153 | - | - | 64 B |
21+
| StructLinqFunction | 10.38 μs | 0.014 μs | 0.012 μs | 0.15 | - | - | - | - |

src/StructLinq.Benchmark/Zip.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Linq;
2+
using BenchmarkDotNet.Attributes;
3+
4+
namespace StructLinq.Benchmark
5+
{
6+
[MemoryDiagnoser]
7+
public class Zip
8+
{
9+
private const int Count1 = 10000;
10+
private const int Count2 = 5000;
11+
public int[] array1;
12+
public int[] array2;
13+
14+
public Zip()
15+
{
16+
array1 = Enumerable.Range(0, Count1).ToArray();
17+
array2 = Enumerable.Range(0, Count2).ToArray();
18+
}
19+
20+
[Benchmark(Baseline = true)]
21+
public int Linq()
22+
{
23+
var sum = 0;
24+
#if !NETFRAMEWORK
25+
foreach (var i in array1.Zip(array2))
26+
{
27+
sum += i.First + i.Second;
28+
}
29+
#else
30+
foreach (var i in array1.Zip(array2, (i, i1) => (i, i1)))
31+
{
32+
sum += i.Item1 + i.Item2;
33+
}
34+
#endif
35+
36+
return sum;
37+
}
38+
39+
[Benchmark]
40+
public int StructLinq()
41+
{
42+
var sum = 0;
43+
foreach (var i in array1.ToStructEnumerable().Zip(array2.ToStructEnumerable()))
44+
{
45+
sum += i.Item1 + i.Item2;
46+
}
47+
48+
return sum;
49+
}
50+
51+
[Benchmark]
52+
public int StructLinqFunction()
53+
{
54+
var sum = 0;
55+
foreach (var i in array1.ToStructEnumerable().Zip(array2.ToStructEnumerable(), x=> x, x=>x))
56+
{
57+
sum += i.Item1 + i.Item2;
58+
}
59+
60+
return sum;
61+
}
62+
63+
}
64+
}

src/StructLinq.Tests/ZipTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Linq;
2+
using StructLinq.Range;
3+
using StructLinq.Zip;
4+
using Xunit;
5+
6+
namespace StructLinq.Tests
7+
{
8+
public class ZipTests : AbstractEnumerableTests<(int, int),
9+
ZipEnumerable<int, RangeEnumerable, RangeEnumerator, int, RangeEnumerable, RangeEnumerator>,
10+
ZipEnumerator<int, RangeEnumerator, int, RangeEnumerator>>
11+
{
12+
protected override ZipEnumerable<int, RangeEnumerable, RangeEnumerator, int, RangeEnumerable, RangeEnumerator> Build(int size)
13+
{
14+
return new(StructEnumerable.Range(-10, size), StructEnumerable.Range(10, size));
15+
}
16+
17+
[Theory]
18+
[InlineData(0, 10)]
19+
[InlineData(10, 0)]
20+
[InlineData(5, 10)]
21+
[InlineData(10, 5)]
22+
[InlineData(10, 10)]
23+
public void ShouldBeTheSameAsSystem(int size1, int size2)
24+
{
25+
var array1 = Enumerable.Range(-10, size1).ToArray();
26+
var array2 = Enumerable.Range(-20, size2).ToArray();
27+
var expected = array1.Zip(array2, (x, y)=> (x, y)).ToArray();
28+
var values = array1.ToStructEnumerable()
29+
.Zip(array2.ToStructEnumerable(), x => x, x => x)
30+
.ToArray();
31+
Assert.Equal(expected, values);
32+
}
33+
}
34+
}

src/StructLinq/StructLinq.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
</PackageReference>
2121
<PackageReference Include="System.Buffers" Version="4.5.1" />
2222
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
23+
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
2324
</ItemGroup>
2425

2526
<ItemGroup>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using StructLinq.Zip;
4+
5+
// ReSharper disable once CheckNamespace
6+
namespace StructLinq
7+
{
8+
public static partial class StructEnumerable
9+
{
10+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11+
public static ZipEnumerable<T1, TEnumerable1, TEnumerator1, T2, TEnumerable2, TEnumerator2> Zip<T1, TEnumerable1, TEnumerator1, T2, TEnumerable2, TEnumerator2>(this TEnumerable1 enumerable1, TEnumerable2 enumerable2,
12+
Func<TEnumerable1, IStructEnumerable<T1, TEnumerator1>> _,
13+
Func<TEnumerable2, IStructEnumerable<T2, TEnumerator2>> __)
14+
where TEnumerable1 : IStructEnumerable<T1, TEnumerator1>
15+
where TEnumerator1 : struct, IStructEnumerator<T1>
16+
where TEnumerable2 : IStructEnumerable<T2, TEnumerator2>
17+
where TEnumerator2 : struct, IStructEnumerator<T2>
18+
{
19+
return new (enumerable1, enumerable2);
20+
}
21+
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
public static ZipEnumerable<T1, IStructEnumerable<T1, TEnumerator1>, TEnumerator1, T2, IStructEnumerable<T2, TEnumerator2>, TEnumerator2> Zip<T1, TEnumerator1, T2, TEnumerator2>(this IStructEnumerable<T1, TEnumerator1> enumerable1, IStructEnumerable<T2, TEnumerator2> enumerable2)
24+
where TEnumerator1 : struct, IStructEnumerator<T1>
25+
where TEnumerator2 : struct, IStructEnumerator<T2>
26+
{
27+
return new (enumerable1, enumerable2);
28+
}
29+
30+
}
31+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace StructLinq.Zip
4+
{
5+
public struct ZipEnumerable<T1, TEnumerable1, TEnumerator1, T2, TEnumerable2, TEnumerator2> : IStructEnumerable<(T1 First, T2 Second), ZipEnumerator<T1, TEnumerator1, T2, TEnumerator2>>
6+
where TEnumerator1 : struct, IStructEnumerator<T1>
7+
where TEnumerator2 : struct, IStructEnumerator<T2>
8+
where TEnumerable1 : IStructEnumerable<T1, TEnumerator1>
9+
where TEnumerable2 : IStructEnumerable<T2, TEnumerator2>
10+
{
11+
private readonly TEnumerable1 enumerable1;
12+
private readonly TEnumerable2 enumerable2;
13+
14+
public ZipEnumerable(TEnumerable1 enumerable1, TEnumerable2 enumerable2)
15+
{
16+
this.enumerable1 = enumerable1;
17+
this.enumerable2 = enumerable2;
18+
}
19+
20+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
21+
public ZipEnumerator<T1, TEnumerator1, T2, TEnumerator2> GetEnumerator()
22+
{
23+
return new (enumerable1.GetEnumerator(), enumerable2.GetEnumerator());
24+
}
25+
26+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27+
public VisitStatus Visit<TVisitor>(ref TVisitor visitor) where TVisitor : IVisitor<(T1, T2)>
28+
{
29+
foreach (var input in this)
30+
{
31+
if (!visitor.Visit(input))
32+
return VisitStatus.VisitorFinished;
33+
}
34+
35+
return VisitStatus.EnumeratorFinished;
36+
37+
}
38+
}
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace StructLinq.Zip
4+
{
5+
public struct ZipEnumerator<T1, TEnumerator1, T2, TEnumerator2> : IStructEnumerator<(T1, T2)>
6+
where TEnumerator1 : struct, IStructEnumerator<T1>
7+
where TEnumerator2 : struct, IStructEnumerator<T2>
8+
{
9+
private TEnumerator1 enumerator1;
10+
private TEnumerator2 enumerator2;
11+
12+
public ZipEnumerator(TEnumerator1 enumerator1, TEnumerator2 enumerator2)
13+
{
14+
this.enumerator1 = enumerator1;
15+
this.enumerator2 = enumerator2;
16+
}
17+
18+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
19+
public void Dispose()
20+
{
21+
enumerator1.Dispose();
22+
enumerator2.Dispose();
23+
}
24+
25+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
26+
public bool MoveNext()
27+
{
28+
return enumerator1.MoveNext() && enumerator2.MoveNext();
29+
}
30+
31+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32+
public void Reset()
33+
{
34+
enumerator1.Reset();
35+
enumerator2.Reset();
36+
}
37+
38+
public (T1, T2) Current
39+
{
40+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
41+
get => (enumerator1.Current, enumerator2.Current);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)