2013-09-23 34 views
5

Tôi đang cố gắng tìm nguồn gốc của rò rỉ bộ nhớ khó chịu trong chương trình Python/NumPy bằng cách sử dụng các phần mở rộng C/Cython và multiprocessing.Gỡ lỗi rò rỉ bộ nhớ Python/NumPy

Mỗi quy trình con xử lý danh sách hình ảnh và mỗi bộ xử lý gửi mảng đầu ra (thường có kích thước khoảng 200-300MB) thông qua Queue đến quy trình chính. Thiết lập bản đồ/giảm tiêu chuẩn hợp lý.

Như bạn có thể tưởng tượng một rò rỉ bộ nhớ có thể mất tỷ lệ ginormous với mảng này lớn, và có nhiều quy trình vui vẻ đi hơn 20GB RAM khi họ sẽ chỉ cần 5-6GB là ... gây phiền nhiễu.

  • Tôi đã thử chạy bản dựng gỡ lỗi của Python thông qua Valgrind và kiểm tra phần mở rộng của tôi cho rò rỉ bộ nhớ nhưng không tìm thấy gì.

  • Tôi đã kiểm tra mã Python của mình để tham chiếu đến các mảng của tôi và cũng sử dụng số allocation tracker của NumPy để kiểm tra xem các mảng của tôi có thực sự được phát hành hay không. Họ là.

Điều cuối cùng tôi đã đính kèm GDB vào một trong các quy trình của tôi (bad boy này hiện đang chạy ở RAM 27 GB và đếm) và bán một phần lớn đĩa vào đĩa. Nhiều đến ngạc nhiên của tôi, các tập tin được đổ đầy đã đầy zeroes! Khoảng 7G giá trị của số 0.

Đây có phải là hành vi phân bổ bộ nhớ tiêu chuẩn trong Python/NumPy không? Tôi đã bỏ lỡ một cái gì đó hiển nhiên mà sẽ giải thích có quá nhiều bộ nhớ được sử dụng cho không có gì? Làm thế nào tôi có thể quản lý bộ nhớ đúng cách?


EDIT: Đối với hồ sơ, tôi đang chạy NumPy 1.7.1 và Python 2.7.3.

EDIT 2: Tôi đã theo dõi quá trình với strace, và có vẻ như nó vẫn không ngừng tăng điểm break của mỗi quá trình (sử dụng brk() syscall).

CPython có thực sự giải phóng bộ nhớ đúng cách không? Điều gì về phần mở rộng C, mảng NumPy? Ai quyết định khi nào nên gọi brk(), có phải là chính Python hay là thư viện cơ bản (libc, ...)?

Dưới đây là nhật ký mẫu thử có nhận xét, từ một lần lặp (tức là một bộ hình ảnh đầu vào). Lưu ý rằng điểm ngắt tiếp tục tăng, nhưng tôi đã chắc chắn (với objgraph) rằng không có mảng NumPy có ý nghĩa nào được giữ bên trong trình thông dịch Python.

# Reading .inf files with metadata 
# Pretty small, no brk() 
open("1_tif_all/AIR00642_1.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_2.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_3.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 
open("1_tif_all/AIR00642_4.inf", O_RDONLY) = 6 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000 
munmap(0x7f9387fff000, 4096)   = 0 

# This is where I'm starting the heavy processing 
write(2, "[INFO/MapProcess-1] Shot 642: Da"..., 68) = 68 
write(2, "[INFO/MapProcess-1] Shot 642: Vi"..., 103) = 103 
write(2, "[INFO/MapProcess-1] Shot 642: Re"..., 66) = 66 

# I'm opening a .tif image (752 x 480, 8-bit, 1 channel) 
open("1_tif_all/AIR00642_3.tif", O_RDONLY) = 6 
read(6, "II*\0JC\4\0", 8)    = 8 
mmap(NULL, 279600, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fbb000 
munmap(0x7f9387fbb000, 279600)   = 0 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 53) = 53 

# Another .tif 
open("1_tif_all/AIR00642_4.tif", O_RDONLY) = 6 
read(6, "II*\0\266\374\3\0", 8)   = 8 
mmap(NULL, 261532, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fc0000 
munmap(0x7f9387fc0000, 261532)   = 0 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 51) = 51 
brk(0x1aea97000)      = 0x1aea97000 

# Another .tif 
open("1_tif_all/AIR00642_1.tif", O_RDONLY) = 6 
read(6, "II*\0\220\253\4\0", 8)   = 8 
mmap(NULL, 306294, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fb5000 
munmap(0x7f9387fb5000, 306294)   = 0 
brk(0x1af309000)      = 0x1af309000 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 53) = 53 
brk(0x1b03da000)      = 0x1b03da000 

# Another .tif 
open("1_tif_all/AIR00642_2.tif", O_RDONLY) = 6 
mmap(NULL, 345726, PROT_READ, MAP_SHARED, 6, 0) = 0x7f9387fab000 
munmap(0x7f9387fab000, 345726)   = 0 
brk(0x1b0c42000)      = 0x1b0c42000 
write(2, "[INFO/MapProcess-1] Shot 642: Pr"..., 51) = 51 

# I'm done reading my images 
write(2, "[INFO/MapProcess-1] Shot 642: Fi"..., 72) = 72 

# Allocating some more arrays for additional variables 
# Increases by about 8M at a time 
brk(0x1b1453000)      = 0x1b1453000 
brk(0x1b1c63000)      = 0x1b1c63000 
brk(0x1b2473000)      = 0x1b2473000 
brk(0x1b2c84000)      = 0x1b2c84000 
brk(0x1b3494000)      = 0x1b3494000 
brk(0x1b3ca5000)      = 0x1b3ca5000 

# What are these mmap calls doing here? 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9377df1000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9367be2000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93579d3000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93477c4000 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93375b5000 
munmap(0x7f93579d3000, 270594048)  = 0 
munmap(0x7f93477c4000, 270594048)  = 0 
mmap(NULL, 270594048, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93579d3000 
munmap(0x7f93375b5000, 270594048)  = 0 
mmap(NULL, 50737152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9354970000 
munmap(0x7f9354970000, 50737152)  = 0 
brk(0x1b4cc6000)      = 0x1b4cc6000 
brk(0x1b5ce7000)      = 0x1b5ce7000 

EDIT 3:Is freeing handled differently for small/large numpy arrays? thể có liên quan. Tôi ngày càng bị thuyết phục rằng tôi chỉ đơn giản là phân bổ quá nhiều mảng mà không được phát hành cho hệ thống bởi vì nó thực sự là hành vi tiêu chuẩn. Sẽ cố gắng phân bổ mảng của tôi trước và tái sử dụng chúng khi cần thiết.

+0

Bạn đang sử dụng gì để đọc tệp hình ảnh? Tôi đã có vấn đề rò rỉ bộ nhớ với các đối tượng PIL 'Image' trong quá khứ –

+0

Tôi đang sử dụng các ràng buộc PyLibTiff. Và tôi đã giải quyết vấn đề này, hãy xem câu trả lời của tôi! –

Trả lời

1

Doh. Tôi thực sự nên đã kiểm tra các tiện ích mở rộng C đó lần thứ năm.

Tôi đã quên giảm số lượng tham chiếu trong một trong các mảng NumPy tạm thời mà tôi đã phân bổ từ C. Mảng không để lại mã C, vì vậy tôi không thấy rằng tôi cần phải giải quyết nó.

Tôi vẫn không biết tại sao nó không xuất hiện trong objgraph.