2009-08-22 41 views
185

Tôi đã viết một chương trình Python hoạt động trên một tệp đầu vào lớn để tạo ra một vài triệu đối tượng đại diện cho hình tam giác. Thuật toán là:Làm thế nào tôi có thể giải phóng bộ nhớ một cách rõ ràng bằng Python?

  1. đọc một tập tin đầu vào
  2. quá trình tập tin và tạo ra một danh sách các hình tam giác, đại diện bởi các đỉnh của họ
  3. đầu ra các đỉnh trong các định dạng OFF: một danh sách các đỉnh theo sau là một danh sách hình tam giác. Các hình tam giác được biểu thị bằng các chỉ mục vào danh sách các đỉnh

Yêu cầu TẮT mà tôi in ra danh sách các đỉnh của tam giác trước khi in ra tam giác có nghĩa là tôi phải giữ danh sách các tam giác trong bộ nhớ trước ghi đầu ra vào tập tin. Trong khi đó tôi nhận được lỗi bộ nhớ vì kích thước của danh sách.

Cách tốt nhất để nói với Python là tôi không còn cần một số dữ liệu nữa và nó có thể được giải phóng?

+5

Tại sao không in ra tam giác để một file trung gian, và đọc chúng trở lại trong một lần nữa khi bạn cần đến chúng? –

Trả lời

169

Theo Python Official Documentation, bạn có thể buộc các Collector rác để giải phóng bộ nhớ unreferenced với gc.collect(). Ví dụ:

import gc 
gc.collect() 
+8

Mọi thứ là rác thường xuyên thu thập, ngoại trừ trong một số trường hợp bất thường, vì vậy tôi không nghĩ rằng sẽ giúp nhiều. –

+4

Có thể không đủ thường xuyên. Anh ta có thể tạo ra "triệu vật thể" này trong vật chất của hàng triệu. – Havenard

+2

Vâng, nhưng bộ thu gom rác đang chạy mỗi lần thứ 700 bạn tạo một đối tượng, vì vậy nó sẽ chạy hàng nghìn lần trong những mili giây đó. –

18

Python được thu thập rác, vì vậy nếu bạn giảm kích thước danh sách của mình, nó sẽ lấy lại bộ nhớ. Bạn cũng có thể sử dụng "del" tuyên bố để thoát khỏi một biến hoàn toàn:

biglist = [blah,blah,blah] 
#... 
del biglist 
+8

Điều này là không đúng. Trong khi giảm kích thước của danh sách cho phép bộ nhớ được khai hoang, không có gì đảm bảo khi điều này xảy ra. – user142350

+1

Không, nhưng thông thường nó sẽ giúp ích. Tuy nhiên, khi tôi hiểu câu hỏi ở đây, vấn đề là anh ta phải có quá nhiều đồ vật mà anh ta hết bộ nhớ trước khi xử lý tất cả, nếu anh ta đọc chúng vào một danh sách. Việc xóa danh sách trước khi xử lý xong không phải là giải pháp hữu ích. ;) –

+1

Cũng lưu ý rằng del không đảm bảo rằng một đối tượng sẽ bị xóa. Nếu có các tham chiếu khác cho đối tượng, nó sẽ không được giải phóng. –

24

Tuyên bố del có thể sử dụng, nhưng IIRC nó không được đảm bảo để giải phóng bộ nhớ. docs are here ... và why it isn't released is here.

Tôi đã nghe mọi người trên các hệ thống kiểu Linux và Unix yêu cầu một quy trình python để thực hiện một số công việc, nhận kết quả và sau đó giết chết nó.

This article có ghi chú trên garbage collector Python, nhưng tôi nghĩ rằng thiếu kiểm soát bộ nhớ là nhược điểm để bộ nhớ được quản lý

+0

IronPython và Jython có phải là một tùy chọn khác để tránh sự cố này không? – voyager

+0

@voyager: Không, không. Và không phải bất kỳ ngôn ngữ nào khác, thực sự. Vấn đề là anh ta đọc một lượng lớn dữ liệu vào một danh sách, và dữ liệu quá lớn đối với bộ nhớ. –

+0

Nó có thể sẽ * tồi tệ hơn * dưới IronPython hoặc Jython. Trong những môi trường đó, bạn thậm chí không được bảo đảm bộ nhớ sẽ được giải phóng nếu không có gì khác đang giữ một tham chiếu. –

14

Bạn không thể nhớ một cách rõ ràng miễn phí. Những gì bạn cần làm là đảm bảo rằng bạn không giữ tham chiếu đến các đối tượng. Sau đó, họ sẽ thu gom rác thải, giải phóng bộ nhớ.

Trong trường hợp của bạn, khi bạn cần danh sách lớn, bạn thường cần tổ chức lại mã, thường sử dụng trình tạo/trình lặp thay thế. Bằng cách đó bạn không cần phải có danh sách lớn trong bộ nhớ.

http://www.prasannatech.net/2009/07/introduction-python-generators.html

+1

Nếu cách tiếp cận này là khả thi, thì có lẽ nó đáng làm. Nhưng cần lưu ý rằng bạn không thể truy cập ngẫu nhiên trên các trình vòng lặp, điều này có thể gây ra vấn đề. –

+0

Đó là sự thật, và nếu đó là cần thiết, sau đó truy cập dữ liệu lớn dữ liệu ngẫu nhiên có thể yêu cầu một số loại cơ sở dữ liệu. –

+0

Bạn có thể dễ dàng sử dụng trình lặp để trích xuất một tập hợp con ngẫu nhiên của một trình lặp khác. –

83

Thật không may (tùy thuộc vào phiên bản và phát hành Python của bạn) một số loại đối tượng sử dụng "danh sách miễn phí", mà là một tối ưu hóa địa phương gọn gàng nhưng có thể gây phân mảnh bộ nhớ, đặc biệt bằng cách làm cho bộ nhớ ngày càng nhiều "dành "chỉ cho các đối tượng thuộc một loại nhất định và do đó không có sẵn cho" quỹ chung ".

Cách duy nhất thực sự đáng tin cậy để đảm bảo rằng việc sử dụng bộ nhớ lớn nhưng tạm thời KHÔNG trả lại tất cả các tài nguyên cho hệ thống khi nó được thực hiện, có sử dụng đó xảy ra trong một tiến trình con không. Trong những điều kiện như vậy, hệ điều hành S W thực hiện công việc của mình và sẵn sàng tái chế tất cả các tài nguyên mà quy trình con có thể đã gobbled lên.May mắn thay, mô-đun multiprocessing làm cho loại hoạt động này (vốn thường là một nỗi đau) không quá tệ trong các phiên bản Python hiện đại.

Trong trường hợp sử dụng của bạn, có vẻ như cách tốt nhất cho các quy trình con để tích lũy một số kết quả và đảm bảo các kết quả đó có sẵn cho quy trình chính là sử dụng các tệp bán tạm thời (bằng bán tạm thời, loại tệp tự động biến mất khi đóng, chỉ các tệp thông thường mà bạn xóa hoàn toàn khi bạn đã hoàn tất các tệp đó).

+18

Tôi chắc chắn muốn xem một ví dụ tầm thường về điều này. –

+3

Nghiêm túc. Những gì @AaronHall nói. –

+10

@AaronHall Ví dụ tầm thường [hiện có sẵn] (http://stackoverflow.com/a/24126616/1600898), sử dụng 'multiprocessing.Manager' thay vì tệp để triển khai trạng thái được chia sẻ. – user4815162342

2

Nếu bạn không quan tâm đến việc tái sử dụng đỉnh, bạn có thể có hai tệp đầu ra - một cho đỉnh và một cho hình tam giác. Sau đó thêm tệp tam giác vào tệp đỉnh khi bạn hoàn thành.

+0

Tôi hình tôi chỉ có thể giữ các đỉnh trong bộ nhớ và in các hình tam giác ra một tập tin, và sau đó in ra các đỉnh chỉ ở cuối. Tuy nhiên, hành động viết các hình tam giác vào một tập tin là một cống hiệu suất rất lớn. Có cách nào để tăng tốc độ * mà * lên không? –

6

Những người khác đã đăng một số cách mà bạn có thể "coax" trình thông dịch Python để giải phóng bộ nhớ (hoặc tránh các vấn đề về bộ nhớ). Có thể bạn nên thử ý tưởng của mình trước tiên. Tuy nhiên, tôi cảm thấy điều quan trọng là cung cấp cho bạn câu trả lời trực tiếp cho câu hỏi của bạn.

Không thực sự có cách nào để trực tiếp cho Python biết về bộ nhớ trống. Thực tế của vấn đề đó là nếu bạn muốn mức điều khiển thấp đó, bạn sẽ phải viết một phần mở rộng trong C hoặc C++.

Điều đó nói rằng, có một số công cụ để giúp với điều này:.

+0

gc.collect() và del gc.garbage [:] hoạt động tốt khi tôi đang sử dụng lượng bộ nhớ lớn –

12

(giải phóng bộ nhớ được định kỳ thực hiện tự động del có thể do đó bạn bạn bè, vì nó đánh dấu các đối tượng như bị xóa.)

Có thể bạn sẽ không gặp phải bất kỳ vấn đề nào về bộ nhớ ngay từ đầu bằng cách sử dụng cấu trúc nhỏ gọn hơn cho dữ liệu của bạn. Vì vậy, danh sách các số ít hiệu quả về bộ nhớ hơn so với định dạng được sử dụng bởi mô-đun array tiêu chuẩn hoặc mô-đun numpy của bên thứ ba. Bạn sẽ tiết kiệm bộ nhớ bằng cách đặt đỉnh của bạn trong một mảng NumPy 3xN và hình tam giác của bạn trong một mảng phần tử N.

3

Tôi gặp sự cố tương tự khi đọc biểu đồ từ tệp. Việc xử lý bao gồm việc tính toán ma trận phao 200 000x200 000 (một dòng tại một thời điểm) không phù hợp với bộ nhớ. Cố gắng giải phóng bộ nhớ giữa các tính toán bằng cách sử dụng gc.collect() cố định khía cạnh liên quan đến bộ nhớ nhưng vấn đề hiệu suất: Tôi không biết tại sao nhưng mặc dù số lượng bộ nhớ đã sử dụng không đổi, mỗi cuộc gọi mới đến gc.collect() thời gian hơn so với trước đó. Vì vậy, khá nhanh chóng việc thu gom rác chiếm phần lớn thời gian tính toán.

Để khắc phục cả vấn đề về bộ nhớ và hiệu năng tôi đã chuyển sang sử dụng mẹo đa luồng tôi đã đọc ở đâu đó (xin lỗi, tôi không thể tìm thấy bài đăng liên quan nữa). Trước khi tôi đọc từng dòng của tập tin trong một vòng lặp lớn for, xử lý nó và chạy gc.collect() mỗi một lần và một thời gian để giải phóng dung lượng bộ nhớ. Bây giờ tôi gọi một hàm đọc và xử lý một đoạn của tập tin trong một chủ đề mới. Khi thread kết thúc, bộ nhớ sẽ tự động được giải phóng mà không có vấn đề về hiệu suất lạ.

Thực tế nó hoạt động như thế này:

from dask import delayed // this module wraps the multithreading 
def f(storage, index, chunk_size): // the processing function 
    // read the chunk of size chunk_size starting at index in the file 
    // process it using data in storage if needed 
    // append data needed for further computations to storage 
    return storage 

partial_result = delayed([]) // put into the delayed() the constructor for your data structure 
// i personally use "delayed(nx.Graph())" since I am creating a networkx Graph 
chunk_size = 100 // ideally you want this as big as possible while still enabling the computations to fit in memory 
for index in range(0, len(file), chunk_size): 
    // we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size 
    partial_result = delayed(f)(partial_result, index, chunk_size) 

    // no computations are done yet ! 
    // dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute() 
    // passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done 
    // it also allows you to use the results of the processing of the previous chunks in the file if needed 

// this launches all the computations 
result = partial_result.compute() 

// one thread is spawned for each "delayed" one at a time to compute its result 
// dask then closes the tread, which solves the memory freeing issue 
// the strange performance issue with gc.collect() is also avoided 
Các vấn đề liên quan