Cleanup of no longer needed classes. ParallelSynchronizationContext/-Pool renamed to IndexOfSynchronizationContext/-Pool.
This commit is contained in:
1 parent
9ece531129
commit
24c2491df0
9 files changed
+80
-255
No files matched your search
@@ -1,10 +1,11 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Recyclable.Collections.Benchmarks.POC
|
namespace Recyclable.Collections.Benchmarks.POC
|
||||||
{
|
{
|
||||||
public enum WhenParallelSearchBenchmarkType
|
public enum WhenParallelSearchBenchmarkType
|
||||||
{
|
{
|
||||||
IndexOfSequentially,
|
IndexOfSequentially,
|
||||||
IndexOfParallel,
|
IndexOfParallel,
|
||||||
@@ -13,6 +14,62 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class WhenParallelSearchBenchmarks : PocBenchmarkBase<WhenParallelSearchBenchmarkType>
|
public class WhenParallelSearchBenchmarks : PocBenchmarkBase<WhenParallelSearchBenchmarkType>
|
||||||
{
|
{
|
||||||
|
#region ItemRange V2
|
||||||
|
|
||||||
|
private sealed class ItemRangeV2
|
||||||
|
{
|
||||||
|
public int BlockIndex;
|
||||||
|
public long ItemsToSearchCount;
|
||||||
|
internal bool _returned;
|
||||||
|
|
||||||
|
public ItemRangeV2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemRangeV2(int blockIndex, long itemsToSearchCount)
|
||||||
|
{
|
||||||
|
BlockIndex = blockIndex;
|
||||||
|
ItemsToSearchCount = itemsToSearchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_returned)
|
||||||
|
{
|
||||||
|
_returned = true;
|
||||||
|
ItemRangePoolV2.Shared.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ItemRangePoolV2
|
||||||
|
|
||||||
|
private static class ItemRangePoolV2
|
||||||
|
{
|
||||||
|
private static readonly SpinLock _updateLock = new(false);
|
||||||
|
public static readonly ObjectPool<ItemRangeV2> Shared = new DefaultObjectPool<ItemRangeV2>(new DefaultPooledObjectPolicy<ItemRangeV2>());
|
||||||
|
|
||||||
|
public static ItemRangeV2 Create(int blockIndex, long itemsToSearchCount)
|
||||||
|
{
|
||||||
|
bool lockTaken = false;
|
||||||
|
_updateLock.Enter(ref lockTaken);
|
||||||
|
var itemRange = Shared.Get();
|
||||||
|
if (lockTaken)
|
||||||
|
{
|
||||||
|
_updateLock.Exit(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemRange._returned = false;
|
||||||
|
itemRange.BlockIndex = blockIndex;
|
||||||
|
itemRange.ItemsToSearchCount = itemsToSearchCount;
|
||||||
|
return itemRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32)]
|
//[Params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32)]
|
||||||
//public int NumberOfThreads { get; set; } = 3;
|
//public int NumberOfThreads { get; set; } = 3;
|
||||||
|
|
||||||
@@ -66,7 +123,7 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
private static long DoLongIndexOfParallel(RecyclableList<long> list, in long itemToFind, ItemRange searchInfo, ManualResetEventSlim itemFoundSignal)
|
private static long DoLongIndexOfParallel(RecyclableList<long> list, in long itemToFind, ItemRangeV2 searchInfo, ManualResetEventSlim itemFoundSignal)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
int blockSize = list.BlockSize;
|
int blockSize = list.BlockSize;
|
||||||
@@ -248,13 +305,13 @@ namespace Recyclable.Collections.Benchmarks.POC
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override Action? GetTestMethod(WhenParallelSearchBenchmarkType benchmarkType) => BenchmarkType switch
|
protected override Action? GetTestMethod(WhenParallelSearchBenchmarkType benchmarkType) => BenchmarkType switch
|
||||||
{
|
{
|
||||||
WhenParallelSearchBenchmarkType.IndexOfSequentially => IndexOfSequentially,
|
WhenParallelSearchBenchmarkType.IndexOfSequentially => IndexOfSequentially,
|
||||||
WhenParallelSearchBenchmarkType.IndexOfParallel => IndexOfParallel,
|
WhenParallelSearchBenchmarkType.IndexOfParallel => IndexOfParallel,
|
||||||
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
_ => throw CreateUnknownBenchmarkTypeException(benchmarkType),
|
||||||
};
|
};
|
||||||
|
|
||||||
public override void Cleanup()
|
public override void Cleanup()
|
||||||
{
|
{
|
||||||
_testData = default;
|
_testData = default;
|
||||||
base.Cleanup();
|
base.Cleanup();
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
using Recyclable.Collections.Pools;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
public sealed class ItemRange
|
|
||||||
{
|
|
||||||
public int BlockIndex;
|
|
||||||
public long ItemsToSearchCount;
|
|
||||||
internal bool _returned;
|
|
||||||
|
|
||||||
public ItemRange()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemRange(int blockIndex, long itemsToSearchCount)
|
|
||||||
{
|
|
||||||
BlockIndex = blockIndex;
|
|
||||||
ItemsToSearchCount = itemsToSearchCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_returned)
|
|
||||||
{
|
|
||||||
_returned = true;
|
|
||||||
ItemRangePool.Shared.Return(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Recyclable.Collections.Parallel;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections
|
|
||||||
{
|
|
||||||
public static class ItemRangesIterator
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.NoInlining)]
|
|
||||||
public static void Iterate<TList, T>(in ParallelSynchronizationContext context, in SearchConfiguration<TList, T> search, Func<ParallelSynchronizationContext, SearchConfiguration<TList, T>, ItemRange, bool> action)
|
|
||||||
{
|
|
||||||
ItemRange itemRange;
|
|
||||||
int blockIndex = search.StartingBlockIndex;
|
|
||||||
long itemsCount = search.ItemsCount;
|
|
||||||
while (itemsCount > search.BlockSize)
|
|
||||||
{
|
|
||||||
itemRange = new(blockIndex, search.BlockSize);
|
|
||||||
if (!action(context, search, itemRange))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemsCount -= search.BlockSize;
|
|
||||||
blockIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemRange = new(blockIndex, itemsCount);
|
|
||||||
_ = action(context, search, itemRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
namespace Recyclable.Collections.Parallel
|
namespace Recyclable.Collections.Parallel
|
||||||
{
|
{
|
||||||
public sealed class ParallelSynchronizationContext : IDisposable
|
public sealed class IndexOfSynchronizationContext : IDisposable
|
||||||
{
|
{
|
||||||
internal bool _isItemFound;
|
internal bool _isItemFound;
|
||||||
|
|
||||||
@@ -9,12 +9,12 @@ namespace Recyclable.Collections.Parallel
|
|||||||
public Exception? Exception;
|
public Exception? Exception;
|
||||||
internal bool _returned;
|
internal bool _returned;
|
||||||
|
|
||||||
public ParallelSynchronizationContext()
|
public IndexOfSynchronizationContext()
|
||||||
{
|
{
|
||||||
AllDoneSignal = new(0);
|
AllDoneSignal = new(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParallelSynchronizationContext(int participantCount)
|
public IndexOfSynchronizationContext(int participantCount)
|
||||||
{
|
{
|
||||||
AllDoneSignal = new(participantCount);
|
AllDoneSignal = new(participantCount);
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ namespace Recyclable.Collections.Parallel
|
|||||||
if (!_returned)
|
if (!_returned)
|
||||||
{
|
{
|
||||||
_returned = true;
|
_returned = true;
|
||||||
ParallelSynchronizationContextPool.Shared.Return(this);
|
IndexOfSynchronizationContextPool.Shared.Return(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
+3
-3
@@ -2,12 +2,12 @@ using Microsoft.Extensions.ObjectPool;
|
|||||||
|
|
||||||
namespace Recyclable.Collections.Parallel
|
namespace Recyclable.Collections.Parallel
|
||||||
{
|
{
|
||||||
public static class ParallelSynchronizationContextPool
|
public static class IndexOfSynchronizationContextPool
|
||||||
{
|
{
|
||||||
private static readonly SpinLock _updateLock = new(false);
|
private static readonly SpinLock _updateLock = new(false);
|
||||||
public static readonly ObjectPool<ParallelSynchronizationContext> Shared = new DefaultObjectPool<ParallelSynchronizationContext>(new DefaultPooledObjectPolicy<ParallelSynchronizationContext>());
|
public static readonly ObjectPool<IndexOfSynchronizationContext> Shared = new DefaultObjectPool<IndexOfSynchronizationContext>(new DefaultPooledObjectPolicy<IndexOfSynchronizationContext>());
|
||||||
|
|
||||||
public static ParallelSynchronizationContext GetWithOneParticipant()
|
public static IndexOfSynchronizationContext GetWithOneParticipant()
|
||||||
{
|
{
|
||||||
bool lockTaken = false;
|
bool lockTaken = false;
|
||||||
_updateLock.Enter(ref lockTaken);
|
_updateLock.Enter(ref lockTaken);
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Recyclable.Collections.Pools
|
|
||||||
{
|
|
||||||
public static class ItemRangePool
|
|
||||||
{
|
|
||||||
private static readonly SpinLock _updateLock = new(false);
|
|
||||||
public static readonly ObjectPool<ItemRange> Shared = new DefaultObjectPool<ItemRange>(new DefaultPooledObjectPolicy<ItemRange>());
|
|
||||||
|
|
||||||
public static ItemRange Create(int blockIndex, long itemsToSearchCount)
|
|
||||||
{
|
|
||||||
bool lockTaken = false;
|
|
||||||
_updateLock.Enter(ref lockTaken);
|
|
||||||
var itemRange = Shared.Get();
|
|
||||||
if (lockTaken)
|
|
||||||
{
|
|
||||||
_updateLock.Exit(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
itemRange._returned = false;
|
|
||||||
itemRange.BlockIndex = blockIndex;
|
|
||||||
itemRange.ItemsToSearchCount = itemsToSearchCount;
|
|
||||||
return itemRange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-4
@@ -5,12 +5,12 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
public partial class RecyclableList<T>
|
public partial class RecyclableList<T>
|
||||||
{
|
{
|
||||||
public static class RecyclableListIndexOfHelpers
|
public static class IndexOfHelpers
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
||||||
private static void ScheduleIndexOfTask(ParallelSynchronizationContext context, RecyclableList<T> list, int blockIndex, T itemToFind)
|
private static void ScheduleIndexOfTask(IndexOfSynchronizationContext context, RecyclableList<T> list, int blockIndex, T itemToFind)
|
||||||
{
|
{
|
||||||
_ = context.AllDoneSignal.AddParticipant();
|
_ = context.AllDoneSignal.AddParticipant();
|
||||||
// & WAS SLOWER
|
// & WAS SLOWER
|
||||||
@@ -123,7 +123,7 @@ namespace Recyclable.Collections
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
// [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.NoOptimization)]
|
||||||
private static void ScheduleIndexOfTaskSingle(ParallelSynchronizationContext context, RecyclableList<T> list, int blockIndex, T itemToFind)
|
private static void ScheduleIndexOfTaskSingle(IndexOfSynchronizationContext context, RecyclableList<T> list, int blockIndex, T itemToFind)
|
||||||
{
|
{
|
||||||
_ = context.AllDoneSignal.AddParticipant();
|
_ = context.AllDoneSignal.AddParticipant();
|
||||||
// & WAS SLOWER
|
// & WAS SLOWER
|
||||||
@@ -172,7 +172,7 @@ namespace Recyclable.Collections
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
//using var context = new ParallelSynchronizationContext(1);
|
//using var context = new ParallelSynchronizationContext(1);
|
||||||
ParallelSynchronizationContext context = ParallelSynchronizationContextPool.GetWithOneParticipant();
|
IndexOfSynchronizationContext context = IndexOfSynchronizationContextPool.GetWithOneParticipant();
|
||||||
// (long)(_longCount * 0.329) => most efficient step, based on benchmarks
|
// (long)(_longCount * 0.329) => most efficient step, based on benchmarks
|
||||||
//Iterate(this, context, item);
|
//Iterate(this, context, item);
|
||||||
|
|
||||||
@@ -857,7 +857,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization)
|
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization)
|
||||||
{
|
{
|
||||||
return (int)RecyclableListIndexOfHelpers.IndexOfSequential(this, item);
|
return (int)IndexOfHelpers.IndexOfSequential(this, item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -866,7 +866,7 @@ namespace Recyclable.Collections
|
|||||||
if (_longCount > _blockSize)
|
if (_longCount > _blockSize)
|
||||||
{
|
{
|
||||||
itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize));
|
itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize));
|
||||||
return itemIndex >= 0 ? _blockSize + itemIndex : (int)RecyclableListIndexOfHelpers.IndexOfParallel(this, item);
|
return itemIndex >= 0 ? _blockSize + itemIndex : (int)IndexOfHelpers.IndexOfParallel(this, item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -896,7 +896,7 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization)
|
else if (_longCount < RecyclableDefaults.MinItemsCountForParallelization)
|
||||||
{
|
{
|
||||||
return RecyclableListIndexOfHelpers.IndexOfSequential(this, item);
|
return IndexOfHelpers.IndexOfSequential(this, item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -905,7 +905,7 @@ namespace Recyclable.Collections
|
|||||||
if (_longCount > _blockSize)
|
if (_longCount > _blockSize)
|
||||||
{
|
{
|
||||||
itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize));
|
itemIndex = Array.IndexOf(_memoryBlocks[1], item, 0, (int)Math.Min(_blockSize, _longCount - _blockSize));
|
||||||
return itemIndex >= 0 ? _blockSize + itemIndex : RecyclableListIndexOfHelpers.IndexOfParallel(this, item);
|
return itemIndex >= 0 ? _blockSize + itemIndex : IndexOfHelpers.IndexOfParallel(this, item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
using FluentAssertions;
|
|
||||||
using Recyclable.Collections;
|
|
||||||
using Recyclable.Collections.Parallel;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Recyclable.CollectionsTests
|
|
||||||
{
|
|
||||||
public class ItemRangesIteratorTests
|
|
||||||
{
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(TestCases))]
|
|
||||||
public void IterateShouldYieldAllItems(int startingBlockIndex, int blockSize, long itemsCount, int step, IEnumerable<(int BlockIndex, long ItemsCount)> expected)
|
|
||||||
{
|
|
||||||
// Prepare
|
|
||||||
var mockList = new RecyclableList<long>();
|
|
||||||
byte blockSizePow2BitShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize));
|
|
||||||
|
|
||||||
var synchronizationContext = new ParallelSynchronizationContext(1);
|
|
||||||
var search = new SearchConfiguration<RecyclableList<long>, long>()
|
|
||||||
{
|
|
||||||
List = mockList,
|
|
||||||
ItemToFind = 0,
|
|
||||||
BlockSizeMinus1 = blockSize - 1,
|
|
||||||
BlockSizePow2BitShift = blockSizePow2BitShift,
|
|
||||||
ItemsCount = itemsCount,
|
|
||||||
BlockSize = step,
|
|
||||||
};
|
|
||||||
|
|
||||||
var actualItemRanges = new List<(int BlockIndex, long ItemsCount)>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ItemRangesIterator.Iterate(synchronizationContext, search, (context, search, itemRange) =>
|
|
||||||
{
|
|
||||||
actualItemRanges.Add((itemRange.BlockIndex, itemRange.ItemsToSearchCount));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Validate
|
|
||||||
_ = actualItemRanges.Should().HaveCount(expected.Count()).And.ContainInConsecutiveOrder(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> TestCases => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 128, 333, (long)(333 * 0.329), new(int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 109),
|
|
||||||
(0, 109),
|
|
||||||
(1, 109),
|
|
||||||
(2, 6)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 2, 10, 2, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 2),
|
|
||||||
(1, 2),
|
|
||||||
(2, 2),
|
|
||||||
(3, 2),
|
|
||||||
(4, 2),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 2, 10, 3, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 3),
|
|
||||||
(1, 3),
|
|
||||||
(3, 3),
|
|
||||||
(4, 1),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 1, 10, 3, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 3),
|
|
||||||
(3, 3),
|
|
||||||
(6, 3),
|
|
||||||
(9, 1),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 2, 10, 4, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 4),
|
|
||||||
(2, 4),
|
|
||||||
(4, 2),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 1, 10, 2, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 2),
|
|
||||||
(2, 2),
|
|
||||||
(4, 2),
|
|
||||||
(6, 2),
|
|
||||||
(8, 2),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 2, 10, 1, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 1),
|
|
||||||
(0, 1),
|
|
||||||
(1, 1),
|
|
||||||
(1, 1),
|
|
||||||
(2, 1),
|
|
||||||
(2, 1),
|
|
||||||
(3, 1),
|
|
||||||
(3, 1),
|
|
||||||
(4, 1),
|
|
||||||
(4, 1),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, 1, 10, 1, new (int BlockIndex, long ItemsCount)[]
|
|
||||||
{
|
|
||||||
(0, 1),
|
|
||||||
(1, 1),
|
|
||||||
(2, 1),
|
|
||||||
(3, 1),
|
|
||||||
(4, 1),
|
|
||||||
(5, 1),
|
|
||||||
(6, 1),
|
|
||||||
(7, 1),
|
|
||||||
(8, 1),
|
|
||||||
(9, 1),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user