![](/img/trans.png)
[英]Telerik WPF virtual grid - Array dimensions exceeded supported range error (VirtualQueryableCollectionView)
[英]2d-Array with more than 65535^2 elements --> Array dimensions exceeded supported range
我有一台帶有128 GB RAM的64位PC,我正在使用C#和.NET 4.5。 我有以下代碼:
double[,] m1 = new double[65535, 65535];
long l1 = m1.LongLength;
double[,] m2 = new double[65536, 65536]; // Array dimensions exceeded supported range
long l2 = m2.LongLength;
我知道<gcAllowVeryLargeObjects enabled="true" />
我已將其設置為true。
為什么多維數組不能超過4294967295個元素? 我看到以下答案https://stackoverflow.com/a/2338797/7556646 。
我檢查了gcAllowVeryLargeObjects的文檔,我看到了以下注釋。
數組中的最大元素數是UInt32.MaxValue (4294967295)。
我不明白為什么有這個限制? 有解決方法嗎? 是否計划在即將推出的.net版本中刪除此限制?
我需要在存儲器中的元素,因為我想計算例如使用英特爾MKL的對稱特征值分解。
[DllImport("custom_mkl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern lapack_int LAPACKE_dsyevd(
int matrix_layout, char jobz, char uplo, lapack_int n, [In, Out] double[,] a, lapack_int lda, [In, Out] double[] w);
免責聲明:這個結果比預期更長
CLR不支持托管堆上的大型數組有多種原因。
其中一些是技術性的,其中一些可能是“范式的”。
這篇博文介紹了為什么存在限制的一些原因。 基本上,由於內存碎片,決定限制(大寫O)對象的最大大小。 實現處理較大對象的成本與這樣一個事實進行權衡,即在大多數情況下,由於程序員的設計謬誤,需要這么大的對象和那些需要大量對象的用例。 因為,對於CLR, 一切都是對象,這個限制也適用於數組。 為了強制實施此限制,數組索引器采用帶符號整數設計。
但是一旦你確定,你的程序設計要求你有這么大的陣列,你將需要一個解決方法。
上面提到的博客文章還表明,您可以實現大型數組而無需進入非托管區域。
但正如Evk在評論中指出的那樣,你想通過PInvoke將數組作為一個整體傳遞給外部函數。 這意味着您將需要非托管堆上的數組,或者它必須在調用期間進行封送處理。 整個事情的編組是一個壞主意,這個數組很大。
因此,由於托管堆是不可能的,因此您需要在非托管堆上分配空間並將該空間用於您的陣列。
假設您需要8 GB的空間:
long size = (1L << 33);
IntPtr basePointer = System.Runtime.InteropServices.Marshal.AllocHGlobal((IntPtr)size);
大! 現在,您在虛擬內存中有一個區域,您可以在其中存儲最多8 GB的數據。
如何將其轉換為數組?
那么C#有兩種方法
這將讓你使用指針。 指針可以轉換為數組。 (在香草C中它們通常是同一個)
如果您對如何通過指針實現2D陣列有一個好主意,那么這將是您的最佳選擇。
這是一個指針
您不需要不安全的上下文,而是必須將您的數據從托管堆“編組”到非托管堆。 你仍然需要理解指針算術。
您要使用的兩個主要功能是PtrToStructure和反向StructureToPtr 。 使用其中一個,您將從非托管堆上的指定位置獲取值類型(例如double)的副本。 使用另一個,您將在非托管堆上放置值類型的副本。
從某種意義上說,這兩種方法都是“不安全的”。 你需要知道你的指針
您可能希望將2D陣列設計轉換為一維陣列設計
在任何情況下,您都希望將其全部包含在具有相應檢查和destsructors的類中。
接下來是基於非托管堆的“類似”數組的泛型類。
特色包括:
T
可以成為值類型的類型。 如果你注意到,我沒有進行任何類型檢查,所以如果Marshal.SizeOf
無法返回正確的數字,我們就會落入上面提到的一個坑中。
您必須自己實現的功能包括:
p = x * size + y
因此,只使用它作為靈感,如果有的話。
using static System.Runtime.InteropServices.Marshal;
public class LongArray<T> : IDisposable where T : struct {
private IntPtr _head;
private Int64 _capacity;
private UInt64 _bytes;
private Int32 _elementSize;
public LongArray(long capacity) {
if(_capacity < 0) throw new ArgumentException("The capacity can not be negative");
_elementSize = SizeOf(default(T));
_capacity = capacity;
_bytes = (ulong)capacity * (ulong)_elementSize;
_head = AllocHGlobal((IntPtr)_bytes);
}
public T this[long index] {
get {
IntPtr p = _getAddress(index);
T val = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
return val;
}
set {
IntPtr p = _getAddress(index);
StructureToPtr<T>(value, p, true);
}
}
protected bool disposed = false;
public void Dispose() {
if(!disposed) {
FreeHGlobal((IntPtr)_head);
disposed = true;
}
}
protected IntPtr _getAddress(long index) {
if(disposed) throw new ObjectDisposedException("Can't access the array once it has been disposed!");
if(index < 0) throw new IndexOutOfRangeException("Negative indices are not allowed");
if(!(index < _capacity)) throw new IndexOutOfRangeException("Index is out of bounds of this array");
return (IntPtr)((ulong)_head + (ulong)index * (ulong)(_elementSize));
}
}
我從MrPaulch的這個答案中使用了“Marshal”方法的基本例子,創建了下面一個名為HugeMatrix<T>
:
public class HugeMatrix<T> : IDisposable
where T : struct
{
public IntPtr Pointer
{
get { return pointer; }
}
private IntPtr pointer = IntPtr.Zero;
public int NRows
{
get { return Transposed ? _NColumns : _NRows; }
}
private int _NRows = 0;
public int NColumns
{
get { return Transposed ? _NRows : _NColumns; }
}
private int _NColumns = 0;
public bool Transposed
{
get { return _Transposed; }
set { _Transposed = value; }
}
private bool _Transposed = false;
private ulong b_element_size = 0;
private ulong b_row_size = 0;
private ulong b_size = 0;
private bool disposed = false;
public HugeMatrix()
: this(0, 0)
{
}
public HugeMatrix(int nrows, int ncols, bool transposed = false)
{
if (nrows < 0)
throw new ArgumentException("The number of rows can not be negative");
if (ncols < 0)
throw new ArgumentException("The number of columns can not be negative");
_NRows = transposed ? ncols : nrows;
_NColumns = transposed ? nrows : ncols;
_Transposed = transposed;
b_element_size = (ulong)(Marshal.SizeOf(typeof(T)));
b_row_size = (ulong)_NColumns * b_element_size;
b_size = (ulong)_NRows * b_row_size;
pointer = Marshal.AllocHGlobal((IntPtr)b_size);
disposed = false;
}
public HugeMatrix(T[,] matrix, bool transposed = false)
: this(matrix.GetLength(0), matrix.GetLength(1), transposed)
{
int nrows = matrix.GetLength(0);
int ncols = matrix.GetLength(1);
for (int i1 = 0; i1 < nrows; i1++)
for (int i2 = 0; i2 < ncols; i2++)
this[i1, i2] = matrix[i1, i2];
}
public void Dispose()
{
if (!disposed)
{
Marshal.FreeHGlobal(pointer);
_NRows = 0;
_NColumns = 0;
_Transposed = false;
b_element_size = 0;
b_row_size = 0;
b_size = 0;
pointer = IntPtr.Zero;
disposed = true;
}
}
public void Transpose()
{
_Transposed = !_Transposed;
}
public T this[int i_row, int i_col]
{
get
{
IntPtr p = getAddress(i_row, i_col);
return (T)Marshal.PtrToStructure(p, typeof(T));
}
set
{
IntPtr p = getAddress(i_row, i_col);
Marshal.StructureToPtr(value, p, true);
}
}
private IntPtr getAddress(int i_row, int i_col)
{
if (disposed)
throw new ObjectDisposedException("Can't access the matrix once it has been disposed");
if (i_row < 0)
throw new IndexOutOfRangeException("Negative row indices are not allowed");
if (i_row >= NRows)
throw new IndexOutOfRangeException("Row index is out of bounds of this matrix");
if (i_col < 0)
throw new IndexOutOfRangeException("Negative column indices are not allowed");
if (i_col >= NColumns)
throw new IndexOutOfRangeException("Column index is out of bounds of this matrix");
int i1 = Transposed ? i_col : i_row;
int i2 = Transposed ? i_row : i_col;
ulong p_row = (ulong)pointer + b_row_size * (ulong)i1;
IntPtr p = (IntPtr)(p_row + b_element_size * (ulong)i2);
return p;
}
}
我現在可以調用帶有巨大矩陣的英特爾MKL庫,例如:
[DllImport("custom_mkl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = false)]
internal static extern lapack_int LAPACKE_dsyevd(
int matrix_layout, char jobz, char uplo, lapack_int n, [In, Out] IntPtr a, lapack_int lda, [In, Out] double[] w);
對於參數IntPtr a
我傳遞了HugeMatrix<T>
類的Pointer
屬性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.