Int64 Index Migration Plan
Migration from int (int32) to long (int64) for all index, stride, offset, and size operations.
Rationale: Support arrays >2GB (int32 max = 2.1B elements). NumPy uses npy_intp = Py_ssize_t (64-bit on x64).
Performance Impact: Benchmarked at 1-3% overhead for scalar loops, <1% for SIMD loops. Acceptable.
Conversion Strategy: int ↔ long Handling
C# Conversion Rules
| Conversion |
Type |
Notes |
int → long |
Implicit |
Always safe, zero cost |
long → int |
Explicit |
Requires cast, may overflow |
int[] → long[] |
Manual |
Element-by-element conversion required |
long[] → int[] |
Manual |
Element-by-element + overflow check |
Why Pointer Arithmetic Works
NumSharp uses unmanaged memory (byte*), not managed arrays. Pointer arithmetic natively supports long offsets:
byte* ptr = baseAddress;
long largeOffset = 3_000_000_000L; // > int.MaxValue
byte* result = ptr + largeOffset; // WORKS! No cast needed
This is why we can migrate internally to long without breaking memory access.
Public API Strategy: Dual Overloads
Keep int overloads for backward compatibility, delegate to long internally:
// Single index - int delegates to long (zero-cost implicit conversion)
public T this[int index] => this[(long)index];
public T this[long index] { get; set; } // Main implementation
// Multi-index - int[] converts to long[] with stackalloc optimization
public NDArray this[params int[] indices]
{
get
{
// Stack alloc for common case (<=8 dims), heap for rare large case
Span<long> longIndices = indices.Length <= 8
? stackalloc long[indices.Length]
: new long[indices.Length];
for (int i = 0; i < indices.Length; i++)
longIndices[i] = indices[i];
return GetByIndicesInternal(longIndices);
}
}
public NDArray this[params long[] indices] // Main implementation
{
get => GetByIndicesInternal(indices);
}
Shape Constructor Overloads
// Backward compatible - accept int[]
public Shape(params int[] dims) : this(ToLongArray(dims)) { }
// New primary constructor - long[]
public Shape(params long[] dims)
{
this.dimensions = dims;
// ... rest of initialization
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long[] ToLongArray(int[] arr)
{
var result = new long[arr.Length];
for (int i = 0; i < arr.Length; i++)
result[i] = arr[i];
return result;
}
Backward Compatible Properties
// Keep int[] for backward compat, throw on overflow
public int[] shape
{
get
{
var dims = Shape.dimensions; // Internal long[]
var result = new int[dims.Length];
for (int i = 0; i < dims.Length; i++)
{
if (dims[i] > int.MaxValue)
throw new OverflowException(
$"Dimension {i} size {dims[i]} exceeds int.MaxValue. Use shapeLong property.");
result[i] = (int)dims[i];
}
return result;
}
}
// New property for large arrays
public long[] shapeLong => Shape.dimensions;
// size - same pattern
public int size => Size > int.MaxValue
? throw new OverflowException("Array size exceeds int.MaxValue. Use sizeLong.")
: (int)Size;
public long sizeLong => Size; // New property
What Stays int (No Change Needed)
| Member |
Reason |
NDim / ndim |
Max ~32 dimensions, never exceeds int |
Slice.Start/Stop/Step |
Python slice semantics use int |
| Loop counters in IL (where safe) |
JIT optimizes better |
NPTypeCode enum values |
Small fixed set |
Conversion Helper Methods
internal static class IndexConvert
{
/// <summary>Throws if value exceeds int range.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToIntChecked(long value)
{
if (value > int.MaxValue || value < int.MinValue)
throw new OverflowException($"Value {value} exceeds int range");
return (int)value;
}
/// <summary>Converts long[] to int[], throws on overflow.</summary>
public static int[] ToIntArrayChecked(long[] arr)
{
var result = new int[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] > int.MaxValue)
throw new OverflowException($"Index {i} value {arr[i]} exceeds int.MaxValue");
result[i] = (int)arr[i];
}
return result;
}
/// <summary>Converts int[] to long[] (always safe).</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long[] ToLongArray(int[] arr)
{
var result = new long[arr.Length];
for (int i = 0; i < arr.Length; i++)
result[i] = arr[i];
return result;
}
/// <summary>Converts int[] to Span<long> using stackalloc.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<long> ToLongSpan(int[] arr, Span<long> buffer)
{
for (int i = 0; i < arr.Length; i++)
buffer[i] = arr[i];
return buffer.Slice(0, arr.Length);
}
}
IL Kernel Considerations
For IL-generated kernels, loop counters can often stay int when:
- Array size is guaranteed < int.MaxValue (checked at call site)
- Counter is only used for iteration, not offset calculation
Offset calculations must use long:
// Before: int offset = baseOffset + i * stride;
// After: long offset = baseOffset + (long)i * stride;
Phase 1: Core Types (CRITICAL PATH)
These changes cascade to everything else. Must be done atomically.
1.1 Shape Struct (View/Shape.cs)
| Current |
Change To |
Lines |
internal readonly int size |
long size |
208 |
internal readonly int[] dimensions |
long[] dimensions |
209 |
internal readonly int[] strides |
long[] strides |
210 |
internal readonly int bufferSize |
long bufferSize |
218 |
internal readonly int offset |
long offset |
225 |
public readonly int OriginalSize |
long OriginalSize |
295 |
public readonly int NDim |
int NDim |
359 (KEEP int - max 32 dims) |
public readonly int Size |
long Size |
380 |
public readonly int Offset |
long Offset |
391 |
public readonly int BufferSize |
long BufferSize |
402 |
public readonly int this[int dim] |
long this[int dim] |
565 |
public readonly int TransformOffset(int offset) |
long TransformOffset(long offset) |
581 |
public readonly int GetOffset(params int[] indices) |
long GetOffset(params long[] indices) |
598 |
public readonly int[] GetCoordinates(int offset) |
long[] GetCoordinates(long offset) |
755 |
Related files:
View/Shape.Unmanaged.cs - unsafe pointer versions of GetOffset, GetSubshape
View/Shape.Reshaping.cs - reshape operations
View/Slice.cs - Start, Stop, Step should stay int (Python slice semantics)
View/SliceDef.cs - may need long for large dimension slicing
1.2 IArraySlice Interface (Backends/Unmanaged/Interfaces/IArraySlice.cs)
| Current |
Change To |
T GetIndex<T>(int index) |
T GetIndex<T>(long index) |
object GetIndex(int index) |
object GetIndex(long index) |
void SetIndex<T>(int index, T value) |
void SetIndex<T>(long index, T value) |
void SetIndex(int index, object value) |
void SetIndex(long index, object value) |
object this[int index] |
object this[long index] |
IArraySlice Slice(int start) |
IArraySlice Slice(long start) |
IArraySlice Slice(int start, int count) |
IArraySlice Slice(long start, long count) |
1.3 ArraySlice Implementation (Backends/Unmanaged/ArraySlice.cs, ArraySlice1.cs`)
All index/count parameters and Count property → long
1.4 IMemoryBlock Interface (Backends/Unmanaged/Interfaces/IMemoryBlock.cs)
| Current |
Change To |
int Count |
long Count |
1.5 UnmanagedStorage (Backends/Unmanaged/UnmanagedStorage.cs)
| Current |
Change To |
Line |
public int Count |
public long Count |
47 |
Related files (same changes):
UnmanagedStorage.Getters.cs - index parameters
UnmanagedStorage.Setters.cs - index parameters
UnmanagedStorage.Slicing.cs - slice parameters
UnmanagedStorage.Cloning.cs - count parameters
Phase 2: NDArray Public API
2.1 NDArray Core (Backends/NDArray.cs)
| Property/Method |
Change |
int size |
long size |
int[] shape |
Keep int[] for API compat OR migrate to long[] |
int ndim |
Keep int (max 32 dimensions) |
int[] strides |
long[] strides |
2.2 NDArray Indexing (Selection/NDArray.Indexing.cs)
| Current |
Change To |
NDArray this[int* dims, int ndims] |
NDArray this[long* dims, int ndims] |
| All coordinate arrays |
int[] → long[] |
Related files:
NDArray.Indexing.Selection.cs
NDArray.Indexing.Selection.Getter.cs
NDArray.Indexing.Selection.Setter.cs
NDArray.Indexing.Masking.cs
2.3 Generic NDArray (Generics/NDArray1.cs`)
| Current |
Change To |
NDArray(int size, bool fillZeros) |
NDArray(long size, bool fillZeros) |
NDArray(int size) |
NDArray(long size) |
Phase 3: Iterators
3.1 NDIterator (Backends/Iterators/NDIterator.cs)
| Current |
Change To |
Func<int[], int> getOffset |
Func<long[], long> getOffset |
| Internal index tracking |
int → long |
Files (12 type-specific generated files):
NDIterator.template.cs
NDIteratorCasts/NDIterator.Cast.*.cs (Boolean, Byte, Char, Decimal, Double, Int16, Int32, Int64, Single, UInt16, UInt32, UInt64)
3.2 MultiIterator (Backends/Iterators/MultiIterator.cs)
Same changes as NDIterator.
3.3 Incrementors (Utilities/Incrementors/)
| File |
Changes |
NDCoordinatesIncrementor.cs |
coords int[] → long[] |
NDCoordinatesAxisIncrementor.cs |
coords int[] → long[] |
NDCoordinatesLeftToAxisIncrementor.cs |
coords int[] → long[] |
NDExtendedCoordinatesIncrementor.cs |
coords int[] → long[] |
NDOffsetIncrementor.cs |
offset int → long |
ValueOffsetIncrementor.cs |
offset int → long |
Phase 4: IL Kernel Generator (924 occurrences)
4.1 IL Emission Changes
Pattern: Replace Ldc_I4 with Ldc_I8, Conv_I4 with Conv_I8
| Current IL |
Change To |
il.Emit(OpCodes.Ldc_I4, value) |
il.Emit(OpCodes.Ldc_I8, (long)value) |
il.Emit(OpCodes.Ldc_I4_0) |
il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I8) or use Ldc_I8 |
il.Emit(OpCodes.Conv_I4) |
il.Emit(OpCodes.Conv_I8) |
| Loop counters (Ldloc/Stloc for int) |
Use int64 locals |
4.2 Files with IL Changes
| File |
Occurrences |
Focus Areas |
ILKernelGenerator.MixedType.cs |
170 |
Loop indices, stride calculations |
ILKernelGenerator.Reduction.cs |
151 |
Index tracking, accumulator positions |
ILKernelGenerator.MatMul.cs |
130 |
Matrix indices, row/col offsets |
ILKernelGenerator.Comparison.cs |
125 |
Loop counters |
ILKernelGenerator.Unary.cs |
78 |
Loop counters |
ILKernelGenerator.Shift.cs |
73 |
Loop counters |
ILKernelGenerator.Binary.cs |
53 |
Loop counters |
ILKernelGenerator.Scan.cs |
52 |
Cumulative indices |
ILKernelGenerator.Unary.Math.cs |
41 |
Loop counters |
ILKernelGenerator.cs |
35 |
Core emit helpers |
| Other partials |
~16 |
Various |
4.3 DynamicMethod Signatures
Current pattern:
new DynamicMethod("Kernel", typeof(void),
new[] { typeof(byte*), typeof(byte*), typeof(byte*), typeof(int) });
// ^^^^ count
Change to:
new DynamicMethod("Kernel", typeof(void),
new[] { typeof(byte*), typeof(byte*), typeof(byte*), typeof(long) });
// ^^^^ count
4.4 Delegate Types
| Current |
Change To |
delegate void ContiguousKernel<T>(T* a, T* b, T* result, int count) |
long count |
delegate void MixedTypeKernel(...) |
All index/count params → long |
delegate void UnaryKernel(...) |
All index/count params → long |
delegate void ComparisonKernel(...) |
All index/count params → long |
delegate void TypedElementReductionKernel<T>(...) |
All index/count params → long |
Phase 5: DefaultEngine Operations
5.1 Math Operations (Backends/Default/Math/)
| File |
Changes |
Default.Clip.cs |
Loop indices |
Default.ClipNDArray.cs |
Loop indices |
Default.Modf.cs |
Loop indices |
Default.Round.cs |
Loop indices |
Default.Shift.cs |
Loop indices |
5.2 Reduction Operations (Backends/Default/Math/Reduction/)
| File |
Changes |
Default.Reduction.Add.cs |
Index tracking |
Default.Reduction.Product.cs |
Index tracking |
Default.Reduction.AMax.cs |
Index tracking |
Default.Reduction.AMin.cs |
Index tracking |
Default.Reduction.ArgMax.cs |
Index tracking, return type stays int for NumPy compat |
Default.Reduction.ArgMin.cs |
Index tracking, return type stays int for NumPy compat |
Default.Reduction.Mean.cs |
Index tracking |
Default.Reduction.Var.cs |
Index tracking |
Default.Reduction.Std.cs |
Index tracking |
5.3 BLAS Operations (Backends/Default/Math/BLAS/)
| File |
Changes |
Default.Dot.NDMD.cs |
Matrix indices, blocked iteration |
Default.MatMul.2D2D.cs |
Matrix indices |
Default.MatMul.cs |
Matrix indices |
5.4 Array Manipulation (Backends/Default/ArrayManipulation/)
| File |
Changes |
Default.Transpose.cs |
Stride/index calculations |
Default.Broadcasting.cs |
Shape/stride calculations |
Phase 6: API Functions
6.1 Creation (Creation/)
| File |
Changes |
np.arange.cs |
count parameter |
np.linspace.cs |
num parameter |
np.zeros.cs |
shape parameters |
np.ones.cs |
shape parameters |
np.empty.cs |
shape parameters |
np.full.cs |
shape parameters |
np.eye.cs |
N, M parameters |
6.2 Manipulation (Manipulation/)
| File |
Changes |
np.repeat.cs |
repeats parameter |
np.roll.cs |
shift parameter |
NDArray.unique.cs |
index tracking |
6.3 Selection (Selection/)
All indexing operations need long indices.
6.4 Statistics (Statistics/)
| File |
Changes |
np.nanmean.cs |
count tracking |
np.nanstd.cs |
count tracking |
np.nanvar.cs |
count tracking |
Phase 7: Utilities
7.1 Array Utilities (Utilities/)
| File |
Changes |
Arrays.cs |
Index parameters |
ArrayConvert.cs |
158 loop occurrences |
Hashset1.cs` |
Index parameters |
7.2 Casting (Casting/)
| File |
Changes |
NdArrayToJaggedArray.cs |
24 loop occurrences |
UnmanagedMemoryBlock.Casting.cs |
291 loop occurrences |
Migration Strategy
Option A: Big Bang (Recommended)
- Create feature branch
int64-indexing
- Change Phase 1 (core types) atomically
- Fix all compilation errors (cascading changes)
- Run full test suite
- Performance benchmark comparison
Pros: Clean, no hybrid state
Cons: Large PR, harder to review
Option B: Incremental with Overloads
- Add
long overloads alongside int versions
- Deprecate
int versions
- Migrate callers incrementally
- Remove
int versions
Pros: Easier to review, can ship incrementally
Cons: Code bloat during transition, easy to miss conversions
Option C: Type Alias
// In a central location
global using npy_intp = System.Int64;
Then search/replace int → npy_intp for index-related uses.
Pros: Easy to toggle for testing, self-documenting
Cons: Requires careful identification of which int to replace
Files Summary by Impact
High Impact (Core Types)
View/Shape.cs - 20+ changes
View/Shape.Unmanaged.cs - 10+ changes
Backends/Unmanaged/Interfaces/IArraySlice.cs - 8 changes
Backends/Unmanaged/ArraySlice1.cs` - 15+ changes
Backends/Unmanaged/UnmanagedStorage.cs - 5+ changes
Medium Impact (IL Generation)
Backends/Kernels/ILKernelGenerator.*.cs - 924 IL emission changes across 13 files
Medium Impact (Iterators)
Backends/Iterators/*.cs - 28 files (including generated casts)
Lower Impact (API Functions)
Creation/*.cs - parameter changes
Manipulation/*.cs - parameter changes
Selection/*.cs - index changes
Math/*.cs - loop indices
Generated Code (Regen)
Utilities/ArrayConvert.cs - 158 changes
Backends/Unmanaged/UnmanagedMemoryBlock.Casting.cs - 291 changes
- NDIterator cast files - template-based
Testing Strategy
- Unit Tests: Run existing 2700+ tests - all should pass
- Edge Cases: Add tests for arrays at int32 boundary (2.1B+ elements)
- Performance: Benchmark suite comparing int32 vs int64 versions
- Memory: Verify no memory leaks from changed allocation patterns
Breaking Changes
| Change |
Impact |
Migration |
Shape.Size returns long |
Low |
Cast to int if needed |
NDArray.size returns long |
Low |
Cast to int if needed |
int[] shape → long[] shape |
Medium |
Update dependent code |
| Iterator coordinate types |
Low |
Internal change |
Most user code uses small arrays where int suffices. The main impact is internal code that stores/passes indices.
Estimated Effort
| Phase |
Files |
Estimated Hours |
| Phase 1: Core Types |
10 |
8 |
| Phase 2: NDArray API |
8 |
4 |
| Phase 3: Iterators |
30 |
6 |
| Phase 4: IL Kernels |
13 |
16 |
| Phase 5: DefaultEngine |
20 |
8 |
| Phase 6: API Functions |
30 |
6 |
| Phase 7: Utilities |
10 |
4 |
| Testing & Fixes |
- |
16 |
| Total |
~120 |
~68 hours |
References
- NumPy
npy_intp definition: numpy/_core/include/numpy/npy_common.h:217
- NumPy uses
Py_ssize_t which is 64-bit on x64 platforms
- .NET
nint/nuint are platform-dependent (like NumPy's approach)
- Benchmark proof: 1-3% overhead acceptable for >2GB array support
Implementation Plan
This plan provides 100% coverage of all changes required for int32 → int64 migration.
Phase 0: Preparation
0.1 Create Branch and Conversion Helpers
git checkout -b int64-indexing
Create src/NumSharp.Core/Utilities/IndexConvert.cs:
using System.Runtime.CompilerServices;
namespace NumSharp.Utilities;
internal static class IndexConvert
{
/// <summary>Convert int[] to long[] (always safe).</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long[] ToLong(int[] arr)
{
var result = new long[arr.Length];
for (int i = 0; i < arr.Length; i++)
result[i] = arr[i];
return result;
}
/// <summary>Convert long[] to int[], throws on overflow.</summary>
public static int[] ToIntChecked(long[] arr)
{
var result = new int[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] > int.MaxValue || arr[i] < int.MinValue)
throw new OverflowException($"Index {i} value {arr[i]} exceeds int range");
result[i] = (int)arr[i];
}
return result;
}
/// <summary>Throws if value exceeds int range.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToIntChecked(long value)
{
if (value > int.MaxValue || value < int.MinValue)
throw new OverflowException($"Value {value} exceeds int range");
return (int)value;
}
}
Phase 1: Core Types (Critical Path)
All other phases depend on this. Must be done atomically.
1.1 Shape Struct (View/Shape.cs)
| Line |
Current |
Change To |
| 207 |
internal readonly int _hashCode |
Keep int (hash codes are int) |
| 208 |
internal readonly int size |
internal readonly long size |
| 209 |
internal readonly int[] dimensions |
internal readonly long[] dimensions |
| 210 |
internal readonly int[] strides |
internal readonly long[] strides |
| 218 |
internal readonly int bufferSize |
internal readonly long bufferSize |
| 225 |
internal readonly int offset |
internal readonly long offset |
Properties to change:
| Property |
Change |
OriginalSize (295) |
Return long |
NDim (359) |
Keep int - max 32 dimensions |
Size (380) |
Return long |
Offset (391) |
Return long |
BufferSize (402) |
Return long |
this[int dim] (565) |
Return long (parameter stays int) |
Methods to change:
| Method |
Change |
TransformOffset(int) → TransformOffset(long) |
Parameter and return type |
GetOffset(params int[]) → GetOffset(params long[]) |
Parameter and return type |
GetCoordinates(int) → GetCoordinates(long) |
Parameter and return long[] |
ComputeSizeAndHash(int[]) → ComputeSizeAndHash(long[]) |
Internal helper |
ComputeIsBroadcastedStatic(int[], int[]) |
Change to long[] |
ComputeIsContiguousStatic(int[], int[]) |
Change to long[] |
ComputeFlagsStatic(int[], int[]) |
Change to long[] |
Constructors:
| Constructor |
Change |
Shape(int[] dims, int[] strides, int offset, int bufferSize) |
All long |
Shape(int[] dims, int[] strides) |
long[] parameters |
Shape(int[] dims, int[] strides, Shape originalShape) |
long[] parameters |
Shape(params int[] dims) |
params long[] dims |
1.2 Shape.Unmanaged.cs (View/Shape.Unmanaged.cs)
| Method |
Change |
GetOffset(int* indices, int ndims) |
GetOffset(long* indices, int ndims) returning long |
GetSubshape(int* dims, int ndims) |
GetSubshape(long* dims, int ndims) returning (Shape, long) |
InferNegativeCoordinates(int[], int*, int) |
Change to long[], long* |
1.3 Shape.Reshaping.cs (View/Shape.Reshaping.cs)
All reshape methods: change dimension parameters to long[].
1.4 IArraySlice Interface (Backends/Unmanaged/Interfaces/IArraySlice.cs)
| Current |
Change To |
T GetIndex<T>(int index) |
T GetIndex<T>(long index) |
object GetIndex(int index) |
object GetIndex(long index) |
void SetIndex<T>(int index, T value) |
void SetIndex<T>(long index, T value) |
void SetIndex(int index, object value) |
void SetIndex(long index, object value) |
object this[int index] |
object this[long index] |
IArraySlice Slice(int start) |
IArraySlice Slice(long start) |
IArraySlice Slice(int start, int count) |
IArraySlice Slice(long start, long count) |
1.5 IMemoryBlock Interface (Backends/Unmanaged/Interfaces/IMemoryBlock.cs)
| Current |
Change To |
int Count { get; } |
long Count { get; } |
1.6 ArraySlice Implementation (Backends/Unmanaged/ArraySlice.cs, ArraySlice1.cs`)
- All
int index parameters → long
Count property → long
- Internal
_count field → long
- All
Slice() methods → long parameters
1.7 UnmanagedStorage (Backends/Unmanaged/UnmanagedStorage.cs)
| Line |
Current |
Change To |
| 47 |
public int Count |
public long Count |
1.8 UnmanagedStorage.Getters.cs
| Method |
Change |
GetValue(params int[] indices) |
GetValue(params long[] indices) |
GetAtIndex(int index) |
GetAtIndex(long index) |
GetAtIndex<T>(int index) |
GetAtIndex<T>(long index) |
GetData(int[] indices) |
GetData(long[] indices) |
GetData(int* indices, int length) |
GetData(long* indices, int length) |
GetValue<T>(params int[] indices) |
GetValue<T>(params long[] indices) |
1.9 UnmanagedStorage.Setters.cs
| Method |
Change |
SetValue(ValueType value, params int[] indices) |
params long[] indices |
SetValue<T>(T value, params int[] indices) |
params long[] indices |
SetAtIndex(int index, object value) |
long index |
SetAtIndex<T>(int index, T value) |
long index |
1.10 UnmanagedStorage.Slicing.cs
All slice offset/count parameters → long.
1.11 UnmanagedStorage.Cloning.cs
Count parameters → long.
1.12 UnmanagedMemoryBlock (Backends/Unmanaged/UnmanagedMemoryBlock*.cs)
Count property → long
- All index parameters →
long
- Allocation size calculations →
long
Phase 2: NDArray Public API
2.1 NDArray Core (Backends/NDArray.cs)
| Property/Field |
Change |
public int[] shape |
public long[] shape |
public int size |
public long size |
public int[] strides |
public long[] strides |
public int ndim |
Keep int |
public int len |
public long len |
2.2 NDArray.Indexing.cs (Selection/NDArray.Indexing.cs)
| Current |
Change To |
this[int* dims, int ndims] |
this[long* dims, int ndims] |
this[params int[] indices] (in generic) |
this[params long[] indices] |
2.3 NDArray.Indexing.Selection.cs
All index arrays int[] → long[].
2.4 NDArray.Indexing.Selection.Getter.cs
All coordinate/index handling → long.
2.5 NDArray.Indexing.Selection.Setter.cs
All coordinate/index handling → long.
2.6 NDArray.Indexing.Masking.cs
Index tracking → long.
2.7 Generic NDArray (Generics/NDArray1.cs`)
| Current |
Change To |
NDArray(int size, bool fillZeros) |
NDArray(long size, bool fillZeros) |
NDArray(int size) |
NDArray(long size) |
this[params int[] indices] |
this[params long[] indices] |
GetAtIndex(int index) |
GetAtIndex(long index) |
2.8 NDArray.String.cs (Backends/NDArray.String.cs)
Index iteration → long.
Phase 3: Iterators
3.1 NDIterator.cs (Backends/Iterators/NDIterator.cs)
| Change |
Func<int[], int> getOffset → Func<long[], long> getOffset |
Internal index tracking int → long |
Coordinate arrays int[] → long[] |
3.2 NDIterator.template.cs
Same changes as NDIterator.cs - this is the template for code generation.
3.3 NDIterator Cast Files (12 files)
All files in Backends/Iterators/NDIteratorCasts/:
NDIterator.Cast.Boolean.cs
NDIterator.Cast.Byte.cs
NDIterator.Cast.Char.cs
NDIterator.Cast.Decimal.cs
NDIterator.Cast.Double.cs
NDIterator.Cast.Int16.cs
NDIterator.Cast.Int32.cs
NDIterator.Cast.Int64.cs
NDIterator.Cast.Single.cs
NDIterator.Cast.UInt16.cs
NDIterator.Cast.UInt32.cs
NDIterator.Cast.UInt64.cs
All need: Func<int[], int> → Func<long[], long>.
3.4 MultiIterator.cs (Backends/Iterators/MultiIterator.cs)
Same index/coordinate changes.
3.5 Incrementors (Utilities/Incrementors/)
| File |
Changes |
NDCoordinatesIncrementor.cs |
coords int[] → long[] |
NDCoordinatesAxisIncrementor.cs |
coords int[] → long[] |
NDCoordinatesLeftToAxisIncrementor.cs |
coords int[] → long[] |
NDExtendedCoordinatesIncrementor.cs |
coords int[] → long[] |
NDOffsetIncrementor.cs |
offset int → long |
ValueOffsetIncrementor.cs |
offset int → long |
Phase 4: IL Kernel Generator
924 IL emission changes across 13 files.
4.1 Common Patterns to Change
| IL Pattern |
Current |
New |
| Load int constant |
il.Emit(OpCodes.Ldc_I4, value) |
il.Emit(OpCodes.Ldc_I8, (long)value) |
| Load 0 |
il.Emit(OpCodes.Ldc_I4_0) |
il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I8) |
| Load 1 |
il.Emit(OpCodes.Ldc_I4_1) |
il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Conv_I8) |
| Convert to int |
il.Emit(OpCodes.Conv_I4) |
il.Emit(OpCodes.Conv_I8) |
| Local variable |
il.DeclareLocal(typeof(int)) |
il.DeclareLocal(typeof(long)) |
| Increment |
il.Emit(OpCodes.Add) |
Same (works for long) |
| Compare |
il.Emit(OpCodes.Clt) |
Same (works for long) |
4.2 Delegate Type Changes
| File |
Delegate |
Change |
ILKernelGenerator.Binary.cs |
ContiguousKernel<T> |
int count → long count |
ILKernelGenerator.MixedType.cs |
MixedTypeKernel |
All index/count → long |
ILKernelGenerator.Unary.cs |
UnaryKernel |
All index/count → long |
ILKernelGenerator.Comparison.cs |
ComparisonKernel |
All index/count → long |
ILKernelGenerator.Reduction.cs |
TypedElementReductionKernel<T> |
All index/count → long |
ILKernelGenerator.Scan.cs |
Scan delegates |
All index/count → long |
ILKernelGenerator.Shift.cs |
Shift delegates |
All index/count → long |
ILKernelGenerator.MatMul.cs |
MatMul delegates |
All index/count → long |
4.3 DynamicMethod Signature Changes
All DynamicMethod parameter types:
// Before
new DynamicMethod("Kernel", typeof(void),
new[] { typeof(byte*), typeof(byte*), typeof(byte*), typeof(int) });
// After
new DynamicMethod("Kernel", typeof(void),
new[] { typeof(byte*), typeof(byte*), typeof(byte*), typeof(long) });
4.4 Files and Estimated Changes
| File |
Occurrences |
Focus |
ILKernelGenerator.cs |
35 |
Core helpers, type utilities |
ILKernelGenerator.Binary.cs |
53 |
Loop counters, contiguous kernels |
ILKernelGenerator.MixedType.cs |
170 |
Loop indices, stride calculations |
ILKernelGenerator.Unary.cs |
78 |
Loop counters |
ILKernelGenerator.Unary.Math.cs |
41 |
Loop counters |
ILKernelGenerator.Unary.Decimal.cs |
7 |
Loop counters |
ILKernelGenerator.Unary.Predicate.cs |
3 |
Loop counters |
ILKernelGenerator.Unary.Vector.cs |
6 |
Vector indices |
ILKernelGenerator.Comparison.cs |
125 |
Loop counters, mask extraction |
ILKernelGenerator.Reduction.cs |
151 |
Index tracking, accumulator positions |
ILKernelGenerator.Scan.cs |
52 |
Cumulative indices |
ILKernelGenerator.Shift.cs |
73 |
Loop counters |
ILKernelGenerator.MatMul.cs |
130 |
Matrix indices, row/col offsets |
4.5 Reduction Axis Files
| File |
Changes |
ILKernelGenerator.Reduction.Axis.cs |
Index iteration |
ILKernelGenerator.Reduction.Axis.Arg.cs |
ArgMax/ArgMin index tracking |
ILKernelGenerator.Reduction.Axis.NaN.cs |
NaN reduction indices |
ILKernelGenerator.Reduction.Axis.Simd.cs |
SIMD loop indices |
ILKernelGenerator.Reduction.Axis.VarStd.cs |
Variance/std indices |
ILKernelGenerator.Reduction.Boolean.cs |
All/Any indices |
4.6 Other IL Files
| File |
Changes |
ILKernelGenerator.Clip.cs |
Loop indices |
ILKernelGenerator.Masking.cs |
Mask indices |
ILKernelGenerator.Masking.Boolean.cs |
Boolean mask indices |
ILKernelGenerator.Masking.NaN.cs |
NaN mask indices |
ILKernelGenerator.Masking.VarStd.cs |
Variance mask indices |
Phase 5: DefaultEngine Operations
5.1 Math Operations (Backends/Default/Math/)
| File |
Changes |
Default.Clip.cs |
Loop indices |
Default.ClipNDArray.cs |
Loop indices |
Default.Modf.cs |
Loop indices |
Default.Round.cs |
Loop indices |
Default.Shift.cs |
Loop indices |
5.2 Reduction Operations (Backends/Default/Math/Reduction/)
| File |
Changes |
Default.Reduction.Add.cs |
Index tracking |
Default.Reduction.Product.cs |
Index tracking |
Default.Reduction.AMax.cs |
Index tracking |
Default.Reduction.AMin.cs |
Index tracking |
Default.Reduction.ArgMax.cs |
Return type int → long |
Default.Reduction.ArgMin.cs |
Return type int → long |
Default.Reduction.Mean.cs |
Index tracking |
Default.Reduction.Var.cs |
Index tracking |
Default.Reduction.Std.cs |
Index tracking |
5.3 BLAS Operations (Backends/Default/Math/BLAS/)
| File |
Changes |
Default.Dot.NDMD.cs |
Matrix indices, blocked iteration |
Default.MatMul.2D2D.cs |
Matrix indices (17 loop occurrences) |
Default.MatMul.cs |
Matrix indices |
5.4 Array Manipulation (Backends/Default/ArrayManipulation/)
| File |
Changes |
Default.Transpose.cs |
Stride/index calculations |
Default.Broadcasting.cs |
Shape/stride calculations |
5.5 Indexing (Backends/Default/Indexing/)
| File |
Changes |
Default.NonZero.cs |
Index collection returns long[][] |
Phase 6: API Functions
6.1 Creation (Creation/)
| File |
Parameter Changes |
np.arange.cs |
start, stop, step calculation → long aware |
np.linspace.cs |
num parameter, index iteration |
np.zeros.cs |
shape parameters int[] → long[] |
np.ones.cs |
shape parameters int[] → long[] |
np.empty.cs |
shape parameters int[] → long[] |
np.full.cs |
shape parameters int[] → long[] |
np.eye.cs |
N, M parameters (can stay int, implicitly convert) |
np.zeros_like.cs |
Shape handling |
np.ones_like.cs |
Shape handling |
np.empty_like.cs |
Shape handling |
np.full_like.cs |
Shape handling |
np.array.cs |
Shape calculation |
np.asarray.cs |
Shape calculation |
np.copy.cs |
Size handling |
np.stack.cs |
Index iteration |
np.hstack.cs |
Index iteration |
np.vstack.cs |
Index iteration |
np.dstack.cs |
Index iteration |
np.concatenate.cs |
Index iteration |
np.broadcast.cs |
Shape handling |
np.broadcast_to.cs |
Shape handling |
np.broadcast_arrays.cs |
Shape handling |
6.2 Manipulation (Manipulation/)
| File |
Changes |
np.reshape.cs |
Shape parameter int[] → long[] |
np.repeat.cs |
repeats parameter, index iteration |
np.roll.cs |
shift parameter, index iteration |
NDArray.unique.cs |
Index tracking |
np.squeeze.cs |
Shape handling |
np.expand_dims.cs |
Shape handling |
np.swapaxes.cs |
Stride handling |
np.moveaxis.cs |
Stride handling |
np.rollaxis.cs |
Stride handling |
np.transpose.cs |
Stride handling |
np.ravel.cs |
Size handling |
NDArray.flatten.cs |
Size handling |
6.3 Math (Math/)
| File |
Changes |
np.sum.cs |
Size handling |
NDArray.prod.cs |
Size handling |
np.cumsum.cs |
Index iteration |
NDArray.negative.cs |
Index iteration |
NdArray.Convolve.cs |
Index iteration |
np.nansum.cs |
Index iteration |
np.nanprod.cs |
Index iteration |
6.4 Statistics (Statistics/)
| File |
Changes |
np.mean.cs |
Count handling |
np.std.cs |
Count handling |
np.var.cs |
Count handling |
np.nanmean.cs |
Count tracking |
np.nanstd.cs |
Count tracking |
np.nanvar.cs |
Count tracking |
6.5 Sorting/Searching (Sorting_Searching_Counting/)
| File |
Changes |
np.argmax.cs |
Return long |
np.argmin.cs |
Return long |
np.argsort.cs |
Index array handling |
np.searchsorted.cs |
Index return type |
np.nanmax.cs |
Index iteration |
np.nanmin.cs |
Index iteration |
6.6 Logic (Logic/)
| File |
Changes |
np.all.cs |
Index iteration |
np.any.cs |
Index iteration |
np.nonzero.cs |
Return long[][] |
6.7 Linear Algebra (LinearAlgebra/)
| File |
Changes |
np.dot.cs |
Index handling |
NDArray.dot.cs |
Index handling |
np.matmul.cs |
Index handling |
np.outer.cs |
Index handling |
NDArray.matrix_power.cs |
Index iteration |
6.8 Random (RandomSampling/)
| File |
Changes |
np.random.rand.cs |
Shape parameters |
np.random.randn.cs |
Shape parameters |
np.random.randint.cs |
Size parameters, index iteration |
np.random.uniform.cs |
Shape parameters |
np.random.choice.cs |
Size parameters |
| All distribution files |
Shape parameters |
6.9 I/O (APIs/)
| File |
Changes |
np.save.cs |
Index iteration in Enumerate |
np.load.cs |
Shape handling |
np.fromfile.cs |
Count handling |
np.tofile.cs |
Count handling |
Phase 7: Utilities and Casting
7.1 Utilities (Utilities/)
| File |
Changes |
Arrays.cs |
Index parameters |
ArrayConvert.cs |
158 loop occurrences |
Hashset1.cs |
Index parameters |
py.cs |
Index handling |
SteppingExtension.cs |
Index handling |
7.2 Casting (Casting/)
| File |
Changes |
NdArrayToJaggedArray.cs |
24 loop occurrences |
UnmanagedMemoryBlock.Casting.cs |
291 loop occurrences |
NdArray.ToString.cs |
Index iteration |
Implicit/NdArray.Implicit.Array.cs |
Index handling |
Phase 8: SIMD Helpers
8.1 SimdKernels.cs (Backends/Kernels/SimdKernels.cs)
Loop counters and index handling → long.
8.2 SimdMatMul.cs (Backends/Kernels/SimdMatMul.cs)
20 loop occurrences - matrix indices → long.
8.3 SimdReductionOptimized.cs (Backends/Kernels/SimdReductionOptimized.cs)
Loop counters → long.
Phase 9: Testing
9.1 Update Existing Tests
- All tests using
int[] for shape assertions → long[]
- All tests storing
argmax/argmin results → long
- All tests checking
size → long
9.2 New Tests: Large Array Boundary
Create test/NumSharp.UnitTest/LargeArrayTests.cs:
[Test]
public void Shape_SupportsLargeSize()
{
// Test shape with size > int.MaxValue
var shape = new Shape(50000, 50000); // 2.5B elements
Assert.That(shape.Size, Is.EqualTo(2_500_000_000L));
}
[Test]
public void ArgMax_ReturnsLongIndex()
{
var arr = np.arange(100);
long idx = arr.argmax(); // Should compile and work
Assert.That(idx, Is.EqualTo(99L));
}
9.3 Performance Regression Tests
Compare benchmarks before/after:
- np.add (scalar and SIMD paths)
- np.cumsum
- np.matmul
- Verify < 5% regression
Execution Order
Phase 0: Preparation (create branch, helpers)
↓
Phase 1: Core Types (Shape, IArraySlice, UnmanagedStorage)
↓ (compilation will fail until complete)
Phase 2: NDArray Public API
↓
Phase 3: Iterators
↓
Phase 4: IL Kernel Generator (largest effort)
↓
Phase 5: DefaultEngine Operations
↓
Phase 6: API Functions
↓
Phase 7: Utilities and Casting
↓
Phase 8: SIMD Helpers
↓
Phase 9: Testing
↓
Final: Build, test, benchmark, merge
Verification Checklist
Int64 Index Migration Plan
Migration from
int(int32) tolong(int64) for all index, stride, offset, and size operations.Rationale: Support arrays >2GB (int32 max = 2.1B elements). NumPy uses
npy_intp=Py_ssize_t(64-bit on x64).Performance Impact: Benchmarked at 1-3% overhead for scalar loops, <1% for SIMD loops. Acceptable.
Conversion Strategy: int ↔ long Handling
C# Conversion Rules
int→longlong→intint[]→long[]long[]→int[]Why Pointer Arithmetic Works
NumSharp uses unmanaged memory (
byte*), not managed arrays. Pointer arithmetic natively supportslongoffsets:This is why we can migrate internally to
longwithout breaking memory access.Public API Strategy: Dual Overloads
Keep
intoverloads for backward compatibility, delegate tolonginternally:Shape Constructor Overloads
Backward Compatible Properties
What Stays int (No Change Needed)
NDim/ndimSlice.Start/Stop/StepNPTypeCodeenum valuesConversion Helper Methods
IL Kernel Considerations
For IL-generated kernels, loop counters can often stay
intwhen:Offset calculations must use
long:Phase 1: Core Types (CRITICAL PATH)
These changes cascade to everything else. Must be done atomically.
1.1 Shape Struct (
View/Shape.cs)internal readonly int sizelong sizeinternal readonly int[] dimensionslong[] dimensionsinternal readonly int[] strideslong[] stridesinternal readonly int bufferSizelong bufferSizeinternal readonly int offsetlong offsetpublic readonly int OriginalSizelong OriginalSizepublic readonly int NDimint NDimpublic readonly int Sizelong Sizepublic readonly int Offsetlong Offsetpublic readonly int BufferSizelong BufferSizepublic readonly int this[int dim]long this[int dim]public readonly int TransformOffset(int offset)long TransformOffset(long offset)public readonly int GetOffset(params int[] indices)long GetOffset(params long[] indices)public readonly int[] GetCoordinates(int offset)long[] GetCoordinates(long offset)Related files:
View/Shape.Unmanaged.cs- unsafe pointer versions of GetOffset, GetSubshapeView/Shape.Reshaping.cs- reshape operationsView/Slice.cs- Start, Stop, Step should stay int (Python slice semantics)View/SliceDef.cs- may need long for large dimension slicing1.2 IArraySlice Interface (
Backends/Unmanaged/Interfaces/IArraySlice.cs)T GetIndex<T>(int index)T GetIndex<T>(long index)object GetIndex(int index)object GetIndex(long index)void SetIndex<T>(int index, T value)void SetIndex<T>(long index, T value)void SetIndex(int index, object value)void SetIndex(long index, object value)object this[int index]object this[long index]IArraySlice Slice(int start)IArraySlice Slice(long start)IArraySlice Slice(int start, int count)IArraySlice Slice(long start, long count)1.3 ArraySlice Implementation (
Backends/Unmanaged/ArraySlice.cs,ArraySlice1.cs`)All index/count parameters and
Countproperty →long1.4 IMemoryBlock Interface (
Backends/Unmanaged/Interfaces/IMemoryBlock.cs)int Countlong Count1.5 UnmanagedStorage (
Backends/Unmanaged/UnmanagedStorage.cs)public int Countpublic long CountRelated files (same changes):
UnmanagedStorage.Getters.cs- index parametersUnmanagedStorage.Setters.cs- index parametersUnmanagedStorage.Slicing.cs- slice parametersUnmanagedStorage.Cloning.cs- count parametersPhase 2: NDArray Public API
2.1 NDArray Core (
Backends/NDArray.cs)int sizelong sizeint[] shapeint[]for API compat OR migrate tolong[]int ndimint(max 32 dimensions)int[] strideslong[] strides2.2 NDArray Indexing (
Selection/NDArray.Indexing.cs)NDArray this[int* dims, int ndims]NDArray this[long* dims, int ndims]int[]→long[]Related files:
NDArray.Indexing.Selection.csNDArray.Indexing.Selection.Getter.csNDArray.Indexing.Selection.Setter.csNDArray.Indexing.Masking.cs2.3 Generic NDArray (
Generics/NDArray1.cs`)NDArray(int size, bool fillZeros)NDArray(long size, bool fillZeros)NDArray(int size)NDArray(long size)Phase 3: Iterators
3.1 NDIterator (
Backends/Iterators/NDIterator.cs)Func<int[], int> getOffsetFunc<long[], long> getOffsetint→longFiles (12 type-specific generated files):
NDIterator.template.csNDIteratorCasts/NDIterator.Cast.*.cs(Boolean, Byte, Char, Decimal, Double, Int16, Int32, Int64, Single, UInt16, UInt32, UInt64)3.2 MultiIterator (
Backends/Iterators/MultiIterator.cs)Same changes as NDIterator.
3.3 Incrementors (
Utilities/Incrementors/)NDCoordinatesIncrementor.csint[]→long[]NDCoordinatesAxisIncrementor.csint[]→long[]NDCoordinatesLeftToAxisIncrementor.csint[]→long[]NDExtendedCoordinatesIncrementor.csint[]→long[]NDOffsetIncrementor.csint→longValueOffsetIncrementor.csint→longPhase 4: IL Kernel Generator (924 occurrences)
4.1 IL Emission Changes
Pattern: Replace
Ldc_I4withLdc_I8,Conv_I4withConv_I8il.Emit(OpCodes.Ldc_I4, value)il.Emit(OpCodes.Ldc_I8, (long)value)il.Emit(OpCodes.Ldc_I4_0)il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I8)or use Ldc_I8il.Emit(OpCodes.Conv_I4)il.Emit(OpCodes.Conv_I8)4.2 Files with IL Changes
ILKernelGenerator.MixedType.csILKernelGenerator.Reduction.csILKernelGenerator.MatMul.csILKernelGenerator.Comparison.csILKernelGenerator.Unary.csILKernelGenerator.Shift.csILKernelGenerator.Binary.csILKernelGenerator.Scan.csILKernelGenerator.Unary.Math.csILKernelGenerator.cs4.3 DynamicMethod Signatures
Current pattern:
Change to:
4.4 Delegate Types
delegate void ContiguousKernel<T>(T* a, T* b, T* result, int count)long countdelegate void MixedTypeKernel(...)longdelegate void UnaryKernel(...)longdelegate void ComparisonKernel(...)longdelegate void TypedElementReductionKernel<T>(...)longPhase 5: DefaultEngine Operations
5.1 Math Operations (
Backends/Default/Math/)Default.Clip.csDefault.ClipNDArray.csDefault.Modf.csDefault.Round.csDefault.Shift.cs5.2 Reduction Operations (
Backends/Default/Math/Reduction/)Default.Reduction.Add.csDefault.Reduction.Product.csDefault.Reduction.AMax.csDefault.Reduction.AMin.csDefault.Reduction.ArgMax.csintfor NumPy compatDefault.Reduction.ArgMin.csintfor NumPy compatDefault.Reduction.Mean.csDefault.Reduction.Var.csDefault.Reduction.Std.cs5.3 BLAS Operations (
Backends/Default/Math/BLAS/)Default.Dot.NDMD.csDefault.MatMul.2D2D.csDefault.MatMul.cs5.4 Array Manipulation (
Backends/Default/ArrayManipulation/)Default.Transpose.csDefault.Broadcasting.csPhase 6: API Functions
6.1 Creation (
Creation/)np.arange.csnp.linspace.csnp.zeros.csnp.ones.csnp.empty.csnp.full.csnp.eye.cs6.2 Manipulation (
Manipulation/)np.repeat.csnp.roll.csNDArray.unique.cs6.3 Selection (
Selection/)All indexing operations need long indices.
6.4 Statistics (
Statistics/)np.nanmean.csnp.nanstd.csnp.nanvar.csPhase 7: Utilities
7.1 Array Utilities (
Utilities/)Arrays.csArrayConvert.csHashset1.cs`7.2 Casting (
Casting/)NdArrayToJaggedArray.csUnmanagedMemoryBlock.Casting.csMigration Strategy
Option A: Big Bang (Recommended)
int64-indexingPros: Clean, no hybrid state
Cons: Large PR, harder to review
Option B: Incremental with Overloads
longoverloads alongsideintversionsintversionsintversionsPros: Easier to review, can ship incrementally
Cons: Code bloat during transition, easy to miss conversions
Option C: Type Alias
Then search/replace
int→npy_intpfor index-related uses.Pros: Easy to toggle for testing, self-documenting
Cons: Requires careful identification of which
intto replaceFiles Summary by Impact
High Impact (Core Types)
View/Shape.cs- 20+ changesView/Shape.Unmanaged.cs- 10+ changesBackends/Unmanaged/Interfaces/IArraySlice.cs- 8 changesBackends/Unmanaged/ArraySlice1.cs` - 15+ changesBackends/Unmanaged/UnmanagedStorage.cs- 5+ changesMedium Impact (IL Generation)
Backends/Kernels/ILKernelGenerator.*.cs- 924 IL emission changes across 13 filesMedium Impact (Iterators)
Backends/Iterators/*.cs- 28 files (including generated casts)Lower Impact (API Functions)
Creation/*.cs- parameter changesManipulation/*.cs- parameter changesSelection/*.cs- index changesMath/*.cs- loop indicesGenerated Code (Regen)
Utilities/ArrayConvert.cs- 158 changesBackends/Unmanaged/UnmanagedMemoryBlock.Casting.cs- 291 changesTesting Strategy
Breaking Changes
Shape.Sizereturnslongintif neededNDArray.sizereturnslongintif neededint[]shape →long[]shapeMost user code uses small arrays where
intsuffices. The main impact is internal code that stores/passes indices.Estimated Effort
References
npy_intpdefinition:numpy/_core/include/numpy/npy_common.h:217Py_ssize_twhich is 64-bit on x64 platformsnint/nuintare platform-dependent (like NumPy's approach)Implementation Plan
This plan provides 100% coverage of all changes required for int32 → int64 migration.
Phase 0: Preparation
0.1 Create Branch and Conversion Helpers
Create
src/NumSharp.Core/Utilities/IndexConvert.cs:Phase 1: Core Types (Critical Path)
All other phases depend on this. Must be done atomically.
1.1 Shape Struct (
View/Shape.cs)internal readonly int _hashCodeint(hash codes are int)internal readonly int sizeinternal readonly long sizeinternal readonly int[] dimensionsinternal readonly long[] dimensionsinternal readonly int[] stridesinternal readonly long[] stridesinternal readonly int bufferSizeinternal readonly long bufferSizeinternal readonly int offsetinternal readonly long offsetProperties to change:
OriginalSize(295)longNDim(359)int- max 32 dimensionsSize(380)longOffset(391)longBufferSize(402)longthis[int dim](565)long(parameter staysint)Methods to change:
TransformOffset(int)→TransformOffset(long)GetOffset(params int[])→GetOffset(params long[])GetCoordinates(int)→GetCoordinates(long)long[]ComputeSizeAndHash(int[])→ComputeSizeAndHash(long[])ComputeIsBroadcastedStatic(int[], int[])long[]ComputeIsContiguousStatic(int[], int[])long[]ComputeFlagsStatic(int[], int[])long[]Constructors:
Shape(int[] dims, int[] strides, int offset, int bufferSize)longShape(int[] dims, int[] strides)long[]parametersShape(int[] dims, int[] strides, Shape originalShape)long[]parametersShape(params int[] dims)params long[] dims1.2 Shape.Unmanaged.cs (
View/Shape.Unmanaged.cs)GetOffset(int* indices, int ndims)GetOffset(long* indices, int ndims)returninglongGetSubshape(int* dims, int ndims)GetSubshape(long* dims, int ndims)returning(Shape, long)InferNegativeCoordinates(int[], int*, int)long[],long*1.3 Shape.Reshaping.cs (
View/Shape.Reshaping.cs)All reshape methods: change dimension parameters to
long[].1.4 IArraySlice Interface (
Backends/Unmanaged/Interfaces/IArraySlice.cs)T GetIndex<T>(int index)T GetIndex<T>(long index)object GetIndex(int index)object GetIndex(long index)void SetIndex<T>(int index, T value)void SetIndex<T>(long index, T value)void SetIndex(int index, object value)void SetIndex(long index, object value)object this[int index]object this[long index]IArraySlice Slice(int start)IArraySlice Slice(long start)IArraySlice Slice(int start, int count)IArraySlice Slice(long start, long count)1.5 IMemoryBlock Interface (
Backends/Unmanaged/Interfaces/IMemoryBlock.cs)int Count { get; }long Count { get; }1.6 ArraySlice Implementation (
Backends/Unmanaged/ArraySlice.cs,ArraySlice1.cs`)intindex parameters →longCountproperty →long_countfield →longSlice()methods →longparameters1.7 UnmanagedStorage (
Backends/Unmanaged/UnmanagedStorage.cs)public int Countpublic long Count1.8 UnmanagedStorage.Getters.cs
GetValue(params int[] indices)GetValue(params long[] indices)GetAtIndex(int index)GetAtIndex(long index)GetAtIndex<T>(int index)GetAtIndex<T>(long index)GetData(int[] indices)GetData(long[] indices)GetData(int* indices, int length)GetData(long* indices, int length)GetValue<T>(params int[] indices)GetValue<T>(params long[] indices)1.9 UnmanagedStorage.Setters.cs
SetValue(ValueType value, params int[] indices)params long[] indicesSetValue<T>(T value, params int[] indices)params long[] indicesSetAtIndex(int index, object value)long indexSetAtIndex<T>(int index, T value)long index1.10 UnmanagedStorage.Slicing.cs
All slice offset/count parameters →
long.1.11 UnmanagedStorage.Cloning.cs
Count parameters →
long.1.12 UnmanagedMemoryBlock (
Backends/Unmanaged/UnmanagedMemoryBlock*.cs)Countproperty →longlonglongPhase 2: NDArray Public API
2.1 NDArray Core (
Backends/NDArray.cs)public int[] shapepublic long[] shapepublic int sizepublic long sizepublic int[] stridespublic long[] stridespublic int ndimintpublic int lenpublic long len2.2 NDArray.Indexing.cs (
Selection/NDArray.Indexing.cs)this[int* dims, int ndims]this[long* dims, int ndims]this[params int[] indices](in generic)this[params long[] indices]2.3 NDArray.Indexing.Selection.cs
All index arrays
int[]→long[].2.4 NDArray.Indexing.Selection.Getter.cs
All coordinate/index handling →
long.2.5 NDArray.Indexing.Selection.Setter.cs
All coordinate/index handling →
long.2.6 NDArray.Indexing.Masking.cs
Index tracking →
long.2.7 Generic NDArray (
Generics/NDArray1.cs`)NDArray(int size, bool fillZeros)NDArray(long size, bool fillZeros)NDArray(int size)NDArray(long size)this[params int[] indices]this[params long[] indices]GetAtIndex(int index)GetAtIndex(long index)2.8 NDArray.String.cs (
Backends/NDArray.String.cs)Index iteration →
long.Phase 3: Iterators
3.1 NDIterator.cs (
Backends/Iterators/NDIterator.cs)Func<int[], int> getOffset→Func<long[], long> getOffsetint→longint[]→long[]3.2 NDIterator.template.cs
Same changes as NDIterator.cs - this is the template for code generation.
3.3 NDIterator Cast Files (12 files)
All files in
Backends/Iterators/NDIteratorCasts/:NDIterator.Cast.Boolean.csNDIterator.Cast.Byte.csNDIterator.Cast.Char.csNDIterator.Cast.Decimal.csNDIterator.Cast.Double.csNDIterator.Cast.Int16.csNDIterator.Cast.Int32.csNDIterator.Cast.Int64.csNDIterator.Cast.Single.csNDIterator.Cast.UInt16.csNDIterator.Cast.UInt32.csNDIterator.Cast.UInt64.csAll need:
Func<int[], int>→Func<long[], long>.3.4 MultiIterator.cs (
Backends/Iterators/MultiIterator.cs)Same index/coordinate changes.
3.5 Incrementors (
Utilities/Incrementors/)NDCoordinatesIncrementor.csint[]→long[]NDCoordinatesAxisIncrementor.csint[]→long[]NDCoordinatesLeftToAxisIncrementor.csint[]→long[]NDExtendedCoordinatesIncrementor.csint[]→long[]NDOffsetIncrementor.csint→longValueOffsetIncrementor.csint→longPhase 4: IL Kernel Generator
924 IL emission changes across 13 files.
4.1 Common Patterns to Change
il.Emit(OpCodes.Ldc_I4, value)il.Emit(OpCodes.Ldc_I8, (long)value)il.Emit(OpCodes.Ldc_I4_0)il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I8)il.Emit(OpCodes.Ldc_I4_1)il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Conv_I8)il.Emit(OpCodes.Conv_I4)il.Emit(OpCodes.Conv_I8)il.DeclareLocal(typeof(int))il.DeclareLocal(typeof(long))il.Emit(OpCodes.Add)il.Emit(OpCodes.Clt)4.2 Delegate Type Changes
ILKernelGenerator.Binary.csContiguousKernel<T>int count→long countILKernelGenerator.MixedType.csMixedTypeKernellongILKernelGenerator.Unary.csUnaryKernellongILKernelGenerator.Comparison.csComparisonKernellongILKernelGenerator.Reduction.csTypedElementReductionKernel<T>longILKernelGenerator.Scan.cslongILKernelGenerator.Shift.cslongILKernelGenerator.MatMul.cslong4.3 DynamicMethod Signature Changes
All
DynamicMethodparameter types:4.4 Files and Estimated Changes
ILKernelGenerator.csILKernelGenerator.Binary.csILKernelGenerator.MixedType.csILKernelGenerator.Unary.csILKernelGenerator.Unary.Math.csILKernelGenerator.Unary.Decimal.csILKernelGenerator.Unary.Predicate.csILKernelGenerator.Unary.Vector.csILKernelGenerator.Comparison.csILKernelGenerator.Reduction.csILKernelGenerator.Scan.csILKernelGenerator.Shift.csILKernelGenerator.MatMul.cs4.5 Reduction Axis Files
ILKernelGenerator.Reduction.Axis.csILKernelGenerator.Reduction.Axis.Arg.csILKernelGenerator.Reduction.Axis.NaN.csILKernelGenerator.Reduction.Axis.Simd.csILKernelGenerator.Reduction.Axis.VarStd.csILKernelGenerator.Reduction.Boolean.cs4.6 Other IL Files
ILKernelGenerator.Clip.csILKernelGenerator.Masking.csILKernelGenerator.Masking.Boolean.csILKernelGenerator.Masking.NaN.csILKernelGenerator.Masking.VarStd.csPhase 5: DefaultEngine Operations
5.1 Math Operations (
Backends/Default/Math/)Default.Clip.csDefault.ClipNDArray.csDefault.Modf.csDefault.Round.csDefault.Shift.cs5.2 Reduction Operations (
Backends/Default/Math/Reduction/)Default.Reduction.Add.csDefault.Reduction.Product.csDefault.Reduction.AMax.csDefault.Reduction.AMin.csDefault.Reduction.ArgMax.csint→longDefault.Reduction.ArgMin.csint→longDefault.Reduction.Mean.csDefault.Reduction.Var.csDefault.Reduction.Std.cs5.3 BLAS Operations (
Backends/Default/Math/BLAS/)Default.Dot.NDMD.csDefault.MatMul.2D2D.csDefault.MatMul.cs5.4 Array Manipulation (
Backends/Default/ArrayManipulation/)Default.Transpose.csDefault.Broadcasting.cs5.5 Indexing (
Backends/Default/Indexing/)Default.NonZero.cslong[][]Phase 6: API Functions
6.1 Creation (
Creation/)np.arange.cslongawarenp.linspace.csnp.zeros.csint[]→long[]np.ones.csint[]→long[]np.empty.csint[]→long[]np.full.csint[]→long[]np.eye.csnp.zeros_like.csnp.ones_like.csnp.empty_like.csnp.full_like.csnp.array.csnp.asarray.csnp.copy.csnp.stack.csnp.hstack.csnp.vstack.csnp.dstack.csnp.concatenate.csnp.broadcast.csnp.broadcast_to.csnp.broadcast_arrays.cs6.2 Manipulation (
Manipulation/)np.reshape.csint[]→long[]np.repeat.csnp.roll.csNDArray.unique.csnp.squeeze.csnp.expand_dims.csnp.swapaxes.csnp.moveaxis.csnp.rollaxis.csnp.transpose.csnp.ravel.csNDArray.flatten.cs6.3 Math (
Math/)np.sum.csNDArray.prod.csnp.cumsum.csNDArray.negative.csNdArray.Convolve.csnp.nansum.csnp.nanprod.cs6.4 Statistics (
Statistics/)np.mean.csnp.std.csnp.var.csnp.nanmean.csnp.nanstd.csnp.nanvar.cs6.5 Sorting/Searching (
Sorting_Searching_Counting/)np.argmax.cslongnp.argmin.cslongnp.argsort.csnp.searchsorted.csnp.nanmax.csnp.nanmin.cs6.6 Logic (
Logic/)np.all.csnp.any.csnp.nonzero.cslong[][]6.7 Linear Algebra (
LinearAlgebra/)np.dot.csNDArray.dot.csnp.matmul.csnp.outer.csNDArray.matrix_power.cs6.8 Random (
RandomSampling/)np.random.rand.csnp.random.randn.csnp.random.randint.csnp.random.uniform.csnp.random.choice.cs6.9 I/O (
APIs/)np.save.csnp.load.csnp.fromfile.csnp.tofile.csPhase 7: Utilities and Casting
7.1 Utilities (
Utilities/)Arrays.csArrayConvert.csHashset1.cspy.csSteppingExtension.cs7.2 Casting (
Casting/)NdArrayToJaggedArray.csUnmanagedMemoryBlock.Casting.csNdArray.ToString.csImplicit/NdArray.Implicit.Array.csPhase 8: SIMD Helpers
8.1 SimdKernels.cs (
Backends/Kernels/SimdKernels.cs)Loop counters and index handling →
long.8.2 SimdMatMul.cs (
Backends/Kernels/SimdMatMul.cs)20 loop occurrences - matrix indices →
long.8.3 SimdReductionOptimized.cs (
Backends/Kernels/SimdReductionOptimized.cs)Loop counters →
long.Phase 9: Testing
9.1 Update Existing Tests
int[]for shape assertions →long[]argmax/argminresults →longsize→long9.2 New Tests: Large Array Boundary
Create
test/NumSharp.UnitTest/LargeArrayTests.cs:9.3 Performance Regression Tests
Compare benchmarks before/after:
Execution Order
Verification Checklist
dotnet buildsucceeds with no errorsintremains for index/size/offset/stride (except ndim, Slice)shapereturnslong[]sizereturnslongargmax/argminreturnlongnonzeroreturnslong[][]Ldc_I8andConv_I8