2009-02-21 13 views
5

Sto facendo alcuni esercizi Project Euler e ho eseguito uno scenario in cui I ha desidera matrici di dimensioni superiori a 2.147.483.647 (il limite superiore di int in C#).La dimensione di un array è vincolata dal limite superiore di int (2147483647)?

Certo questi sono grandi array, ma per esempio, non posso fare questo

// fails 
bool[] BigArray = new BigArray[2147483648]; 

// also fails, cannot convert uint to int 
ArrayList BigArrayList = new ArrayList(2147483648); 

Quindi, posso avere array più grandi?

EDIT: E 'stato per un Sieve of Atkin, si sa, quindi ho solo voluto una davvero grande: D

+1

se si sta cercando di creare una tale varietà di risolvere un problema del progetto Eulero, quindi penso che tu abbia scelto una strategia di soluzione scadente per il problema. (Non so se puoi creare array più grandi su x64, spero che qualcuno possa dare una risposta reale alla tua domanda .Net) – Brian

+0

Sì, so che è il caso (ri: strategia) ma sono rimasto scioccato quando ho raggiunto il limite! – inspite

+0

Ho già fatto la stessa domanda, non posso ottenere una risposta completa, spero che tu abbia una risposta per superare questo problema. Http://stackoverflow.com/questions/494923/numbers-that-exceeds-basic-types-in-c – Canavar

risposta

12

Ogni volta che si sta lavorando con una serie così grande, probabilmente si dovrebbe cercare di trovare una soluzione migliore per il problema. Ma detto questo cercherò ancora di rispondere alla tua domanda.

Come menzionato in questo article c'è un limite di 2 GB su qualsiasi oggetto in .Net. Per tutti x86, x64 e IA64.

Come con operativi a 32 bit di Windows sistemi, c'è un limite di 2 GB alla dimensione di un oggetto è possibile creare durante l'esecuzione di un 64-bit gestito applicazione su un sistema operativo Windows a 64 bit.

Inoltre, se si definisce un array troppo grande nello stack, si verificherà un overflow dello stack. Se si definisce la matrice sull'heap, tenterà di allocarlo tutto in un unico grande blocco continuo. Sarebbe meglio usare un ArrayList che ha allocazione dinamica implicita sull'heap. Questo non ti permetterà di superare i 2 GB, ma probabilmente ti permetterò di avvicinarti ad esso.

Penso che il limite della dimensione dello stack sarà maggiore solo se si utilizza un'architettura e un sistema operativo x64 o IA64. Usando x64 o IA64 avrai una memoria allocabile a 64 bit invece di 32-bit.

Se non si è in grado di allocare l'elenco di array tutto in una volta, è probabile che sia possibile allocarlo in parti.

Utilizzando un elenco di array e aggiungendo 1 oggetto alla volta su un computer Windows 64 x64 con 6 GB di RAM, il massimo che posso ottenere è ArrayList: 134217728. Quindi penso davvero che sia necessario trovare una soluzione migliore al tuo problema che non usa tanta memoria. Magari scrivendo su un file invece di usare la RAM.

+0

ma non posso farlo: ArrayList BigArrayList = new ArrayList (2147483648); o – inspite

+0

"overflow dello stack": Capisco che l'array sia nello stack se si tratta di una variabile locale, ma stai dicendo che i ** contenuti ** di un array sono allocati nello stack (invece che sull'heap)? – ChrisW

+0

Sono d'accordo. Questa sarebbe una limitazione dell'heap, non dello stack. – recursive

8

Il limite dell'array è, afaik, corretto come int32 anche su 64 bit. C'è un berretto sulla dimensione massima di un singolo oggetto. Tuttavia, si potrebbe avere abbastanza facilmente un grande array frastagliato.

Peggio; poiché i riferimenti sono maggiori in x64, per gli array di tipo ref in effetti si ottiene in meno di elementi in un singolo array.

Vedi here:

ho ricevuto un numero di query come sul motivo per cui la versione a 64 bit del runtime 2.0 Net ha ancora massima di array dimensioni limitate a 2 GB.Dato che è sembra essere un tema caldo di ritardo I capito un po 'di sfondo e una discussione delle opzioni per ottenere intorno a questa limitazione era in ordine.

Primo sfondo; nella versione 2.0 di .Net runtime (CLR) abbiamo deciso consapevolmente per il di mantenere la dimensione massima dell'oggetto consentita nell'heap GC a 2 GB, anche nella versione a 64 bit del runtime. Questo è uguale alla corrente 1.1 attuazione del CLR 32 bit, tuttavia si sarebbe difficile da effettivamente riescono a allocare un oggetto 2GB sul CLR 32 bit perché lo spazio di indirizzi virtuali è semplicemente troppo frammentato per trovare realisticamente un foro da 2 GB . Generalmente le persone non sono particolarmente preoccupato per la creazione di tipi che sarebbero> 2GB quando istanziato (o dovunque vicino), tuttavia, poiché gli array sono solo uno speciale tipo di tipo gestito quali sono creato all'interno della heap gestito hanno anche soffrire di questa limitazione


Va notato che nella NET 4.5 dimensioni limite memoria è opzionalmente rimosso dal flag gcAllowVeryLargeObjects, tuttavia, questo non cambia la massima dimensione dimensioni. Il punto chiave è che se si dispone di matrici di un tipo personalizzato o di matrici a più dimensioni, è ora possibile andare oltre i 2 GB di memoria.

+0

Molto interessante, se questo è vero, mi chiedo quale sia la giustificazione per l'esistenza della proprietà Array.LongLength. –

+0

È presumibilmente necessario ottenere elementi tra 1gb e 2gb (presupponendo byte []) poiché int è firmato e non volevano usare uint a causa della conformità CLS. –

2

Credo che anche all'interno di un CLR a 64 bit, c'è un limite di 2 GB (o forse 1 GB - non ricordo esattamente) per oggetto. Ciò impedirebbe di creare un array più grande. Il fatto che Array.CreateInstance richieda solo argomenti Int32 per le dimensioni è anch'esso suggestivo.

Su una nota più ampia, sospetto che se hai bisogno di array così grandi, dovresti davvero cambiare il modo in cui ti stai avvicinando al problema.

+0

bello, speravo di ricevere una risposta da te: D – inspite

+0

In una domanda devi ottenere numeri primi fino a 50 miliardi, ma il modo efficace è usare il Sieve di Eratostene che ti obbliga a dichiarare un array con tale index .. http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes – Canavar

+0

Direi che a quel punto * non è * un modo efficace. –

6

Non è necessario alcun array di grandi dimensioni.

Quando il metodo si imbatte in problemi di risorse, non limitarsi a esaminare come espandere le risorse, ma anche il metodo. :)

Ecco una classe che utilizza un buffer da 3 MB per calcolare i primi utilizzando il setaccio di Eratostene. La classe tiene traccia di quanto sono stati calcolati i numeri primi e quando l'intervallo deve essere espanso crea un buffer per testare altri 3 milioni di numeri.

Mantiene i numeri primi trovati in un elenco e quando l'intervallo viene espanso, i numeri primi vengono utilizzati per escludere i numeri nel buffer.

Ho eseguito alcuni test e un buffer di circa 3 MB è il più efficiente.

public class Primes { 

    private const int _blockSize = 3000000; 

    private List<long> _primes; 
    private long _next; 

    public Primes() { 
     _primes = new List<long>() { 2, 3, 5, 7, 11, 13, 17, 19 }; 
     _next = 23; 
    } 

    private void Expand() { 
     bool[] sieve = new bool[_blockSize]; 
     foreach (long prime in _primes) { 
     for (long i = ((_next + prime - 1L)/prime) * prime - _next; 
      i < _blockSize; i += prime) { 
      sieve[i] = true; 
     } 
     } 
     for (int i = 0; i < _blockSize; i++) { 
     if (!sieve[i]) { 
      _primes.Add(_next); 
      for (long j = i + _next; j < _blockSize; j += _next) { 
       sieve[j] = true; 
      } 
     } 
     _next++; 
     } 
    } 

    public long this[int index] { 
     get { 
     if (index < 0) throw new IndexOutOfRangeException(); 
     while (index >= _primes.Count) { 
      Expand(); 
     } 
     return _primes[index]; 
     } 
    } 

    public bool IsPrime(long number) { 
     while (_primes[_primes.Count - 1] < number) { 
     Expand(); 
     } 
     return _primes.BinarySearch(number) >= 0; 
    } 

} 
+0

A livello di efficienza, penso che sarebbe più efficiente avere la dimensione del blocco allineata ad una potenza di 2 (ad es. 3 MB == 3 * 1024 * 1024), perché renderebbe la gestione della memoria un po 'più semplice per il sistema operativo (es. perché la tua memoria è divisa equamente nelle pagine). –

+1

Non sarebbe più efficiente usare set di bit invece di array booleani? Potrebbe risparmiare molto spazio per lo meno. –

+0

@HosamAly: questo non è importante dal momento che siamo nello spazio gestito. – mafu

1

Sono molto un principiante con C# (vale a dire l'apprendimento questa settimana), quindi non sono sicuro dei dettagli esatti di come viene implementato ArrayList. Tuttavia, suppongo che siccome non hai definito un tipo per l'esempio ArrayList, l'array verrebbe assegnato come una matrice di riferimenti a oggetti. Ciò potrebbe significare che in realtà stai allocando 4-8 GB di memoria a seconda dell'architettura.

+0

Buon punto, i booleani occupano 4 byte in .NET e, quindi, 2 GB di booleani sono 8 GB totali. La classe ArrayList viene implementata come matrice internamente che riassegna una nuova (più grande) matrice in base alle esigenze per adattarsi a dimensioni maggiori: http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx –

+1

In realtà, usa molto di più. In un array bool ogni bool utilizza solo un byte, ma in un ArrayList ogni bool usa 16 byte. Ogni riferimento è 4 byte, ogni oggetto che inscatola un bool ha due puntatori intermedi e 4 byte per il bool. Quindi un ArrayList con 2 milioni di booleans usa 32 GB di memoria. – Guffa

+0

@Guffa - o peggio * di nuovo * su x64, poiché i riferimenti sono più grandi ;-p –

0

According to MSDN, l'indice per la matrice di byte non può essere maggiore di 2147483591. Per .NET precedente alla 4.5 era anche un limite di memoria per un array. In .NET 4.5 questo massimo è lo stesso, ma per gli altri tipi può essere fino a 2146435071.

Questo è il codice per l'illustrazione:

static void Main(string[] args) 
    { 
     // ----------------------------------------------- 
     // Pre .NET 4.5 or gcAllowVeryLargeObjects unset 
     const int twoGig = 2147483591; // magic number from .NET 

     var type = typeof(int);   // type to use 
     var size = Marshal.SizeOf(type); // type size 
     var num = twoGig/size;   // max element count 

     var arr20 = Array.CreateInstance(type, num); 
     var arr21 = new byte[num]; 

     // ----------------------------------------------- 
     // .NET 4.5 with x64 and gcAllowVeryLargeObjects set 
     var arr451 = new byte[2147483591]; 
     var arr452 = Array.CreateInstance(typeof(int), 2146435071); 
     var arr453 = new byte[2146435071]; // another magic number 

     return; 
    }