Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
bcb9895dc0 | |||
02e6994b4f | |||
a75596d64b | |||
ae6aece08b | |||
6578d9a08e | |||
b1fba18f7f | |||
4f038c81c5 | |||
c4abad94cc | |||
b72059dfa1 | |||
0d6c719148 | |||
42b7d4349d | |||
b124716f49 | |||
96b1fbf2b6 | |||
75aed77272 | |||
5fc0c90b11 | |||
7e00a48e67 | |||
ed9143f240 | |||
67c29abe79 | |||
0968edab80 | |||
d2ae4895c4 | |||
a791ad6b0a | |||
de75d12140 | |||
c5b26afad0 | |||
1c30d49965 | |||
4dcfa391d2 | |||
c7c77c90fb | |||
242fead373 | |||
7f56894efe | |||
8c0722be44 | |||
3f76c61d94 | |||
79eb5fc9c9 | |||
3c49516f23 | |||
de0da97e95 | |||
62aa37d31f | |||
88aa920231 | |||
239fbd0ce0 | |||
7048a6c5ff | |||
70d069e9bd | |||
db897c51bf | |||
59fd14f64b | |||
7330e702dd | |||
9e97aa6a7d | |||
9a7718b663 |
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
@ -1,15 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace>Exa</RootNamespace>
|
||||
<PackageId>ExaArray</PackageId>
|
||||
<Authors>Thorsten Sommer</Authors>
|
||||
<Company>Thorsten Sommer</Company>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageProjectUrl>https://code.tsommer.org/thorsten/ExaArray</PackageProjectUrl>
|
||||
<PackageProjectUrl>https://github.com/SommerEngineering/ExaArray</PackageProjectUrl>
|
||||
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
|
||||
<RepositoryUrl>https://code.tsommer.org/thorsten/ExaArray</RepositoryUrl>
|
||||
<PackageVersion>1.4.1-rc</PackageVersion>
|
||||
<AssemblyVersion>1.4.1</AssemblyVersion>
|
||||
<FileVersion>1.4.1</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
|
265
ExaArray/ExaArray1D.Factories.cs
Normal file
265
ExaArray/ExaArray1D.Factories.cs
Normal file
@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Exa
|
||||
{
|
||||
public partial class ExaArray1D<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from another.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <c>T</c> is a value type, data gets copied as values. When <c>T</c> is a reference type, the pointers
|
||||
/// to the original objects are copied. Thus, this factory method does not create a deep copy.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="other">The instance from which the new instance is to be created.</param>
|
||||
/// <returns>The new instance</returns>
|
||||
public static ExaArray1D<T> CreateFrom(ExaArray1D<T> other)
|
||||
{
|
||||
var next = new ExaArray1D<T>(other.OptimizationStrategy)
|
||||
{
|
||||
Length = other.Length,
|
||||
chunks = new T[other.chunks.Length][]
|
||||
};
|
||||
|
||||
for (var n = 0; n < other.chunks.Length; n++)
|
||||
{
|
||||
next.chunks[n] = new T[other.chunks[n].Length];
|
||||
Array.Copy(other.chunks[n], next.chunks[n], other.chunks[n].Length);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from another, respecting the given range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <c>T</c> is a value type, data gets copied as values. When <c>T</c> is a reference type, the pointers
|
||||
/// to the original objects are copied. Thus, this factory method does not create a deep copy.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
///
|
||||
/// The indices are inclusive.
|
||||
/// </remarks>
|
||||
/// <param name="other">The instance from which the new instance is to be created.</param>
|
||||
/// <param name="indexFrom">The first source element which should be part of the new array.</param>
|
||||
/// <param name="indexTo">The last source element which should be part of the new array.</param>
|
||||
/// <returns>The new instance</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">Throws, when one or both of the indices are out of range.</exception>
|
||||
public static ExaArray1D<T> CreateFrom(ExaArray1D<T> other, ulong indexFrom, ulong indexTo)
|
||||
{
|
||||
if (indexTo < indexFrom)
|
||||
throw new IndexOutOfRangeException("Index to must be greater than index from.");
|
||||
|
||||
if (indexTo >= other.Length || indexFrom >= other.Length)
|
||||
throw new IndexOutOfRangeException("Index to must be within the range of the source.");
|
||||
|
||||
//
|
||||
// Determine the source start chunk and index.
|
||||
//
|
||||
int sourceChunkIndexTo = -1;
|
||||
int sourceElementIndexTo = -1;
|
||||
switch (other.OptimizationStrategy)
|
||||
{
|
||||
case Strategy.MAX_PERFORMANCE:
|
||||
sourceChunkIndexTo = (int) (indexTo / MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
sourceElementIndexTo = (int) (indexTo - (ulong) sourceChunkIndexTo * MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
break;
|
||||
case Strategy.MAX_ELEMENTS:
|
||||
sourceChunkIndexTo = (int) (indexTo / MAX_CAPACITY_ARRAY);
|
||||
sourceElementIndexTo = (int) (indexTo - (ulong) sourceChunkIndexTo * MAX_CAPACITY_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Determine the source end chunk and index.
|
||||
//
|
||||
int sourceChunkIndexFrom = -1;
|
||||
int sourceElementIndexFrom = -1;
|
||||
switch (other.OptimizationStrategy)
|
||||
{
|
||||
case Strategy.MAX_PERFORMANCE:
|
||||
sourceChunkIndexFrom = (int) (indexFrom / MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
sourceElementIndexFrom = (int) (indexFrom - (ulong) sourceChunkIndexFrom * MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
break;
|
||||
case Strategy.MAX_ELEMENTS:
|
||||
sourceChunkIndexFrom = (int) (indexFrom / MAX_CAPACITY_ARRAY);
|
||||
sourceElementIndexFrom = (int) (indexFrom - (ulong) sourceChunkIndexFrom * MAX_CAPACITY_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
// How many element we have to copy?
|
||||
ulong newRange = indexTo - indexFrom + 1;
|
||||
|
||||
//
|
||||
// Determine, how many total chunks we need for the copy.
|
||||
//
|
||||
int destChunks = -1;
|
||||
switch (other.OptimizationStrategy)
|
||||
{
|
||||
case Strategy.MAX_PERFORMANCE:
|
||||
destChunks = (int) ((newRange - 1) / MAX_CAPACITY_ARRAY_PERFORMANCE) + 1;
|
||||
break;
|
||||
case Strategy.MAX_ELEMENTS:
|
||||
destChunks = (int) ((newRange - 1) / MAX_CAPACITY_ARRAY) + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the copy and allocate the needed number of outer chunk.
|
||||
var next = new ExaArray1D<T>(other.OptimizationStrategy)
|
||||
{
|
||||
Length = newRange,
|
||||
chunks = new T[destChunks][],
|
||||
};
|
||||
|
||||
//
|
||||
// Variables for the copy process.
|
||||
//
|
||||
|
||||
int sourceChunkIndex = sourceChunkIndexFrom;
|
||||
int destinationChunkIndex = 0;
|
||||
int sourceElementIndex = sourceElementIndexFrom;
|
||||
int destinationElementIndex = 0;
|
||||
ulong leftOverTotal = newRange;
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Determine how many elements we copy next.
|
||||
//
|
||||
|
||||
uint numberToCopy = 0;
|
||||
|
||||
// Case: small number of elements from first chunk only
|
||||
if (sourceChunkIndexFrom == sourceChunkIndexTo)
|
||||
numberToCopy = (uint) (sourceElementIndexTo - sourceElementIndexFrom + 1);
|
||||
|
||||
// Case: this is first chunk of multiple chunks + start somewhere in the middle
|
||||
else if (sourceElementIndex > 0)
|
||||
numberToCopy = (uint) (other.maxArrayCapacity - sourceElementIndex);
|
||||
|
||||
// Case: multiple chunks + we are in the middle of huge copy process + next chunk does __not__ exist
|
||||
else if (next.chunks[destinationChunkIndex] == null && sourceElementIndex == 0 && leftOverTotal >= other.maxArrayCapacity)
|
||||
numberToCopy = other.maxArrayCapacity;
|
||||
|
||||
// Case: multiple chunks + we are in the middle of huge copy process + next chunk does exist
|
||||
else if (next.chunks[destinationChunkIndex] != null && sourceElementIndex == 0 && leftOverTotal >= other.maxArrayCapacity)
|
||||
numberToCopy = (uint) (other.maxArrayCapacity - next.chunks[destinationChunkIndex].Length);
|
||||
|
||||
// Case: multiple chunks + this seems to be the last chunk
|
||||
else if (sourceElementIndex == 0 && leftOverTotal < other.maxArrayCapacity)
|
||||
numberToCopy = (uint) leftOverTotal;
|
||||
|
||||
//
|
||||
// Can we allocate an entire chunk or do we have to fill up the existing
|
||||
// chunk first?
|
||||
//
|
||||
if(next.chunks[destinationChunkIndex] == null)
|
||||
next.chunks[destinationChunkIndex] = new T[numberToCopy];
|
||||
else
|
||||
{
|
||||
var extended = new T[next.chunks[destinationChunkIndex].Length + numberToCopy];
|
||||
Array.Copy(next.chunks[destinationChunkIndex], extended, next.chunks[destinationChunkIndex].Length);
|
||||
next.chunks[destinationChunkIndex] = extended;
|
||||
}
|
||||
|
||||
// Copy the data:
|
||||
Array.Copy(other.chunks[sourceChunkIndex], sourceElementIndex, next.chunks[destinationChunkIndex], destinationElementIndex, numberToCopy);
|
||||
|
||||
//
|
||||
// Update the state machine.
|
||||
//
|
||||
var needNewDestinationChunk = next.chunks[destinationChunkIndex].Length == next.maxArrayCapacity;
|
||||
var readNextSourceChunk = sourceElementIndex + numberToCopy == other.maxArrayCapacity;
|
||||
var leftOverCurrentSourceChunk = (int) (readNextSourceChunk ? 0 : other.maxArrayCapacity - sourceElementIndex - numberToCopy);
|
||||
|
||||
leftOverTotal -= numberToCopy;
|
||||
sourceChunkIndex = readNextSourceChunk ? sourceChunkIndex + 1 : sourceChunkIndex;
|
||||
destinationChunkIndex = needNewDestinationChunk ? destinationChunkIndex + 1 : destinationChunkIndex;
|
||||
sourceElementIndex = leftOverCurrentSourceChunk;
|
||||
destinationElementIndex = (int) (needNewDestinationChunk ? 0 : numberToCopy);
|
||||
|
||||
} while (leftOverTotal > 0);
|
||||
return next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from an enumerable sequence of items. The number of items in the sequence is __unknown__.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This factory method is slow because the number of items in the sequence is unknown. When you know the
|
||||
/// number of items, you should use another factory method, where the number of items can be provided.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="sequence">The sequence to consume in order to create the array.</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> CreateFrom(IEnumerable<T> sequence, Strategy strategy = Strategy.MAX_PERFORMANCE)
|
||||
{
|
||||
var inst = new ExaArray1D<T>(strategy);
|
||||
ulong position = 0;
|
||||
foreach (var element in sequence)
|
||||
{
|
||||
inst.Extend();
|
||||
inst[position++] = element;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from an enumerable sequence of items. The number of items in the sequence is __known__.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Creates an array with <c>length</c> items. When the sequence contains less elements, the remaining values are <c>default(T)</c>.
|
||||
/// When the sequence contains more elements, these additional elements getting ignored.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="sequence">The sequence to consume in order to create the array.</param>
|
||||
/// <param name="length">The number of elements in the sequence. When the sequence contains more elements, these additional elements are ignored.</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> CreateFrom(IEnumerable<T> sequence, ulong length, Strategy strategy = Strategy.MAX_PERFORMANCE)
|
||||
{
|
||||
var inst = new ExaArray1D<T>(strategy);
|
||||
inst.Extend(length);
|
||||
|
||||
ulong position = 0;
|
||||
foreach (var element in sequence)
|
||||
{
|
||||
if(position == length)
|
||||
break;
|
||||
|
||||
inst[position++] = element;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from a collection of items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="collection">The collection to use</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> CreateFrom(ICollection<T> collection, Strategy strategy = Strategy.MAX_PERFORMANCE)
|
||||
{
|
||||
var inst = new ExaArray1D<T>(strategy);
|
||||
inst.Extend((ulong) collection.Count);
|
||||
|
||||
ulong position = 0;
|
||||
foreach (var element in collection)
|
||||
inst[position++] = element;
|
||||
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Exa
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents an one-dimensional array with the ability to grow up
|
||||
/// to 4.4 quintillion (4,410,000,000,000,000,000) or 4.4 exa elements.
|
||||
/// to 4.6 quintillion (4,607,183,514,018,780,000) or 4.6 exa elements.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The desired type to use, e.g. byte, int, etc.</typeparam>
|
||||
public sealed class ExaArray1D<T>
|
||||
[Serializable]
|
||||
public sealed partial class ExaArray1D<T> : ISerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Unfortunately, this seems to be the maximal number of entries an
|
||||
/// C# array can hold e.g. of uint. Due to this limitation, this is
|
||||
/// also the maximum number of possible chunks.
|
||||
/// </summary>
|
||||
private const int MAX_CAPACITY_ARRAY = 2_100_000_000;
|
||||
private const uint MAX_CAPACITY_ARRAY = 2_146_435_071; // Thanks @Frassle on Github for the exact number: https://github.com/dotnet/runtime/issues/12221#issuecomment-665553557
|
||||
|
||||
/// <summary>
|
||||
/// The total number of possible elements.
|
||||
/// This is the maximal number of elements per array when we use a number within power of two.
|
||||
/// Then, the divisions are faster.
|
||||
/// </summary>
|
||||
public const ulong MAX_NUMBER_ELEMENTS = 4_410_000_000_000_000_000;
|
||||
private const uint MAX_CAPACITY_ARRAY_PERFORMANCE = 1_073_741_824; // 2^30... Thanks @jkotas on Github: https://github.com/dotnet/runtime/issues/12221#issuecomment-665644665
|
||||
|
||||
/// <summary>
|
||||
/// The total number of possible elements, when using the optimization strategy for max. elements.
|
||||
/// </summary>
|
||||
public const ulong MAX_NUMBER_ELEMENTS = 4_607_183_514_018_780_000;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of possible elements, when using the optimization strategy for max. performance. This is the default.
|
||||
/// </summary>
|
||||
public const ulong MAX_NUMBER_ELEMENTS_PERFORMANCE = 1_152_921_504_606_850_000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured optimization strategy.
|
||||
/// </summary>
|
||||
public Strategy OptimizationStrategy { get; }
|
||||
|
||||
// Chunk storage:
|
||||
private T[][] chunks = new T[1][];
|
||||
|
||||
// Max. array and chunk capacity:
|
||||
private readonly uint maxArrayCapacity;
|
||||
|
||||
// Max. number elements:
|
||||
private readonly ulong maxElements;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty one-dimensional exa-scale array.
|
||||
/// </summary>
|
||||
public ExaArray1D() => this.chunks[0] = new T[0];
|
||||
public ExaArray1D(Strategy strategy = Strategy.MAX_PERFORMANCE)
|
||||
{
|
||||
this.chunks[0] = new T[0];
|
||||
this.OptimizationStrategy = strategy;
|
||||
this.maxElements = this.OptimizationStrategy == Strategy.MAX_PERFORMANCE ? MAX_NUMBER_ELEMENTS_PERFORMANCE : MAX_NUMBER_ELEMENTS;
|
||||
this.maxArrayCapacity = this.OptimizationStrategy == Strategy.MAX_PERFORMANCE ? MAX_CAPACITY_ARRAY_PERFORMANCE : MAX_CAPACITY_ARRAY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extends the array's capacity by some extend.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please ensure, that neither the <c>extendBy</c> parameter nor the total number of
|
||||
/// elements can exceed 4,410,000,000,000,000,000. Otherwise, an <c>ArgumentOutOfRangeException</c>
|
||||
/// elements can exceed 4,607,183,514,018,780,000. Otherwise, an <c>ArgumentOutOfRangeException</c>
|
||||
/// will be thrown. You can use the <see cref="MAX_NUMBER_ELEMENTS"/> constant to perform checks.
|
||||
///
|
||||
/// Performance: O(n) where n is the new total number of elements
|
||||
/// Memory: O(n+m) where <c>n</c> is the necessary memory for the previously elements, and <c>m</c>
|
||||
/// is the memory needed for the desired new capacity.
|
||||
/// </remarks>
|
||||
/// <param name="extendBy">Extend this array by this number of elements. Cannot exceed 4,410,000,000,000,000,000.</param>
|
||||
/// <param name="extendBy">Extend this array by this number of elements. Cannot exceed 4,607,183,514,018,780,000.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Throws, if either the total number of elements or the
|
||||
/// <c>extendBy</c> argument exceeds the limit of 4,410,000,000,000,000,000 elements.</exception>
|
||||
/// <c>extendBy</c> argument exceeds the limit of 4,607,183,514,018,780,000 elements.</exception>
|
||||
public void Extend(ulong extendBy = 1)
|
||||
{
|
||||
if(extendBy > MAX_NUMBER_ELEMENTS || this.Length + extendBy >= MAX_NUMBER_ELEMENTS)
|
||||
throw new ArgumentOutOfRangeException($"It is not possible to extend more than {MAX_NUMBER_ELEMENTS} elements.");
|
||||
|
||||
if (extendBy > this.maxElements || this.Length + extendBy >= this.maxElements)
|
||||
throw new ArgumentOutOfRangeException($"It is not possible to extend more than {this.maxElements} elements.");
|
||||
|
||||
this.Length += extendBy;
|
||||
int availableInCurrentChunk = MAX_CAPACITY_ARRAY - this.chunks[^1]?.Length ?? 0;
|
||||
var availableInCurrentChunk = this.maxArrayCapacity - (ulong) (this.chunks[^1]?.Length ?? 0);
|
||||
if (extendBy >= (ulong)availableInCurrentChunk)
|
||||
{
|
||||
// Extend the current chunk to its max:
|
||||
var extendedInner = new T[MAX_CAPACITY_ARRAY];
|
||||
var extendedInner = new T[this.maxArrayCapacity];
|
||||
Array.Copy(this.chunks[^1], extendedInner, this.chunks[^1].Length);
|
||||
this.chunks[^1] = extendedInner;
|
||||
|
||||
@ -68,8 +100,8 @@ namespace Exa
|
||||
|
||||
do
|
||||
{
|
||||
int allocating = leftOver >= MAX_CAPACITY_ARRAY ? MAX_CAPACITY_ARRAY : (int)leftOver;
|
||||
leftOver -= (ulong) allocating;
|
||||
ulong allocating = leftOver >= this.maxArrayCapacity ? this.maxArrayCapacity : leftOver;
|
||||
leftOver -= allocating;
|
||||
|
||||
// First, we allocate space for the new chunk:
|
||||
var extendedOuter = new T[this.chunks.Length + 1][];
|
||||
@ -111,21 +143,48 @@ namespace Exa
|
||||
{
|
||||
get
|
||||
{
|
||||
if(index >= MAX_NUMBER_ELEMENTS)
|
||||
if(index >= this.maxElements)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
int chunkIndex = -1;
|
||||
int elementIndex = -1;
|
||||
switch (this.OptimizationStrategy)
|
||||
{
|
||||
case Strategy.MAX_PERFORMANCE:
|
||||
chunkIndex = (int) (index / MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
break;
|
||||
case Strategy.MAX_ELEMENTS:
|
||||
chunkIndex = (int) (index / MAX_CAPACITY_ARRAY);
|
||||
elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkIndex >= this.chunks.Length || elementIndex >= this.chunks[chunkIndex].Length)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
int chunkIndex = (int) (index / (ulong)MAX_CAPACITY_ARRAY);
|
||||
int elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY);
|
||||
return this.chunks[chunkIndex][elementIndex];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if(index >= MAX_NUMBER_ELEMENTS)
|
||||
if(index >= this.maxElements)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
int chunkIndex = (int) (index / (ulong)MAX_CAPACITY_ARRAY);
|
||||
int elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY);
|
||||
int chunkIndex = -1;
|
||||
int elementIndex = -1;
|
||||
switch (this.OptimizationStrategy)
|
||||
{
|
||||
case Strategy.MAX_PERFORMANCE:
|
||||
chunkIndex = (int) (index / MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY_PERFORMANCE);
|
||||
break;
|
||||
case Strategy.MAX_ELEMENTS:
|
||||
chunkIndex = (int) (index / MAX_CAPACITY_ARRAY);
|
||||
elementIndex = (int) (index - (ulong) chunkIndex * MAX_CAPACITY_ARRAY);
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkIndex >= this.chunks.Length || elementIndex >= this.chunks[chunkIndex].Length)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
@ -133,6 +192,23 @@ namespace Exa
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this instance, respecting the given range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <c>T</c> is a value type, data gets copied as values. When <c>T</c> is a reference type, the pointers
|
||||
/// to the original objects are copied. Thus, this factory method does not create a deep copy.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
///
|
||||
/// The indices are inclusive.
|
||||
/// </remarks>
|
||||
/// <param name="indexFrom">The first source element which should be part of the new array.</param>
|
||||
/// <param name="indexTo">The last source element which should be part of the new array.</param>
|
||||
/// <returns>The new instance</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">Throws, when one or both of the indices are out of range.</exception>
|
||||
public ExaArray1D<T> this[ulong indexFrom, ulong indexTo] => ExaArray1D<T>.CreateFrom(this, indexFrom, indexTo);
|
||||
|
||||
/// <summary>
|
||||
/// Yields an enumerator across all elements.
|
||||
/// </summary>
|
||||
@ -146,5 +222,72 @@ namespace Exa
|
||||
for (ulong n = 0; n < this.Length; n++)
|
||||
yield return this[n];
|
||||
}
|
||||
|
||||
#region Store and load
|
||||
|
||||
/// <summary>
|
||||
/// Stores the exa array into a stream. <b>Please read the remarks regarding security issues.</b>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The data stored in this way should never be part of a public API. Serializing and
|
||||
/// deserializing is not secure: an attacker can manipulate the data in a targeted
|
||||
/// manner to compromise the API server, etc.
|
||||
///
|
||||
/// This method does not dispose the stream.
|
||||
/// </remarks>
|
||||
public void Store(Stream outputStream)
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(outputStream, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores an exa array from the given stream. <b>Please read the remarks regarding security issues.</b>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The data loaded in this way should never be part of a public API. Serializing and
|
||||
/// deserializing is not secure: an attacker can manipulate the data in a targeted
|
||||
/// manner to compromise the API server, etc.
|
||||
///
|
||||
/// This method does not dispose the stream.
|
||||
/// </remarks>
|
||||
public static ExaArray1D<T> Restore(Stream inputStream)
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
return formatter.Deserialize(inputStream) as ExaArray1D<T>;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
||||
/// <summary>
|
||||
/// This method serves for the serialization process. Do not call it manually.
|
||||
/// </summary>
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("version", "v1");
|
||||
info.AddValue("strategy", this.OptimizationStrategy, typeof(Strategy));
|
||||
info.AddValue("length", this.Length);
|
||||
info.AddValue("chunks", this.chunks, typeof(T[][]));
|
||||
}
|
||||
|
||||
private ExaArray1D(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
switch (info.GetString("version"))
|
||||
{
|
||||
case "v1":
|
||||
this.Length = info.GetUInt64("length");
|
||||
this.chunks = info.GetValue("chunks", typeof(T[][])) as T[][];
|
||||
this.OptimizationStrategy = (Strategy) info.GetValue("strategy", typeof(Strategy));
|
||||
break;
|
||||
}
|
||||
|
||||
this.chunks[0] ??= new T[0];
|
||||
this.maxElements = this.OptimizationStrategy == Strategy.MAX_PERFORMANCE ? MAX_NUMBER_ELEMENTS_PERFORMANCE : MAX_NUMBER_ELEMENTS;
|
||||
this.maxArrayCapacity = this.OptimizationStrategy == Strategy.MAX_PERFORMANCE ? MAX_CAPACITY_ARRAY_PERFORMANCE : MAX_CAPACITY_ARRAY;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
151
ExaArray/ExaArray2D.cs
Normal file
151
ExaArray/ExaArray2D.cs
Normal file
@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Exa
|
||||
{
|
||||
/// <summary>
|
||||
/// The two-dimensional exa-scale array. Can grow up to 18,446,744,073,709,551,615 elements in total.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class ExaArray2D<T> : ISerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// The total number of possible elements.
|
||||
/// </summary>
|
||||
public const ulong MAX_NUMBER_ELEMENTS = ulong.MaxValue;
|
||||
|
||||
private ulong sumLengthOrdinates = 0;
|
||||
|
||||
// Chunk storage:
|
||||
private readonly ExaArray1D<ExaArray1D<T>> chunks = new ExaArray1D<ExaArray1D<T>>(Strategy.MAX_PERFORMANCE);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a two-dimensional exa-scale array.
|
||||
/// </summary>
|
||||
public ExaArray2D()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current total number of elements across all dimensions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Performance: O(1)
|
||||
/// </remarks>
|
||||
public ulong Length => this.sumLengthOrdinates;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an element of the array.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Getting a value: When asking for elements which are not yet allocated, returns <c>default(T)</c>. Performance: O(1).
|
||||
///
|
||||
/// Setting a value: The underlying data structure gets extended on demand as necessary. The array can and will grow
|
||||
/// on demand per index. For example, the abscissa index 5 might have allocated memory for 15 elements while abscissa
|
||||
/// index 16 have allocated memory for 1,000 elements.
|
||||
///
|
||||
/// Performance, when the memory is already allocated: O(1)
|
||||
/// Performance to extend on demand: O(n)
|
||||
///
|
||||
/// On the abscissa, you extend up to 1,152,921,504,606,850,000 entries. Across all dimensions, you can have 18,446,744,073,709,551,615
|
||||
/// elements on total.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Throws, when you tried to extend more than 1,152,921,504,606,850,000 elements on the abscissa
|
||||
/// or you tried to extend to more than 18,446,744,073,709,551,615 elements in total.</exception>
|
||||
public T this[ulong indexAbscissa, ulong indexOrdinate]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.chunks.Length == 0 || indexAbscissa >= this.chunks.Length || indexOrdinate >= this.chunks[indexAbscissa]?.Length)
|
||||
return default(T);
|
||||
|
||||
return this.chunks[indexAbscissa][indexOrdinate];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if(this.chunks.Length == 0 || indexAbscissa >= this.chunks.Length)
|
||||
this.chunks.Extend(indexAbscissa - this.chunks.Length + 1);
|
||||
|
||||
this.chunks[indexAbscissa] ??= new ExaArray1D<T>(Strategy.MAX_PERFORMANCE);
|
||||
if(this.chunks[indexAbscissa].Length == 0 || indexOrdinate >= this.chunks[indexAbscissa].Length - 1)
|
||||
{
|
||||
var extendBy = (indexOrdinate - this.chunks[indexAbscissa].Length) + 1;
|
||||
if(extendBy > MAX_NUMBER_ELEMENTS - this.sumLengthOrdinates)
|
||||
throw new ArgumentOutOfRangeException($"It is not possible to extend more than {MAX_NUMBER_ELEMENTS} total elements across all dimensions.");
|
||||
|
||||
this.chunks[indexAbscissa].Extend(extendBy);
|
||||
this.sumLengthOrdinates += extendBy;
|
||||
}
|
||||
|
||||
this.chunks[indexAbscissa][indexOrdinate] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#region Store and load
|
||||
|
||||
/// <summary>
|
||||
/// Stores the exa array into a stream. <b>Please read the remarks regarding security issues.</b>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The data stored in this way should never be part of a public API. Serializing and
|
||||
/// deserializing is not secure: an attacker can manipulate the data in a targeted
|
||||
/// manner to compromise the API server, etc.
|
||||
///
|
||||
/// This method does not dispose the stream.
|
||||
/// </remarks>
|
||||
public void Store(Stream outputStream)
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(outputStream, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores an exa array from the given stream. <b>Please read the remarks regarding security issues.</b>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The data loaded in this way should never be part of a public API. Serializing and
|
||||
/// deserializing is not secure: an attacker can manipulate the data in a targeted
|
||||
/// manner to compromise the API server, etc.
|
||||
///
|
||||
/// This method does not dispose the stream.
|
||||
/// </remarks>
|
||||
public static ExaArray2D<T> Restore(Stream inputStream)
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
return formatter.Deserialize(inputStream) as ExaArray2D<T>;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
||||
/// <summary>
|
||||
/// This method serves for the serialization process. Do not call it manually.
|
||||
/// </summary>
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("version", "v1");
|
||||
info.AddValue("length", this.Length);
|
||||
info.AddValue("chunks", this.chunks, typeof(ExaArray1D<ExaArray1D<T>>));
|
||||
}
|
||||
|
||||
private ExaArray2D(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
switch (info.GetString("version"))
|
||||
{
|
||||
case "v1":
|
||||
this.sumLengthOrdinates = info.GetUInt64("length");
|
||||
this.chunks = info.GetValue("chunks", typeof(ExaArray1D<ExaArray1D<T>>)) as ExaArray1D<ExaArray1D<T>>;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
41
ExaArray/Extensions.ExaArray1D.cs
Normal file
41
ExaArray/Extensions.ExaArray1D.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace Exa
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <c>ExaArray1D</c>.
|
||||
/// </summary>
|
||||
public static class ExtensionsExaArray1D
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <c>T</c> is a value type, data gets copied as values. When <c>T</c> is a reference type, the pointers
|
||||
/// to the original objects are copied. Thus, this factory method does not create a deep copy.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="other">The instance from which the new instance is to be created.</param>
|
||||
/// <returns>The new instance</returns>
|
||||
public static ExaArray1D<T> Clone<T>(this ExaArray1D<T> other) => ExaArray1D<T>.CreateFrom(other);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this instance, respecting the given range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <c>T</c> is a value type, data gets copied as values. When <c>T</c> is a reference type, the pointers
|
||||
/// to the original objects are copied. Thus, this factory method does not create a deep copy.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
///
|
||||
/// The indices are inclusive.
|
||||
/// </remarks>
|
||||
/// <param name="other">The instance from which the new instance is to be created.</param>
|
||||
/// <param name="indexFrom">The first source element which should be part of the new array.</param>
|
||||
/// <param name="indexTo">The last source element which should be part of the new array.</param>
|
||||
/// <returns>The new instance</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">Throws, when one or both of the indices are out of range.</exception>
|
||||
public static ExaArray1D<T> Clone<T>(this ExaArray1D<T> other, ulong indexFrom, ulong indexTo) => ExaArray1D<T>.CreateFrom(other, indexFrom, indexTo);
|
||||
}
|
||||
}
|
50
ExaArray/Extensions.Framework.cs
Normal file
50
ExaArray/Extensions.Framework.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Exa
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods.
|
||||
/// </summary>
|
||||
public static class ExtensionsFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this collection of items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="collection">The collection to use</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> AsExaArray<T>(this ICollection<T> collection, Strategy strategy = Strategy.MAX_PERFORMANCE) => ExaArray1D<T>.CreateFrom(collection, strategy);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this enumerable sequence of items. The number of items in the sequence is __known__.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Creates an array with <c>length</c> items. When this sequence contains less elements, the remaining values are <c>default(T)</c>.
|
||||
/// When the sequence contains more elements, these additional elements getting ignored.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="sequence">The sequence to consume in order to create the array.</param>
|
||||
/// <param name="length">The number of elements in the sequence. When the sequence contains more elements, these additional elements are ignored.</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> AsExaArray<T>(this IEnumerable<T> sequence, ulong length, Strategy strategy = Strategy.MAX_PERFORMANCE) => ExaArray1D<T>.CreateFrom(sequence, length, strategy);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExaArray1D from this enumerable sequence of items. The number of items in the sequence is __unknown__.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is slow because the number of items in this sequence is unknown. When you know the
|
||||
/// number of items, you should use another factory method, where the number of items can be provided.
|
||||
///
|
||||
/// Performance: O(n)
|
||||
/// </remarks>
|
||||
/// <param name="sequence">The sequence to consume in order to create the array.</param>
|
||||
/// <param name="strategy">The optional optimization strategy.</param>
|
||||
/// <returns>The desired instance</returns>
|
||||
public static ExaArray1D<T> AsExaArray<T>(this IEnumerable<T> sequence, Strategy strategy = Strategy.MAX_PERFORMANCE) => ExaArray1D<T>.CreateFrom(sequence, strategy);
|
||||
}
|
||||
}
|
18
ExaArray/Strategy.cs
Normal file
18
ExaArray/Strategy.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace Exa
|
||||
{
|
||||
/// <summary>
|
||||
/// Different strategies for memory vs. performance handling.
|
||||
/// </summary>
|
||||
public enum Strategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximizes the performance. Thus, only 1.1 quintillion elements can be stored.
|
||||
/// </summary>
|
||||
MAX_PERFORMANCE = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Maximizes the number of available elements. Thus, the full 4.6 quintillion elements are available.
|
||||
/// </summary>
|
||||
MAX_ELEMENTS = 2,
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Exa;
|
||||
@ -7,82 +10,788 @@ using NUnit.Framework;
|
||||
|
||||
namespace ExaArrayTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ExaArray1DTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class InfinityArrayTests
|
||||
[Serializable]
|
||||
private class TestClass
|
||||
{
|
||||
[Test]
|
||||
public void CreatingNormalSize01()
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void CreatingNormalSize01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
Assert.That(exaA.Length, Is.EqualTo(0));
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
Assert.That(exaA.Length, Is.EqualTo(0));
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var n = exaA[0];
|
||||
});
|
||||
Assert.That(exaA.Items(), Is.Empty);
|
||||
var n = exaA[0];
|
||||
});
|
||||
Assert.That(exaA.Items(), Is.Empty);
|
||||
|
||||
exaA.Extend();
|
||||
Assert.That(exaA.Length, Is.EqualTo(1));
|
||||
Assert.That(exaA.Items(), Is.EquivalentTo(new[] {0x00}));
|
||||
Assert.That(exaA[0], Is.EqualTo(0));
|
||||
exaA.Extend();
|
||||
Assert.That(exaA.Length, Is.EqualTo(1));
|
||||
Assert.That(exaA.Items(), Is.EquivalentTo(new[] {0x00}));
|
||||
Assert.That(exaA[0], Is.EqualTo(0));
|
||||
|
||||
exaA[0] = 0xFF;
|
||||
Assert.That(exaA.Length, Is.EqualTo(1));
|
||||
Assert.That(exaA.Items(), Is.EquivalentTo(new[] {0xFF}));
|
||||
Assert.That(exaA[0], Is.EqualTo(0xFF));
|
||||
exaA[0] = 0xFF;
|
||||
Assert.That(exaA.Length, Is.EqualTo(1));
|
||||
Assert.That(exaA.Items(), Is.EquivalentTo(new[] {0xFF}));
|
||||
Assert.That(exaA[0], Is.EqualTo(0xFF));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void CreatingNormalSize02()
|
||||
{
|
||||
var exaA = new ExaArray1D<int>();
|
||||
exaA.Extend(1_000_000);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(1_000_000));
|
||||
for (ulong n = 0; n < 1_000_000; n++)
|
||||
{
|
||||
exaA[n] = (int) (n * n);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreatingNormalSize02()
|
||||
Assert.That(exaA.Items().Count(), Is.EqualTo(1_000_000));
|
||||
|
||||
exaA.Extend();
|
||||
Assert.That(exaA.Length, Is.EqualTo(1_000_001));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void ExtendingTooFar01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
var exaA = new ExaArray1D<int>();
|
||||
exaA.Extend(1_000_000);
|
||||
// Cannot handle more than 1.1 quintillion elements:
|
||||
exaA.Extend(ulong.MaxValue);
|
||||
});
|
||||
}
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(1_000_000));
|
||||
for (ulong n = 0; n < 1_000_000; n++)
|
||||
{
|
||||
exaA[n] = (int) (n * n);
|
||||
}
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void ExtendingToEndFirstChunk01()
|
||||
{
|
||||
const uint MAX = 1_073_741_824;
|
||||
var exaA = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exaA.Extend(MAX - 2);
|
||||
|
||||
Assert.That(exaA.Items().Count(), Is.EqualTo(1_000_000));
|
||||
Assert.That(exaA.Length, Is.EqualTo(MAX - 2));
|
||||
|
||||
exaA.Extend();
|
||||
Assert.That(exaA.Length, Is.EqualTo(1_000_001));
|
||||
exaA.Extend(2);
|
||||
Assert.That(exaA.Length, Is.EqualTo(MAX));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void GetInvalidIndex01()
|
||||
{
|
||||
var exaPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exaPerf.Extend(2);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var t = exaPerf[ulong.MaxValue]; // Because index >= max
|
||||
});
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var t1 = exaPerf[0];
|
||||
var t2 = exaPerf[1];
|
||||
});
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var t = exaPerf[2]; // Because we don't allocate
|
||||
});
|
||||
|
||||
exaPerf = null;
|
||||
|
||||
|
||||
var exaElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exaElem.Extend(2);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var t = exaElem[ulong.MaxValue]; // Because index >= max
|
||||
});
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var t1 = exaElem[0];
|
||||
var t2 = exaElem[1];
|
||||
});
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var t = exaElem[2]; // Because we don't allocate
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void SetInvalidIndex01()
|
||||
{
|
||||
var exaPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exaPerf.Extend(2);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
exaPerf[ulong.MaxValue] = 0x00; // Because index >= max
|
||||
});
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
exaPerf[0] = 0x01;
|
||||
exaPerf[1] = 0x02;
|
||||
});
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var t = exaPerf[2] = 0x03; // Because we don't allocate
|
||||
});
|
||||
|
||||
exaPerf = null;
|
||||
|
||||
|
||||
var exaElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exaElem.Extend(2);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
exaElem[ulong.MaxValue] = 0x00; // Because index >= max
|
||||
});
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
exaElem[0] = 0x01;
|
||||
exaElem[1] = 0x02;
|
||||
});
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
exaElem[2] = 0x03; // Because we don't allocate
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("intensive")]
|
||||
public void CountingHugeSize01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000_000);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(5_000_000_000));
|
||||
Assert.Throws<OverflowException>(() =>
|
||||
{
|
||||
// Cannot work because linq uses int:
|
||||
var n = exaA.Items().Count();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFrom001()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(2);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf);
|
||||
Assert.That(next.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exPerf.OptimizationStrategy));
|
||||
Assert.That(next.Items(), Is.EquivalentTo(exPerf.Items()));
|
||||
|
||||
exPerf = null;
|
||||
next = null;
|
||||
|
||||
var exElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exElem.Extend(2);
|
||||
exElem[0] = 0x03;
|
||||
exElem[1] = 0x04;
|
||||
|
||||
next = ExaArray1D<byte>.CreateFrom(exElem);
|
||||
Assert.That(next.Length, Is.EqualTo(exElem.Length));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exElem.OptimizationStrategy));
|
||||
Assert.That(next.Items(), Is.EquivalentTo(exElem.Items()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFrom002Objects()
|
||||
{
|
||||
var exPerf = new ExaArray1D<object>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(2);
|
||||
exPerf[0] = new object();
|
||||
exPerf[1] = new object();
|
||||
|
||||
var next = ExaArray1D<object>.CreateFrom(exPerf);
|
||||
Assert.That(next.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exPerf.OptimizationStrategy));
|
||||
Assert.That(next[0], Is.SameAs(exPerf[0]));
|
||||
Assert.That(next[1], Is.SameAs(exPerf[1]));
|
||||
Assert.That(next[0], Is.Not.SameAs(next[1]));
|
||||
|
||||
exPerf = null;
|
||||
next = null;
|
||||
|
||||
var exElem = new ExaArray1D<object>(Strategy.MAX_ELEMENTS);
|
||||
exElem.Extend(2);
|
||||
exElem[0] = new object();
|
||||
exElem[1] = new object();
|
||||
|
||||
next = ExaArray1D<object>.CreateFrom(exElem);
|
||||
Assert.That(next.Length, Is.EqualTo(exElem.Length));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exElem.OptimizationStrategy));
|
||||
Assert.That(next[0], Is.SameAs(exElem[0]));
|
||||
Assert.That(next[1], Is.SameAs(exElem[1]));
|
||||
Assert.That(next[0], Is.Not.SameAs(next[1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFrom003()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(5_000_000_000); // more than one chunk
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf);
|
||||
Assert.That(next.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.DoesNotThrow(() => { next[4_999_999_999] = 0xab; });
|
||||
|
||||
exPerf = null;
|
||||
next = null;
|
||||
|
||||
var exElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exElem.Extend(5_000_000_000);
|
||||
|
||||
next = ExaArray1D<byte>.CreateFrom(exElem);
|
||||
Assert.That(next.Length, Is.EqualTo(exElem.Length));
|
||||
Assert.DoesNotThrow(() => { next[4_999_999_999] = 0xab; });
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFrom004()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf);
|
||||
Assert.That(next.Length, Is.EqualTo(0));
|
||||
|
||||
exPerf = null;
|
||||
next = null;
|
||||
|
||||
var exElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
next = ExaArray1D<byte>.CreateFrom(exElem);
|
||||
Assert.That(next.Length, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFrom005Objects()
|
||||
{
|
||||
var exPerf = new ExaArray1D<TestClass>();
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = new TestClass {Age = 5};
|
||||
exPerf[1] = new TestClass {Age = 10};
|
||||
exPerf[2] = new TestClass {Age = 45};
|
||||
|
||||
var next = ExaArray1D<TestClass>.CreateFrom(exPerf);
|
||||
Assert.That(next.Length, Is.EqualTo(3));
|
||||
Assert.That(next[1].Age, Is.EqualTo(10));
|
||||
|
||||
next[1].Age = 50;
|
||||
Assert.That(next[1].Age, Is.EqualTo(50));
|
||||
Assert.That(exPerf[1].Age, Is.EqualTo(50));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange001()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
exPerf[2] = 0x03;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 0, 1);
|
||||
Assert.That(next.Length, Is.EqualTo(2));
|
||||
Assert.That(next[0], Is.EqualTo(exPerf[0]));
|
||||
Assert.That(next[1], Is.EqualTo(exPerf[1]));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exPerf.OptimizationStrategy));
|
||||
|
||||
exPerf = null;
|
||||
next = null;
|
||||
|
||||
var exElem = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exElem.Extend(3);
|
||||
exElem[0] = 0x01;
|
||||
exElem[1] = 0x02;
|
||||
exElem[2] = 0x03;
|
||||
|
||||
next = ExaArray1D<byte>.CreateFrom(exElem, 1, 2);
|
||||
Assert.That(next.Length, Is.EqualTo(2));
|
||||
Assert.That(next[0], Is.EqualTo(exElem[1]));
|
||||
Assert.That(next[1], Is.EqualTo(exElem[2]));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(exElem.OptimizationStrategy));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange002()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
exPerf[2] = 0x03;
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 100, 200);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange003()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
exPerf[2] = 0x03;
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 0, 200);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange004()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
exPerf[2] = 0x03;
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 200, 100);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange005()
|
||||
{
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = 0x01;
|
||||
exPerf[1] = 0x02;
|
||||
exPerf[2] = 0x03;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 2, 2);
|
||||
Assert.That(next.Length, Is.EqualTo(1));
|
||||
Assert.That(next.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
Assert.That(next[0], Is.EqualTo(exPerf[2]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange006Objects()
|
||||
{
|
||||
var exPerf = new ExaArray1D<TestClass>();
|
||||
exPerf.Extend(3);
|
||||
exPerf[0] = new TestClass {Age = 5};
|
||||
exPerf[1] = new TestClass {Age = 10};
|
||||
exPerf[2] = new TestClass {Age = 45};
|
||||
|
||||
var next = ExaArray1D<TestClass>.CreateFrom(exPerf, 2, 2);
|
||||
Assert.That(next.Length, Is.EqualTo(1));
|
||||
Assert.That(next[0].Age, Is.EqualTo(45));
|
||||
|
||||
next[0].Age = 50;
|
||||
Assert.That(next[0].Age, Is.EqualTo(50));
|
||||
Assert.That(exPerf[2].Age, Is.EqualTo(50));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange007()
|
||||
{
|
||||
const uint MAX = 1_073_741_824;
|
||||
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(MAX + 3); // more than one chunk
|
||||
exPerf[MAX - 1 + 2] = 0x01;
|
||||
exPerf[MAX - 1 + 1] = 0x02;
|
||||
exPerf[MAX - 1 - 0] = 0x03;
|
||||
exPerf[MAX - 1 - 1] = 0x04;
|
||||
exPerf[MAX - 1 - 2] = 0x05;
|
||||
exPerf[MAX - 1 - 3] = 0x06;
|
||||
exPerf[MAX - 1 - 4] = 0x07;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, MAX - 1 - 2, MAX - 1 + 2);
|
||||
Assert.That(next.Length, Is.EqualTo(5));
|
||||
Assert.That(next[0], Is.EqualTo(exPerf[MAX - 1 - 2]));
|
||||
Assert.That(next[1], Is.EqualTo(exPerf[MAX - 1 - 1]));
|
||||
Assert.That(next[2], Is.EqualTo(exPerf[MAX - 1 - 0]));
|
||||
Assert.That(next[3], Is.EqualTo(exPerf[MAX - 1 + 1]));
|
||||
Assert.That(next[4], Is.EqualTo(exPerf[MAX - 1 + 2]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange008()
|
||||
{
|
||||
const uint MAX = 1_073_741_824;
|
||||
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3 * MAX + 3); // more than one chunk
|
||||
exPerf[MAX - 1 + 2] = 0x01;
|
||||
exPerf[MAX - 1 + 1] = 0x02;
|
||||
exPerf[MAX - 1 - 0] = 0x03;
|
||||
exPerf[MAX - 1 - 1] = 0x04;
|
||||
exPerf[MAX - 1 - 2] = 0x05;
|
||||
exPerf[MAX - 1 - 3] = 0x06;
|
||||
exPerf[MAX - 1 - 4] = 0x07;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, MAX - 1 - 2, MAX - 1 + 2);
|
||||
Assert.That(next.Length, Is.EqualTo(5));
|
||||
Assert.That(next[0], Is.EqualTo(exPerf[MAX - 1 - 2]));
|
||||
Assert.That(next[1], Is.EqualTo(exPerf[MAX - 1 - 1]));
|
||||
Assert.That(next[2], Is.EqualTo(exPerf[MAX - 1 - 0]));
|
||||
Assert.That(next[3], Is.EqualTo(exPerf[MAX - 1 + 1]));
|
||||
Assert.That(next[4], Is.EqualTo(exPerf[MAX - 1 + 2]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange009()
|
||||
{
|
||||
const uint MAX = 1_073_741_824;
|
||||
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3 * MAX + 3); // more than one chunk
|
||||
exPerf[100_000_000] = 0xFF;
|
||||
exPerf[3 * MAX - 1 + 2] = 0x01;
|
||||
exPerf[3 * MAX - 1 + 1] = 0x02;
|
||||
exPerf[3 * MAX - 1 - 0] = 0x03;
|
||||
exPerf[3 * MAX - 1 - 1] = 0x04;
|
||||
exPerf[3 * MAX - 1 - 2] = 0x05;
|
||||
exPerf[3 * MAX - 1 - 3] = 0x06;
|
||||
exPerf[3 * MAX - 1 - 4] = 0x07;
|
||||
|
||||
var next = ExaArray1D<byte>.CreateFrom(exPerf, 100_000_000, 3 * MAX - 1 + 2);
|
||||
Assert.That(next.Length, Is.EqualTo(exPerf.Length - 100_000_000 - 1));
|
||||
Assert.That(next[0], Is.EqualTo(0xFF));
|
||||
|
||||
Assert.That(next[next.Length - 1 - 4], Is.EqualTo(exPerf[3 * MAX - 1 - 2]));
|
||||
Assert.That(next[next.Length - 1 - 3], Is.EqualTo(exPerf[3 * MAX - 1 - 1]));
|
||||
Assert.That(next[next.Length - 1 - 2], Is.EqualTo(exPerf[3 * MAX - 1 - 0]));
|
||||
Assert.That(next[next.Length - 1 - 1], Is.EqualTo(exPerf[3 * MAX - 1 + 1]));
|
||||
Assert.That(next[next.Length - 1 - 0], Is.EqualTo(exPerf[3 * MAX - 1 + 2]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromRange010()
|
||||
{
|
||||
const uint MAX = 1_073_741_824;
|
||||
|
||||
var exPerf = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exPerf.Extend(3 * MAX); // more than one chunk
|
||||
|
||||
var next1 = ExaArray1D<byte>.CreateFrom(exPerf, 0, exPerf.Length - 1);
|
||||
var next2 = exPerf.Clone();
|
||||
var next3 = exPerf.Clone(0, exPerf.Length - 1);
|
||||
var next4 = exPerf[0, exPerf.Length - 1];
|
||||
|
||||
Assert.That(next1.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.That(next2.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.That(next3.Length, Is.EqualTo(exPerf.Length));
|
||||
Assert.That(next4.Length, Is.EqualTo(exPerf.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromSequence001()
|
||||
{
|
||||
var list = new List<int>
|
||||
{
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
16,
|
||||
};
|
||||
|
||||
var exPerf1 = list.AsExaArray();
|
||||
Assert.That(exPerf1.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
Assert.That(exPerf1.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf1.Items(), Is.EquivalentTo(list));
|
||||
|
||||
var exPerf2 = list.AsExaArray(Strategy.MAX_ELEMENTS);
|
||||
Assert.That(exPerf2.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
Assert.That(exPerf2.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf2.Items(), Is.EquivalentTo(list));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromSequence002()
|
||||
{
|
||||
var array = new int[]
|
||||
{
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
16,
|
||||
};
|
||||
|
||||
var exPerf1 = array.AsExaArray();
|
||||
Assert.That(exPerf1.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
Assert.That(exPerf1.Length, Is.EqualTo(array.Length));
|
||||
Assert.That(exPerf1.Items(), Is.EquivalentTo(array));
|
||||
|
||||
var exPerf2 = array.AsExaArray(Strategy.MAX_ELEMENTS);
|
||||
Assert.That(exPerf2.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
Assert.That(exPerf2.Length, Is.EqualTo(array.Length));
|
||||
Assert.That(exPerf2.Items(), Is.EquivalentTo(array));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromSequence003()
|
||||
{
|
||||
var list = new List<int>
|
||||
{
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
16,
|
||||
};
|
||||
|
||||
var exPerf1 = list.AsEnumerable().AsExaArray();
|
||||
Assert.That(exPerf1.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
Assert.That(exPerf1.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf1.Items(), Is.EquivalentTo(list));
|
||||
|
||||
var exPerf2 = list.AsEnumerable().AsExaArray(Strategy.MAX_ELEMENTS);
|
||||
Assert.That(exPerf2.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
Assert.That(exPerf2.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf2.Items(), Is.EquivalentTo(list));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("normal")]
|
||||
[Category("cover")]
|
||||
public void CreateFromSequence004()
|
||||
{
|
||||
var list = new List<int>
|
||||
{
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
16,
|
||||
};
|
||||
|
||||
var exPerf1 = list.AsEnumerable().AsExaArray((ulong) list.Count);
|
||||
Assert.That(exPerf1.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
Assert.That(exPerf1.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf1.Items(), Is.EquivalentTo(list));
|
||||
|
||||
var exPerf2 = list.AsEnumerable().AsExaArray((ulong) list.Count, Strategy.MAX_ELEMENTS);
|
||||
Assert.That(exPerf2.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
Assert.That(exPerf2.Length, Is.EqualTo(list.Count));
|
||||
Assert.That(exPerf2.Items(), Is.EquivalentTo(list));
|
||||
|
||||
var exPerf3 = list.AsEnumerable().AsExaArray(3);
|
||||
Assert.That(exPerf3.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
Assert.That(exPerf3.Length, Is.EqualTo(3));
|
||||
Assert.That(exPerf3.Items(), Is.EquivalentTo(new int[] {5, 7, 8}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("intensive")]
|
||||
public void Adding5Billion01()
|
||||
{
|
||||
ulong sum1 = 0;
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000_000);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(5_000_000_000));
|
||||
for (ulong n = 0; n < 5_000_000_000; n++)
|
||||
{
|
||||
exaA[n] = 1;
|
||||
sum1 += 1;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CountingHugeSize01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000_000);
|
||||
var sum2 = exaA.Items().Aggregate<byte, ulong>(0, (current, item) => current + item);
|
||||
Assert.That(sum1, Is.EqualTo(sum2));
|
||||
}
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(5_000_000_000));
|
||||
Assert.Throws<OverflowException>(() =>
|
||||
{
|
||||
// Cannot work because linq uses int:
|
||||
var n = exaA.Items().Count();
|
||||
});
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void Using5Billion01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000_000);
|
||||
Assert.That(exaA.Length, Is.EqualTo(5_000_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void TestStrategies01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
Assert.That(exaA.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
|
||||
exaA = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
Assert.That(exaA.OptimizationStrategy, Is.EqualTo(Strategy.MAX_PERFORMANCE));
|
||||
|
||||
exaA = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
Assert.That(exaA.OptimizationStrategy, Is.EqualTo(Strategy.MAX_ELEMENTS));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("performance")]
|
||||
public void TestPerformance01()
|
||||
{
|
||||
var t1Times = new long[10];
|
||||
var t2Times = new long[10];
|
||||
|
||||
var exaMaxElements = new ExaArray1D<byte>(Strategy.MAX_ELEMENTS);
|
||||
exaMaxElements.Extend(5_000_000_000);
|
||||
|
||||
// Take 10 samples:
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var t1 = new Stopwatch();
|
||||
t1.Start();
|
||||
|
||||
for (ulong n = 0; n < 100_000_000; n++)
|
||||
exaMaxElements[n] = 0x55;
|
||||
t1.Stop();
|
||||
t1Times[i] = t1.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Adding5Billion01()
|
||||
TestContext.WriteLine($"Performing 100M assignments took {t1Times.Average()} ms (average) by means of the max. elements strategy (min={t1Times.Min()} ms, max={t1Times.Max()} ms)");
|
||||
exaMaxElements = null;
|
||||
|
||||
var exaMaxPerformance = new ExaArray1D<byte>(Strategy.MAX_PERFORMANCE);
|
||||
exaMaxPerformance.Extend(5_000_000_000);
|
||||
|
||||
// Take 10 samples:
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
ulong sum1 = 0;
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000_000);
|
||||
var t2 = new Stopwatch();
|
||||
t2.Start();
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(5_000_000_000));
|
||||
for (ulong n = 0; n < 5_000_000_000; n++)
|
||||
{
|
||||
exaA[n] = 1;
|
||||
sum1 += 1;
|
||||
}
|
||||
|
||||
var sum2 = exaA.Items().Aggregate<byte, ulong>(0, (current, item) => current + item);
|
||||
Assert.That(sum1, Is.EqualTo(sum2));
|
||||
for (ulong n = 0; n < 100_000_000; n++)
|
||||
exaMaxPerformance[n] = 0x55;
|
||||
t2.Stop();
|
||||
t2Times[i] = t2.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
TestContext.WriteLine($"Performing 100M assignments took {t2Times.Average()} ms (average) by means of the max. performance strategy (min={t2Times.Min()} ms, max={t2Times.Max()} ms)");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void StoreAndLoad01()
|
||||
{
|
||||
var exaA = new ExaArray1D<byte>();
|
||||
exaA.Extend(5_000_000);
|
||||
exaA[4_483_124] = 0xff;
|
||||
|
||||
var filename = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
using (var file = File.OpenWrite(filename))
|
||||
{
|
||||
exaA.Store(file);
|
||||
}
|
||||
|
||||
using (var file = File.OpenRead(filename))
|
||||
{
|
||||
var exaB = ExaArray1D<byte>.Restore(file);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(exaB.Length));
|
||||
Assert.That(exaA[4_483_124], Is.EqualTo(0xff));
|
||||
Assert.That(exaB[4_483_124], Is.EqualTo(0xff));
|
||||
}
|
||||
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void StoreAndLoad02()
|
||||
{
|
||||
var exaA = new ExaArray1D<TestClass>();
|
||||
exaA.Extend(100);
|
||||
exaA[66] = new TestClass
|
||||
{
|
||||
Age = 55,
|
||||
};
|
||||
|
||||
var filename = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
using (var file = File.OpenWrite(filename))
|
||||
{
|
||||
exaA.Store(file);
|
||||
}
|
||||
|
||||
using (var file = File.OpenRead(filename))
|
||||
{
|
||||
var exaB = ExaArray1D<TestClass>.Restore(file);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(exaB.Length));
|
||||
Assert.That(exaA[66].Age, Is.EqualTo(55));
|
||||
Assert.That(exaB[66].Age, Is.EqualTo(55));
|
||||
}
|
||||
|
||||
File.Delete(filename);
|
||||
}
|
||||
}
|
||||
}
|
103
ExaArrayTests/ExaArray2DTests.cs
Normal file
103
ExaArrayTests/ExaArray2DTests.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Exa;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ExaArrayTests
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class ExaArray2DTests
|
||||
{
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void CreatingNormalSize01()
|
||||
{
|
||||
var arr = new ExaArray2D<int>();
|
||||
|
||||
// Empty array must have length = 0:
|
||||
Assert.That(arr.Length, Is.EqualTo(0));
|
||||
|
||||
// Try to access some points, which should return default(T):
|
||||
Assert.That(arr[0, 0], Is.EqualTo(default(int)));
|
||||
Assert.That(arr[1_000_000, 1_000_000], Is.EqualTo(default(int)));
|
||||
|
||||
// Even after accessing some points, there should be no space allocated i.e. length is still 0:
|
||||
Assert.That(arr.Length, Is.EqualTo(0));
|
||||
|
||||
// Write an int to a position:
|
||||
arr[500, 500] = 4_756;
|
||||
|
||||
// Now, we have 500 empty "rows" + 1 "row" with 501 (0-500) elements:
|
||||
Assert.That(arr.Length, Is.EqualTo(501));
|
||||
|
||||
// Should be possible to read out the value:
|
||||
Assert.That(arr[500, 500], Is.EqualTo(4_756));
|
||||
|
||||
// Change the value:
|
||||
arr[500, 500] = 100;
|
||||
Assert.That(arr[500, 500], Is.EqualTo(100));
|
||||
|
||||
// Still the same size:
|
||||
Assert.That(arr.Length, Is.EqualTo(501));
|
||||
|
||||
// Add another value in the same "row":
|
||||
arr[500, 499] = 499;
|
||||
Assert.That(arr[500, 499], Is.EqualTo(499));
|
||||
Assert.That(arr[500, 500], Is.EqualTo(100));
|
||||
|
||||
// Now, we should have still 501 elements, because
|
||||
// we added the new value "below" the previously:
|
||||
Assert.That(arr.Length, Is.EqualTo(501));
|
||||
|
||||
// Add another value in the same "row", but higher:
|
||||
arr[500, 1_000] = 6;
|
||||
Assert.That(arr[500, 499], Is.EqualTo(499));
|
||||
Assert.That(arr[500, 500], Is.EqualTo(100));
|
||||
Assert.That(arr[500, 1_000], Is.EqualTo(6));
|
||||
|
||||
// Now we should have more elements:
|
||||
Assert.That(arr.Length, Is.EqualTo(1_001));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void CreatingHugeSize01()
|
||||
{
|
||||
var arr = new ExaArray2D<int>();
|
||||
arr[0, 1_000_000] = 47;
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
arr[1, UInt64.MaxValue - 1] = 6;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Category("cover")]
|
||||
[Category("normal")]
|
||||
public void StoreAndLoad01()
|
||||
{
|
||||
var exaA = new ExaArray2D<byte>();
|
||||
exaA[5_000_124, 5_000_666] = 0xff;
|
||||
|
||||
var filename = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
using (var file = File.OpenWrite(filename))
|
||||
{
|
||||
exaA.Store(file);
|
||||
}
|
||||
|
||||
using (var file = File.OpenRead(filename))
|
||||
{
|
||||
var exaB = ExaArray2D<byte>.Restore(file);
|
||||
|
||||
Assert.That(exaA.Length, Is.EqualTo(exaB.Length));
|
||||
Assert.That(exaA[5_000_124, 5_000_666], Is.EqualTo(0xff));
|
||||
Assert.That(exaB[5_000_124, 5_000_666], Is.EqualTo(0xff));
|
||||
}
|
||||
|
||||
File.Delete(filename);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
@ -1,7 +1,6 @@
|
||||
# ExaArray
|
||||
ExaArray is a .NET library for exa-scale array-like structures. By using this library, it becomes possible to
|
||||
add up to 4.4 quintillion i.e. 4,410,000,000,000,000,000 elements into one array. When using `byte` for `T`,
|
||||
this would need approx. 3.8 EB of memory.
|
||||
ExaArray is a .NET library for exa-scale array-like structures. By using this library, it becomes possible to add up to 4.6 quintillion i.e. 4,607,183,514,018,780,000 elements into a one-dimensional array. When using `byte` for `T`, this would need approx. 4 EB of memory. The two-dimensional array can grow up to 18.4 quintillion i.e. 18,446,744,073,709,551,615 elements, though.
|
||||
|
||||
Extending the data structure performs as O(n) with O(m+n) of memory. Accessing the data performs as O(1), though.
|
||||
For the generic type `T`, any .NET type can be used: The ExaArray uses managed memory.
|
||||
Extending the data structure performs as O(n) with O(m+n) of memory. Accessing the data performs as O(1), though. For the generic type `T`, any .NET type can be used: The ExaArray uses managed memory.
|
||||
|
||||
Storing to and loading from an arbitrary stream is supported. The data stored in this way should never be part of a public API. Serializing and deserializing is not secure: an attacker can manipulate the data in a targeted manner to compromise the API server, etc.
|
Loading…
Reference in New Issue
Block a user