2009-02-21 34 views
5

Tôi đang thực hiện một số bài tập Project Euler và tôi đã chạy vào một trường hợp mà tôi muốn các mảng lớn hơn 2.147.483.647 (giới hạn trên của int trong C#).Kích thước của một mảng có bị hạn chế bởi giới hạn trên của int (2147483647) không?

Chắc chắn đây là những mảng lớn, nhưng ví dụ, tôi không thể làm điều này

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

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

Vì vậy, tôi có thể có mảng lớn hơn không?

EDIT: Đó là một Sieve of Atkin, bạn biết đấy, vì vậy tôi chỉ muốn có một thực sự lớn nhất: D

+1

Nếu bạn đang cố gắng để tạo ra một mảng như vậy để giải quyết một dự án vấn đề Euler, sau đó tôi nghĩ rằng bạn đã chọn một chiến lược giải pháp kém cho vấn đề này. (Dunno nếu có thể tạo mảng lớn hơn trên x64, hy vọng ai đó sẽ trả lời thực sự cho câu hỏi .Net của bạn.) – Brian

+0

Vâng, tôi biết đó là trường hợp (lại: chiến lược) nhưng tôi đã bị sốc khi tôi đạt đến giới hạn! – inspite

+0

Tôi hỏi cùng một câu hỏi trước đó, không thể có được câu trả lời hoàn chỉnh, hy vọng bạn sẽ có được một câu trả lời để khắc phục vấn đề này .. http://stackoverflow.com/questions/494923/numbers-that-exceeds-basic-types-in-c – Canavar

Trả lời

12

Bất cứ lúc nào bạn đang làm việc với một mảng lớn này, có lẽ bạn nên cố gắng tìm một giải pháp tốt hơn để vấn đề. Nhưng điều đó đang được nói là tôi vẫn cố gắng trả lời câu hỏi của bạn.

Như đã đề cập trong điều này article có giới hạn 2 GB đối với mọi đối tượng trong .Net. Đối với tất cả x86, x64 và IA64.

Như với Windows 32-bit hoạt động hệ thống, có một giới hạn 2 GB vào kích thước của một đối tượng, bạn có thể tạo ra trong khi chạy một phiên bản 64-bit quản lý ứng dụng trên hệ điều hành Windows 64-bit.

Ngoài ra nếu bạn xác định một mảng quá lớn trên ngăn xếp, bạn sẽ bị tràn ngăn xếp. Nếu bạn định nghĩa mảng trên heap, nó sẽ cố gắng phân bổ tất cả trong một khối liên tục lớn. Nó sẽ là tốt hơn để sử dụng một ArrayList có phân bổ động tiềm ẩn trên heap. Điều này sẽ không cho phép bạn vượt qua 2GB, nhưng có lẽ sẽ cho phép bạn tiến gần hơn đến nó.

Tôi nghĩ giới hạn kích thước ngăn xếp sẽ lớn hơn nếu bạn đang sử dụng kiến ​​trúc và hệ điều hành x64 hoặc IA64. Sử dụng x64 hoặc IA64, bạn sẽ có bộ nhớ phân bổ 64 bit thay vì 32 bit.

Nếu bạn không thể phân bổ danh sách mảng cùng một lúc, bạn có thể phân bổ nó trong các phần.

Sử dụng danh sách mảng và thêm 1 đối tượng cùng lúc trên máy x64 Windows 2008 có RAM 6 GB, nhiều nhất tôi có thể lấy ArrayList là kích thước: 134217728. Vì vậy, tôi thực sự nghĩ bạn phải tìm một giải pháp tốt hơn cho vấn đề của bạn không sử dụng nhiều bộ nhớ. Có thể ghi vào một tập tin thay vì sử dụng RAM.

+0

nhưng tôi không thể làm điều này: ArrayList BigArrayList = new ArrayList (2147483648); hoặc là – inspite

+0

"tràn ngăn xếp": Tôi hiểu mảng đang ở trên ngăn xếp nếu đó là biến cục bộ, nhưng bạn có nói rằng ** nội dung ** của một mảng được phân bổ trên ngăn xếp không (thay vì trên heap)? – ChrisW

+0

Tôi đồng ý. Điều này sẽ là một hạn chế heap, không ngăn xếp. – recursive

8

Giới hạn mảng là, afaik, được sửa thành int32 ngay cả trên 64 bit. Có một nắp trên kích thước tối đa của một đối tượng duy nhất. Tuy nhiên, bạn có thể có một mảng lởm chởm khá đẹp mắt khá dễ dàng.

Tệ hơn; vì tham chiếu lớn hơn trong x64, đối với mảng kiểu ref bạn thực sự nhận được ít hơn yếu tố trong một mảng.

Xem here:

Tôi đã nhận được một số thắc mắc như tại sao phiên bản 64-bit của runtime 2.0 Net vẫn có mảng tối đa kích thước giới hạn 2GB.Cho rằng nó có vẻ là một chủ đề nóng của cuối tôi tìm một nền tảng nhỏ và một cuộc thảo luận về các tùy chọn để có được xung quanh giới hạn này là theo thứ tự.

Đầu tiên một số nền; trong phiên bản 2.0 của thời gian chạy .Net (CLR), chúng tôi đưa ra quyết định thiết kế có ý thức để giữ kích thước đối tượng tối đa cho phép trong GC Heap ở 2GB, ngay cả trên phiên bản 64 bit của thời gian chạy. Đây là giống như việc thực hiện 1.1 hiện tại của CLR 32 bit, tuy nhiên bạn sẽ khó bấm thực sự quản lý phân bổ đối tượng 2GB trên CLR 32 bit vì không gian địa chỉ ảo cũng đơn giản phân mảnh để thực tế tìm thấy lỗ 2GB . Nói chung mọi người không đặc biệt quan tâm đến việc tạo ra loại đó sẽ là> 2GB khi instantiated (hoặc bất cứ nơi nào gần), tuy nhiên kể từ mảng chỉ là một loại đặc biệt kiểu quản lý mà là tạo bên trong đống quản lý họ cũng bị giới hạn này.


Cần lưu ý rằng trong .NET 4.5 bộ nhớ kích thước giới hạn tùy chọn gỡ bỏ bởi các gcAllowVeryLargeObjects cờ, tuy nhiên, điều này không làm thay đổi tối đa Tham số kích thước. Điểm mấu chốt là nếu bạn có mảng của một loại tùy chỉnh, hoặc mảng đa chiều, thì bây giờ bạn có thể vượt quá 2GB trong kích thước bộ nhớ.

+0

Rất thú vị, nếu điều này là đúng, tôi tự hỏi những gì biện minh cho sự tồn tại của tài sản Array.LongLength. –

+0

Nó có lẽ là cần thiết để có được các yếu tố giữa 1gb và 2gb (giả sử byte []) kể từ khi int được ký kết, và họ không muốn sử dụng uint do CLS tuân thủ. –

2

Tôi tin rằng ngay cả trong CLR 64 bit, có giới hạn 2GB (hoặc có thể 1GB - tôi không thể nhớ chính xác) cho mỗi đối tượng. Điều đó sẽ ngăn cản bạn tạo ra một mảng lớn hơn. Thực tế là Array.CreateInstance chỉ lấy đối số Int32 cho các kích thước cũng là gợi ý.

Trên một lưu ý rộng hơn, tôi nghi ngờ rằng nếu bạn cần mảng lớn, bạn thực sự nên thay đổi cách bạn đang tiếp cận vấn đề.

+0

tốt đẹp, tôi đã hy vọng I'ld nhận được một phản hồi từ bạn: D – inspite

+0

Trong một câu hỏi mà bạn cần phải nhận được số nguyên tố lên đến 50 tỷ đồng, nhưng cách hiệu quả là sử dụng Các Sieve of Eratosthenes mà buộc bạn phải khai báo một mảng với ví dụ index .. http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes – Canavar

+0

Tôi cho rằng tại thời điểm đó nó * không phải là * một cách hiệu quả. –

6

Bạn không cần một mảng lớn chút nào.

Khi phương pháp của bạn gặp sự cố tài nguyên, không chỉ xem xét cách mở rộng tài nguyên, hãy xem phương pháp này. :)

Đây là lớp sử dụng bộ đệm 3 MB để tính toán số nguyên tố bằng sàng của Eratosthenes. Lớp này theo dõi bạn đã tính số nguyên tố bao xa và khi phạm vi cần được mở rộng, nó tạo ra bộ đệm để kiểm tra 3 triệu số khác.

Nó giữ số nguyên tố được tìm thấy trong danh sách và khi phạm vi được mở rộng, số nguyên tố tỷ lệ được sử dụng để loại trừ các số trong bộ đệm.

Tôi đã thực hiện một số thử nghiệm và bộ đệm khoảng 3 MB là hiệu quả nhất.

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

Hiệu quả khôn ngoan, tôi nghĩ sẽ hiệu quả hơn nếu kích thước khối của bạn được căn chỉnh với một số công suất 2 (ví dụ 3 MB == 3 * 1024 * 1024), vì nó sẽ giúp quản lý bộ nhớ dễ dàng hơn cho hệ điều hành (ví dụ: vì bộ nhớ của bạn được chia đều thành các trang). –

+1

Sẽ không hiệu quả hơn khi sử dụng các bộ bit thay vì các mảng boolean? Nó có thể tiết kiệm nhiều không gian ở mức tối thiểu. –

+0

@HosamAly: Điều này không quan trọng vì chúng tôi đang ở trong không gian được quản lý. – mafu

1

Tôi là một người mới với C# (tức là học trong tuần này), vì vậy tôi không chắc chắn chi tiết chính xác về cách ArrayList được triển khai. Tuy nhiên, tôi sẽ đoán rằng khi bạn chưa định nghĩa một kiểu cho ví dụ ArrayList, thì mảng sẽ được phân bổ như là một mảng các tham chiếu đối tượng. Điều này cũng có nghĩa là bạn đang thực sự phân bổ 4-8Gb bộ nhớ tùy thuộc vào kiến ​​trúc.

+0

Điểm tốt, booleans mất 4 byte trong .NET và, do đó, 2 GB booleans là tổng số 8 GB. Lớp ArrayList được thực hiện như là một mảng nội bộ mà phân bổ lại một mảng mới (lớn hơn) khi cần để chứa các kích thước lớn hơn: http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx –

+1

Trên thực tế, nó sử dụng nhiều hơn thế. Trong một mảng bool, mỗi bool chỉ sử dụng một byte, nhưng trong một ArrayList, mỗi bool sử dụng 16 byte. Mỗi tham chiếu là 4 byte, mỗi đối tượng boxing một bool có hai con trỏ giữa và 4 byte cho bool. Vì vậy, một ArrayList với 2 triệu booleans sử dụng 32 GB bộ nhớ. – Guffa

+0

@Guffa - hoặc tệ hơn * lần nữa * trên x64, vì tham chiếu lớn hơn ;-p –

0

According to MSDN, chỉ mục cho mảng byte không được lớn hơn 2147483591. Đối với .NET trước 4.5 thì cũng là giới hạn bộ nhớ cho mảng. Trong .NET 4.5 tối đa này là như nhau, nhưng đối với các loại khác, nó có thể lên đến 2146435071.

Đây là mã để minh hoạ:

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; 
    } 
Các vấn đề liên quan