Приветствую!
Хотел использовать в проекте расширение EnumerableExtensions.Concat<T>(this IEnumerable<T> source, T element).
Посмотрел реализацию: появилось подозрение, что они делают обход коллекции неэффективно.
Набросал свою реализацию.
Код и результаты ниже.
[SimpleJob(RuntimeMoniker.Net472)]
[SimpleJob(RuntimeMoniker.NetCoreApp22)]
public class ConcatBenchmark
{
private const int N = 3000000;
public int[] startInt = Enumerable.Repeat(1, N).ToArray();
public long[] startLong = Enumerable.Repeat(1l, N).ToArray();
public string[] startStr = Enumerable.Repeat(1, N).Select(i => i.ToString()).ToArray();
[Benchmark]
public int ReferenceCodeJam() => this.startStr.Concat("").Count();
[Benchmark]
public int ReferenceMy() => Concat(this.startStr, "").Count();
[Benchmark]
public int Referencex5CodeJam() => this.startStr.Concat("").Concat("").Concat("").Concat("").Concat("").Count();
[Benchmark]
public int Referencex5My() => Concat(Concat(Concat(Concat(Concat(this.startStr, ""), ""), ""), ""), "").Count();
[Benchmark]
public int intCodeJam() => this.startInt.Concat(N).Sum();
[Benchmark]
public int intMy() => Concat(this.startInt, N).Sum();
[Benchmark]
public int intx5CodeJam() => this.startInt.Concat(N).Concat(N).Concat(N).Concat(N).Concat(N).Sum();
[Benchmark]
public int intx5My() => Concat(Concat(Concat(Concat(Concat(this.startInt, N), N), N), N), N).Sum();
[Benchmark]
public long longCodeJam() => this.startLong.Concat(N).Sum();
[Benchmark]
public long longMy() => Concat(this.startLong, N).Sum();
[Benchmark]
public long longx5CodeJam() => this.startLong.Concat(N).Concat(N).Concat(N).Concat(N).Concat(N).Sum();
[Benchmark]
public long longx5My() => Concat(Concat(Concat(Concat(Concat(this.startLong, N), N), N), N), N).Sum();
public class ConcatEnumerator<T> : IEnumerator<T>
{
private State state;
private IEnumerator<T> start;
private readonly T last;
public ConcatEnumerator(IEnumerator<T> start, T last)
{
this.state = State.ConsumingFirst;
this.start = start;
this.last = last;
}
public void Dispose()
{
}
public Boolean MoveNext()
{
switch (this.state)
{
case State.ConsumingFirst:
if (this.start.MoveNext())
{
return true;
}
else
{
this.state = State.StartConsumed;
return true;
}
case State.StartConsumed:
this.state = State.Empty;
return false;
}
return false;
}
public void Reset()
{
this.start.Reset();
this.state = State.ConsumingFirst;
}
public T Current
{
get
{
switch (this.state)
{
case State.ConsumingFirst:
return this.start.Current;
case State.StartConsumed:
return this.last;
default:
throw new InvalidOperationException();
}
}
}
Object IEnumerator.Current => this.Current;
enum State
{
ConsumingFirst,
StartConsumed,
Empty
}
}
public class ConcatEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> start;
private readonly T last;
public ConcatEnumerable(IEnumerable<T> start, T last)
{
this.start = start;
this.last = last;
}
public IEnumerator<T> GetEnumerator()
{
return new ConcatEnumerator<T>(this.start.GetEnumerator(), this.last);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public static IEnumerable<T> Concat<T>(IEnumerable<T> first, T last)
{
return new ConcatEnumerable<T>(first, last);
}
}
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.836 (1909/November2018Update/19H2)
Intel Core i7-10710U CPU 1.10GHz, 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=2.2.106
[Host] : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), X64 RyuJIT
.NET 4.7.2 : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT
.NET Core 2.2 : .NET Core 2.2.4 (CoreCLR 4.6.27521.02, CoreFX 4.6.27521.01), X64 RyuJIT
| Method | Job | Runtime | Mean | Error | StdDev | Median |
|------------------- |-------------- |-------------- |----------:|---------:|---------:|----------:|
| ReferenceCodeJam | .NET 4.7.2 | .NET 4.7.2 | 34.36 ms | 1.132 ms | 3.247 ms | 33.16 ms |
| ReferenceMy | .NET 4.7.2 | .NET 4.7.2 | 15.28 ms | 0.359 ms | 1.037 ms | 14.76 ms |
| Referencex5CodeJam | .NET 4.7.2 | .NET 4.7.2 | 157.93 ms | 0.961 ms | 0.852 ms | 157.82 ms |
| Referencex5My | .NET 4.7.2 | .NET 4.7.2 | 53.54 ms | 1.066 ms | 1.923 ms | 52.89 ms |
| intCodeJam | .NET 4.7.2 | .NET 4.7.2 | 33.20 ms | 0.633 ms | 0.561 ms | 33.07 ms |
| intMy | .NET 4.7.2 | .NET 4.7.2 | 26.45 ms | 0.251 ms | 0.209 ms | 26.42 ms |
| intx5CodeJam | .NET 4.7.2 | .NET 4.7.2 | 119.49 ms | 2.170 ms | 1.695 ms | 119.04 ms |
| intx5My | .NET 4.7.2 | .NET 4.7.2 | 87.00 ms | 1.227 ms | 1.148 ms | 86.95 ms |
| longCodeJam | .NET 4.7.2 | .NET 4.7.2 | 32.32 ms | 0.283 ms | 0.251 ms | 32.30 ms |
| longMy | .NET 4.7.2 | .NET 4.7.2 | 28.16 ms | 0.202 ms | 0.189 ms | 28.10 ms |
| longx5CodeJam | .NET 4.7.2 | .NET 4.7.2 | 118.99 ms | 1.045 ms | 0.978 ms | 118.84 ms |
| longx5My | .NET 4.7.2 | .NET 4.7.2 | 89.98 ms | 1.082 ms | 0.904 ms | 90.05 ms |
| ReferenceCodeJam | .NET Core 2.2 | .NET Core 2.2 | 32.73 ms | 0.438 ms | 0.388 ms | 32.81 ms |
| ReferenceMy | .NET Core 2.2 | .NET Core 2.2 | 15.08 ms | 0.098 ms | 0.092 ms | 15.05 ms |
| Referencex5CodeJam | .NET Core 2.2 | .NET Core 2.2 | 166.51 ms | 3.323 ms | 3.955 ms | 165.46 ms |
| Referencex5My | .NET Core 2.2 | .NET Core 2.2 | 51.68 ms | 0.361 ms | 0.320 ms | 51.69 ms |
| intCodeJam | .NET Core 2.2 | .NET Core 2.2 | 35.83 ms | 0.311 ms | 0.275 ms | 35.74 ms |
| intMy | .NET Core 2.2 | .NET Core 2.2 | 31.74 ms | 0.625 ms | 0.522 ms | 31.60 ms |
| intx5CodeJam | .NET Core 2.2 | .NET Core 2.2 | 123.43 ms | 0.978 ms | 0.867 ms | 123.31 ms |
| intx5My | .NET Core 2.2 | .NET Core 2.2 | 98.99 ms | 0.880 ms | 0.780 ms | 98.77 ms |
| longCodeJam | .NET Core 2.2 | .NET Core 2.2 | 34.29 ms | 0.303 ms | 0.283 ms | 34.36 ms |
| longMy | .NET Core 2.2 | .NET Core 2.2 | 30.74 ms | 0.154 ms | 0.128 ms | 30.77 ms |
| longx5CodeJam | .NET Core 2.2 | .NET Core 2.2 | 124.48 ms | 2.454 ms | 2.826 ms | 123.33 ms |
| longx5My | .NET Core 2.2 | .NET Core 2.2 | 101.79 ms | 0.816 ms | 0.763 ms | 101.74 ms |