2010-06-11 32 views
9

Tôi cần tối ưu hóa việc sử dụng RAM của ứng dụng.
XIN VUI LÒNG cho tôi những bài giảng nói với tôi rằng tôi không nên quan tâm đến bộ nhớ khi mã hóa Python. Tôi có vấn đề về bộ nhớ vì tôi sử dụng từ điển mặc định rất lớn (vâng, tôi cũng muốn nhanh). Mức tiêu thụ bộ nhớ hiện tại của tôi là 350MB và đang tăng lên. Tôi đã không thể sử dụng chia sẻ lưu trữ và nếu Apache của tôi mở ra nhiều quá trình bộ nhớ tăng gấp đôi và gấp ba ... và nó là tốn kém.
Tôi đã thực hiện hồ sơ mở rộng và tôi biết chính xác nơi mà vấn đề của tôi là.
Tôi có nhiều từ điển lớn (> 100K mục) với các phím Unicode. Một từ điển bắt đầu ở 140 byte và phát triển nhanh, nhưng vấn đề lớn hơn là các phím. Python tối ưu hóa chuỗi trong bộ nhớ (hoặc vì vậy tôi đã đọc) để tra cứu có thể được so sánh ID ('interning' chúng). Không chắc chắn điều này cũng đúng đối với các chuỗi unicode (tôi đã không thể 'intern' chúng).
Các đối tượng được lưu trữ trong từ điển là danh sách các bộ dữ liệu (an_object, int, int).Mẹo nhỏ để tối ưu hóa bộ nhớ

my_big_dict [some_unicode_string] .append ((my_object, an_int, another_int))

Tôi đã phát hiện ra rằng nó là giá trị thời gian để chia cho một số từ điển vì tuples mất rất nhiều không gian ..
Tôi thấy rằng tôi có thể tiết kiệm RAM bằng cách băm chuỗi trước khi sử dụng chúng làm khóa! Nhưng sau đó, thật đáng buồn, tôi gặp phải những va chạm sinh nhật trên hệ thống 32 bit của mình. (câu hỏi phụ: có từ điển khóa 64 bit tôi có thể sử dụng trên hệ thống 32 bit không?)

Python 2.6.5 trên cả Linux (sản xuất) và Windows. Bất kỳ mẹo nào về tối ưu hóa việc sử dụng bộ nhớ của từ điển/danh sách/bộ nhớ? Tôi thậm chí còn nghĩ đến việc sử dụng C - Tôi không quan tâm nếu đoạn mã rất nhỏ này là xấu. Nó chỉ là một vị trí số ít.

Cảm ơn trước!

+0

2 nhận xét nhỏ: Tôi thực sự thích câu trả lời cấp hệ thống, nhưng có thể (ví dụ: cơ sở dữ liệu, ngay cả khi được lưu trong bộ nhớ cache) thực sự so sánh hiệu suất với từ điển Python không? Tôi đang chạy thuật toán thời gian thực và từ điển chỉ đủ nhanh. Tôi chắc chắn sẽ cố gắng memcached và Redis (cool) nhưng sẽ liên lạc quá trình liên thông đủ nhanh cho tôi? (xin lỗi vì đã thêm điều này ngay bây giờ. Khó tối ưu hóa cho cả bộ nhớ và tốc độ ...) Ngoài ra, từ điển của tôi chủ yếu là chỉ đọc. Tôi có thể sử dụng kiến ​​thức này bằng cách nào đó không? –

+1

"PEP 412: Từ điển chia sẻ khóa" có thể được bạn quan tâm. Tôi tin rằng nó được đưa vào Python 3.3 http://www.python.org/dev/peps/pep-0412/ – bcoughlan

+0

@bcoughlan rất tuyệt, cảm ơn! Đáng buồn là tôi cần phải chờ đợi một 2.7 backport. –

Trả lời

10

Tôi đề nghị những điều sau đây: lưu trữ tất cả các giá trị trong một DB và giữ một từ điển trong bộ nhớ bằng các chuỗi băm dưới dạng khóa. Nếu xảy ra xung đột, hãy lấy các giá trị từ DB, nếu không (phần lớn các trường hợp) sử dụng từ điển. Hiệu quả, nó sẽ là một bộ nhớ cache khổng lồ.

Một vấn đề với từ điển trong Python là chúng sử dụng nhiều không gian: ngay cả từ điển int-int cũng sử dụng 45-80 byte cho mỗi cặp khóa-giá trị trên hệ thống 32 bit. Đồng thời, array.array('i') chỉ sử dụng 8 byte cho mỗi cặp int và với một chút sổ sách kế toán, người dùng có thể triển khai từ điển nhanh int → int từ điển.

Khi bạn đã thực hiện hiệu quả bộ nhớ của từ điển int-int, hãy chia chuỗi → (đối tượng, int, int) từ điển thành ba từ điển và sử dụng băm thay vì chuỗi đầy đủ. Bạn sẽ nhận được int → đối tượng và hai int → int từ điển. Mô phỏng đối tượng int → từ điển như sau: giữ danh sách đối tượng và chỉ mục lưu trữ của các đối tượng dưới dạng giá trị của một từ điển int → int.

Tôi nhận ra có một số lượng đáng kể mã hóa liên quan để có được một từ điển dựa trên mảng. Tôi đã gặp vấn đề tương tự như vấn đề của bạn và tôi đã triển khai một từ điển hash-int rất nhanh, rất hiệu quả về bộ nhớ. Here's my code (giấy phép BSD). Nó là mảng dựa trên (8 byte cho mỗi cặp), nó sẽ chăm sóc kiểm tra băm và va chạm quan trọng, nó giữ mảng (một số mảng nhỏ hơn, thực sự) ra lệnh trong khi viết và tìm kiếm nhị phân trên lần đọc. mã của bạn được giảm xuống một cái gì đó như:

dictionary = HashIntDict(checking = HashIntDict.CHK_SHOUTING) 
# ... 
database.store(k, v) 
try: 
    dictionary[k] = v 
except CollisionError: 
    pass 
# ... 
try: 
    v = dictionary[k] 
except CollisionError: 
    v = database.fetch(k) 

Tham số checking xác định những gì sẽ xảy ra khi một vụ va chạm xảy ra: CHK_SHOUTING tăng CollisionError về đọc và viết, CHK_DELETING lợi nhuận None vào đọc và vẫn im lặng về viết, CHK_IGNORING không làm kiểm tra va chạm.

Sau đây là mô tả ngắn gọn về việc triển khai của tôi, gợi ý tối ưu hóa được hoan nghênh! Cấu trúc dữ liệu cấp cao nhất là một từ điển thông thường của các mảng. Mỗi mảng chứa tối đa 2^16 = 65536 cặp nguyên (căn bậc hai của 2^32). Khóa k và giá trị tương ứng v đều được lưu trữ trong k/65536-mảng. Các mảng được khởi tạo theo yêu cầu và được sắp xếp theo các khóa. Tìm kiếm nhị phân được thực hiện trên mỗi lần đọc và ghi. Kiểm tra va chạm là một tùy chọn.Nếu được bật, nỗ lực ghi đè khóa đã tồn tại sẽ xóa khóa và giá trị được liên kết khỏi từ điển, thêm khóa vào tập hợp các phím va chạm và (một lần nữa, tùy chọn) sẽ tăng ngoại lệ.

1

Sử dụng shelve hoặc cơ sở dữ liệu để lưu trữ dữ liệu thay vì dict trong bộ nhớ.

2

Tôi đã có các tình huống mà tôi đã có một bộ sưu tập các đối tượng lớn mà tôi cần sắp xếp và lọc theo các phương thức khác nhau dựa trên một số thuộc tính siêu dữ liệu. Tôi không cần những phần lớn hơn của họ vì vậy tôi đổ chúng vào đĩa.

Khi dữ liệu của bạn đơn giản như vậy, một cơ sở dữ liệu SQLite nhanh có thể giải quyết mọi vấn đề của bạn, thậm chí tăng tốc độ lên một chút.

4

Đối với ứng dụng web, bạn nên sử dụng cơ sở dữ liệu, cách bạn đang thực hiện, bạn đang tạo một bản sao dict cho mỗi quy trình apache, điều cực kỳ lãng phí. Nếu bạn có đủ bộ nhớ trên máy chủ, bảng cơ sở dữ liệu sẽ được lưu trữ trong bộ nhớ (nếu bạn không có đủ cho một bản sao của bảng, hãy đặt nhiều RAM hơn vào máy chủ). Chỉ cần nhớ để đưa các chỉ số chính xác vào bảng cơ sở dữ liệu của bạn hoặc bạn sẽ nhận được hiệu suất kém.

0

Nếu bạn muốn tối ưu hóa rộng rãi và có toàn quyền kiểm soát việc sử dụng bộ nhớ, bạn cũng có thể viết mô-đun C/C++. Sử dụng Swig mã gói vào Python có thể được thực hiện dễ dàng, với một số chi phí hoạt động nhỏ so với mô-đun C Python thuần túy.

1

Nếu bạn muốn ở lại với kho lưu trữ dữ liệu trong bộ nhớ, bạn có thể thử một cái gì đó như memcached.

Bằng cách đó, bạn có thể sử dụng một khóa/giá trị lưu trữ trong bộ nhớ duy nhất từ ​​tất cả các quy trình Python.

Có một số thư viện máy khách memcached python.

+3

memcached là mất mát, và như vậy là không phù hợp cho một kho dữ liệu. –

1

Redis sẽ là một tùy chọn tuyệt vời ở đây nếu bạn có tùy chọn sử dụng nó trên máy chủ chia sẻ - tương tự như memcached, nhưng được tối ưu hóa cho cấu trúc dữ liệu. Redis cũng hỗ trợ các ràng buộc python.

Tôi sử dụng nó trên cơ sở hàng ngày để crunching số mà còn trong các hệ thống sản xuất như một kho dữ liệu và không thể khuyên bạn nên nó đủ cao.

Ngoài ra, bạn có tùy chọn proxy ứng dụng của mình sau nginx thay vì sử dụng Apache không? Bạn có thể tìm thấy (nếu được phép) sự sắp xếp proxy/webapp này ít đói hơn về tài nguyên.

Chúc may mắn.

Các vấn đề liên quan