2013-05-11 21 views
7

Tôi đang làm việc trên một dự án có liên quan đến việc truy cập dữ liệu từ danh sách lớn được lưu trong bộ nhớ. Bởi vì danh sách này khá là đồ sộ (hàng triệu dòng) nên tôi để mắt đến lượng bộ nhớ đang được sử dụng. Tôi sử dụng OS X để tôi tiếp tục mở Activity Monitor khi tạo các danh sách này.Danh sách Python sử dụng bộ nhớ bất thường

Tôi đã nhận thấy rằng lượng bộ nhớ được sử dụng bởi danh sách có thể khác nhau tùy thuộc vào cách nó được xây dựng nhưng tôi dường như không thể hiểu tại sao.

Bây giờ đối với một số mã ví dụ:

(Tôi đang sử dụng Python 2.7.4 trên OSX 10.8.3)

Chức năng đầu tiên dưới đây tạo ra một danh sách và điền nó với tất cả cùng ba số ngẫu nhiên.

Hàm thứ hai bên dưới tạo danh sách và điền nó với tất cả các số ngẫu nhiên khác nhau.

import random 
import sys 


def make_table1(size): 
    list1 = size *[(float(),float(),float())] # initialize the list 
    line = (random.random(), 
      random.random(), 
      random.random()) 
    for count in xrange(0, size): # Now fill it 
     list1[count] = line 
    return list1 

def make_table2(size): 
    list1 = size *[(float(),float(),float())] # initialize the list 
    for count in xrange(0, size): # Now fill it 
     list1[count] = (random.random(), 
         random.random(), 
         random.random()) 
    return list1 

(Trước tiên cho tôi nói rằng tôi nhận ra đoạn mã trên có thể được viết hiệu quả hơn nhiều. Nó được viết theo cách này để giữ cho hai ví dụ như tương tự càng tốt.)

Bây giờ tôi có thể tạo một số danh sách sử dụng các chức năng này:

In [2]: thing1 = make_table1(6000000) 

In [3]: sys.getsizeof(thing1) 
Out[3]: 48000072 

Tại thời điểm này, bộ nhớ của tôi đã sử dụng nhảy khoảng 46 MB, đó là những gì tôi mong đợi từ thông tin được đưa ra ở trên.

Bây giờ cho các chức năng sau:

In [4]: thing2 = make_table2(6000000) 

In [5]: sys.getsizeof(thing2) 
Out[5]: 48000072 

Như bạn thấy, bộ nhớ đưa lên bởi hai danh sách là như nhau. Chúng có cùng độ dài như vậy, điều đó được mong đợi. Những gì tôi đã không mong đợi là bộ nhớ của tôi được sử dụng như được đưa ra bởi Activity Monitor nhảy đến hơn 1 GB!

Tôi hiểu rằng sẽ có một số chi phí nhưng cao hơn 20x? 1 GB cho danh sách 46MB?

Nghiêm túc?

Được rồi, vào chẩn đoán ...

Điều đầu tiên tôi đã cố gắng là để thu thập bất kỳ rác:

In [5]: import gc 

In [6]: gc.collect() 
Out[6]: 0 

Nó làm không khác biệt với dung lượng bộ nhớ sử dụng.

Tiếp theo, tôi sử dụng cá bảy màu để xem nơi bộ nhớ đang diễn ra:

In [7]: from guppy import hpy 

In [8]: hpy().heap() 

Out[8]: 
Partition of a set of 24217689 objects. Total size = 1039012560 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 6054789 25 484821768 47 484821768 47 tuple 
    1 18008261 74 432198264 42 917020032 88 float 
    2 2267 0 96847576 9 1013867608 98 list 
    3 99032 0 11392880 1 1025260488 99 str 
    4 585 0 1963224 0 1027223712 99 dict of module 
    5 1712 0 1799552 0 1029023264 99 dict (no owner) 
    6 13606 0 1741568 0 1030764832 99 types.CodeType 
    7 13355 0 1602600 0 1032367432 99 function 
    8 1494 0 1348088 0 1033715520 99 type 
    9 1494 0 1300752 0 1035016272 100 dict of type 
<691 more rows. Type e.g. '_.more' to view.> 

okay, bộ nhớ của tôi được đưa lên bởi:

462 MB của tuple (huh?)

412 MB phao (cái gì?)

92 MB danh sách (Được rồi, cái này có ý nghĩa. 2 * 46MB = 92)

Danh sách của tôi được preallocated vì vậy tôi không nghĩ rằng có quá phân bổ đang xảy ra.

Câu hỏi:

Tại sao dung lượng bộ nhớ được sử dụng bởi hai danh sách này rất khác nhau?

Có cách nào khác để điền danh sách không có quá nhiều chi phí không?

Có cách nào giải phóng tất cả bộ nhớ đó không?

Lưu ý: Vui lòng không đề xuất lưu trữ trên đĩa hoặc sử dụng cấu trúc dữ liệu array.array hoặc sumpy hoặc gấu trúc. Đó là tất cả những lựa chọn tuyệt vời nhưng câu hỏi này không phải là về chúng. Câu hỏi này là về các danh sách cũ đơn giản.

Tôi đã thử mã tương tự với Python 3.3 và kết quả giống nhau.

Đây là người có số similar problem. Nó chứa một số gợi ý nhưng nó không phải là cùng một câu hỏi.

Cảm ơn tất cả!

+1

bạn dường như có lợi ích trong một mảng 2ngày kích thước 6000000 x 3; Bạn đã xem xét gumpy (ví dụ: 'numpy.random.rand (6000000, 3)') – SingleNegationElimination

Trả lời

8

Cả hai hàm đều tạo danh sách 6000000 tham chiếu.

sizeof(thelist) ≅ sizeof(reference_to_a_python_object) * 6000000 

Danh sách đầu tiên chứa 6000000 tham chiếu đến cùng một bộ ba phao.

Danh sách thứ hai chứa tham chiếu đến 6000000 bộ dữ liệu khác nhau chứa 18000000 các phao khác nhau.

enter image description here

Như bạn có thể thấy, một phao mất 24 byte và một triple mất 80 byte (sử dụng xây dựng của bạn của python). Không, không có cách nào xung quanh ngoại trừ việc numpy.

Để bật danh sách thành rác phải thu, bạn cần để thoát khỏi của bất kỳ tài liệu tham khảo đối với họ:

del thing1 
del thing2 
Các vấn đề liên quan