Hướng dẫn does python consume more memory? - python có tốn nhiều bộ nhớ hơn không?

Tìm hiểu lý do tại sao các ứng dụng Python của bạn đang sử dụng quá nhiều bộ nhớ và giảm sử dụng RAM của chúng với các thủ thuật đơn giản này và cấu trúc dữ liệu hiệu quả

Ảnh của Apeventurer Bayc buồn chán trên Unplash

Khi nói đến tối ưu hóa hiệu suất, mọi người thường chỉ tập trung vào việc sử dụng tốc độ và CPU. Hiếm khi bất cứ ai quan tâm đến việc tiêu thụ bộ nhớ, tốt, cho đến khi họ hết RAM. Có nhiều lý do để cố gắng hạn chế sử dụng bộ nhớ, không chỉ tránh bị lỗi ứng dụng của bạn vì các lỗi ngoài bộ nhớ.

Trong bài viết này, chúng tôi sẽ khám phá các kỹ thuật để tìm ra phần nào trong các ứng dụng Python của bạn đang tiêu thụ quá nhiều bộ nhớ, phân tích lý do cho nó và cuối cùng làm giảm mức tiêu thụ bộ nhớ và dấu chân bằng cách sử dụng các thủ thuật đơn giản và cấu trúc dữ liệu hiệu quả bộ nhớ.

Tại sao phải bận tâm, dù sao?

Nhưng trước tiên, tại sao bạn nên bận tâm đến việc tiết kiệm RAM? Có thực sự có lý do gì để lưu bộ nhớ ngoài việc tránh các lỗi/sự cố ngoài bộ nhớ đã nói ở trên không?

Một lý do đơn giản là tiền. Tài nguyên - cả CPU và RAM - chi phí tiền, tại sao phải lãng phí bộ nhớ bằng cách chạy các ứng dụng không hiệu quả, nếu có cách để giảm dấu chân bộ nhớ?

Một lý do khác là khái niệm rằng dữ liệu của người Viking có khối lượng lớn, nếu có rất nhiều điều đó, thì nó sẽ di chuyển từ từ. Nếu dữ liệu phải được lưu trữ trên đĩa thay vì trong RAM hoặc bộ nhớ cache nhanh, thì sẽ mất một thời gian để tải và xử lý, ảnh hưởng đến hiệu suất tổng thể. Do đó, tối ưu hóa việc sử dụng bộ nhớ có thể có tác dụng phụ tốt của việc tăng tốc độ chạy ứng dụng.

Cuối cùng, trong một số trường hợp, hiệu suất có thể được cải thiện bằng cách thêm nhiều bộ nhớ hơn (nếu hiệu suất ứng dụng bị ràng buộc trong bộ nhớ), nhưng bạn có thể làm điều đó nếu bạn không còn bộ nhớ nào trên máy.

Tìm tắc nghẽn

Nó rõ ràng rằng có những lý do chính đáng để giảm việc sử dụng bộ nhớ của các ứng dụng Python của chúng tôi, trước khi chúng tôi làm điều đó, trước tiên chúng tôi cần tìm các nút thắt hoặc các phần của mã đang gây ra tất cả bộ nhớ.

Công cụ đầu tiên chúng tôi sẽ giới thiệu là memory_profiler. Công cụ này đo lường sử dụng bộ nhớ của chức năng cụ thể trên cơ sở từng dòng:

Để bắt đầu sử dụng nó, chúng tôi cài đặt nó với pip cùng với gói psutil giúp cải thiện đáng kể hiệu suất của Profiler. Thêm vào đó, chúng ta cũng cần đánh dấu chức năng mà chúng ta muốn điểm chuẩn với @profile trang trí. Cuối cùng, chúng tôi chạy hồ sơ chống lại mã của chúng tôi bằng cách sử dụng python -m memory_profiler. Điều này cho thấy việc sử dụng bộ nhớ/phân bổ trên cơ sở từng dòng cho hàm được trang trí - trong trường hợp này là memory_intensive - cố tình tạo và xóa danh sách lớn.

Bây giờ chúng tôi đã biết cách thu hẹp trọng tâm của chúng tôi và tìm các dòng cụ thể làm tăng mức tiêu thụ bộ nhớ, chúng tôi có thể muốn đào sâu hơn một chút và xem mỗi biến đang sử dụng bao nhiêu. Bạn có thể đã thấy sys.getsizeof được sử dụng để đo lường điều này trước đây. Tuy nhiên, chức năng này sẽ cung cấp cho bạn thông tin nghi vấn cho một số loại cấu trúc dữ liệu. Đối với các số nguyên hoặc bytearrays, bạn sẽ có được kích thước thực sự trong byte, đối với các thùng chứa như danh sách mặc dù, bạn sẽ chỉ nhận được kích thước của chính container chứ không phải nội dung của nó:

Chúng ta có thể thấy rằng với các số nguyên đơn giản, mỗi khi chúng ta vượt qua ngưỡng, 4 byte được thêm vào kích thước. Tương tự, với các chuỗi đơn giản, mỗi khi chúng ta thêm một nhân vật khác được thêm vào byte. Tuy nhiên, với các danh sách, điều này không giữ được - sys.getsizeof không "đi bộ" cấu trúc dữ liệu và chỉ trả về kích thước của đối tượng cha, trong trường hợp này là list.

Cách tiếp cận tốt hơn là sử dụng công cụ cụ thể được thiết kế để phân tích hành vi bộ nhớ. Một công cụ như vậy là, có thể giúp bạn có ý tưởng thực tế hơn về kích thước đối tượng Python:

Pympler cung cấp mô -đun asizeof có chức năng cùng tên trong đó báo cáo chính xác kích thước của danh sách cũng như tất cả các giá trị mà nó chứa. Ngoài ra, mô -đun này cũng có hàm pip0, có thể cung cấp cho chúng ta sự phân chia kích thước hơn nữa của các thành phần riêng lẻ của đối tượng.

Pympler có nhiều tính năng hơn, bao gồm cả các trường hợp theo dõi hoặc xác định rò rỉ bộ nhớ. Trong trường hợp đây là một cái gì đó có thể cần thiết cho ứng dụng của bạn, thì tôi khuyên bạn nên kiểm tra các hướng dẫn có sẵn trong tài liệu.

Tiết kiệm một số ram

Bây giờ chúng ta đã biết cách tìm kiếm tất cả các loại vấn đề bộ nhớ tiềm năng, chúng ta cần tìm cách khắc phục chúng. Có khả năng, giải pháp nhanh nhất và dễ nhất có thể chuyển sang các cấu trúc dữ liệu hiệu quả hơn.

Python pip1 là một trong những tùy chọn đói bộ nhớ hơn khi lưu trữ các mảng giá trị:

Hàm đơn giản ở trên (pip2) tạo ra một python list các số bằng cách sử dụng pip4 được chỉ định. Để đo lường số lượng bộ nhớ mà chúng ta có thể sử dụng memory_profiler được hiển thị trước đó cho chúng ta lượng bộ nhớ được sử dụng trong khoảng 0,2 giây trong quá trình thực hiện chức năng. Chúng ta có thể thấy rằng việc tạo list của 10 triệu số đòi hỏi hơn 350 triệu bộ nhớ. Chà, điều đó có vẻ như rất nhiều cho một loạt các con số. Chúng ta có thể làm tốt hơn không?

Trong ví dụ này, chúng tôi đã sử dụng mô -đun Python từ pip7, có thể lưu trữ các nguyên thủy, chẳng hạn như số nguyên hoặc ký tự. Chúng ta có thể thấy rằng trong trường hợp này, việc sử dụng bộ nhớ đạt đỉnh chỉ hơn 100mib. Đó là một sự khác biệt rất lớn so với list. Bạn có thể giảm thêm việc sử dụng bộ nhớ bằng cách chọn độ chính xác phù hợp:

Một nhược điểm chính của việc sử dụng pip7 làm thùng chứa dữ liệu là nó không hỗ trợ nhiều loại đó.

Nếu bạn có kế hoạch thực hiện nhiều hoạt động toán học trên dữ liệu, thì bạn có thể tốt hơn nên sử dụng các mảng numpy thay thế: thay vào đó:

Chúng ta có thể thấy rằng các mảng numpy cũng hoạt động khá tốt khi sử dụng bộ nhớ với kích thước mảng cực đại là ~ 123mib. Đó là một chút so với pip7 nhưng với Numpy, bạn có thể tận dụng các chức năng toán học nhanh cũng như các loại không được hỗ trợ bởi pip7 như các số phức.

Các tối ưu hóa ở trên giúp với kích thước tổng thể của các mảng giá trị, nhưng chúng ta cũng có thể thực hiện một số cải tiến cho kích thước của các đối tượng riêng lẻ được xác định bởi các lớp Python. Điều này có thể được thực hiện bằng cách sử dụng thuộc tính lớp psutil2 được sử dụng để khai báo rõ ràng các thuộc tính lớp. Tuyên bố psutil2 trên một lớp cũng có tác dụng phụ tốt đẹp từ chối tạo các thuộc tính psutil4 và psutil5:

Ở đây chúng ta có thể thấy phiên bản lớp psutil6 thực sự nhỏ hơn bao nhiêu. Sự vắng mặt của psutil4 sẽ loại bỏ toàn bộ 104 byte khỏi mỗi trường hợp có thể tiết kiệm lượng bộ nhớ khổng lồ khi khởi tạo hàng triệu giá trị.

Các mẹo và thủ thuật trên sẽ hữu ích trong việc xử lý các giá trị số cũng như các đối tượng psutil8. Những gì về chuỗi, mặc dù? Cách bạn nên lưu trữ những người nói chung phụ thuộc vào những gì bạn dự định làm với chúng. Nếu bạn sẽ tìm kiếm thông qua số lượng lớn các giá trị chuỗi, thì như chúng ta đã thấy - sử dụng list là ý tưởng rất tồi. @profile0 có thể phù hợp hơn một chút nếu tốc độ thực thi là quan trọng, nhưng có thể sẽ tiêu thụ nhiều RAM hơn. Tùy chọn tốt nhất có thể là sử dụng cấu trúc dữ liệu được tối ưu hóa như Trie, đặc biệt là cho các bộ dữ liệu tĩnh mà bạn sử dụng ví dụ để truy vấn. Như thường thấy với Python, đã có một thư viện cho điều đó, cũng như cho nhiều cấu trúc dữ liệu giống như cây khác, một số trong đó bạn sẽ tìm thấy tại https://github.com/pytries.

Hoàn toàn không sử dụng RAM

Cách dễ nhất để lưu RAM là không sử dụng nó ở vị trí đầu tiên. Bạn rõ ràng có thể tránh sử dụng RAM hoàn toàn, nhưng bạn có thể tránh tải toàn bộ dữ liệu cùng một lúc và thay vào đó làm việc với dữ liệu tăng dần khi có thể. Cách đơn giản nhất để đạt được điều này là bằng cách sử dụng các trình tạo trả về một trình lặp lười biếng, tính toán các yếu tố theo yêu cầu thay vì tất cả cùng một lúc.

Công cụ mạnh hơn mà bạn có thể tận dụng các tệp được ánh xạ bộ nhớ, cho phép chúng tôi chỉ tải các phần dữ liệu từ một tệp. Thư viện tiêu chuẩn Python sườn cung cấp mô-đun @profile1 cho việc này, có thể được sử dụng để tạo các tệp được ánh xạ bộ nhớ hoạt động giống như các tệp và bytearrays. Bạn có thể sử dụng cả hai với các hoạt động tệp như @profile2, @profile3 hoặc @profile4 cũng như các hoạt động chuỗi:

Tải/đọc tệp ánh xạ bộ nhớ rất đơn giản. Trước tiên chúng tôi mở tập tin để đọc như chúng tôi thường làm. Sau đó, chúng tôi sử dụng bộ mô tả tệp tệp (@profile5) để tạo tệp ánh xạ bộ nhớ từ nó. Từ đó, chúng tôi có thể truy cập dữ liệu của mình cả với các hoạt động tệp như @profile2 hoặc các hoạt động chuỗi như cắt.

Hầu hết thời gian, bạn có thể sẽ quan tâm nhiều hơn khi đọc thêm tệp như được hiển thị ở trên, nhưng nó cũng có thể ghi vào tệp ánh xạ bộ nhớ:

Sự khác biệt đầu tiên trong mã mà bạn sẽ nhận thấy là sự thay đổi trong chế độ truy cập thành @profile7, biểu thị cả đọc và viết. Để cho thấy rằng chúng tôi thực sự có thể thực hiện cả các hoạt động đọc và viết, trước tiên chúng tôi đọc từ tệp và sau đó sử dụng Regex để tìm kiếm tất cả các từ bắt đầu bằng chữ viết hoa. Sau đó, chúng tôi chứng minh xóa dữ liệu từ tệp. Điều này không đơn giản như đọc và tìm kiếm, bởi vì chúng ta cần điều chỉnh kích thước của tệp khi chúng ta xóa một số nội dung của nó. Để làm như vậy, chúng tôi sử dụng phương thức @profile8 của mô -đun @profile1 sao chép python -m memory_profiler0 byte dữ liệu từ INDEX python -m memory_profiler1 sang INDEX python -m memory_profiler2, trong trường hợp này chuyển sang xóa 10 byte đầu tiên.

Nếu bạn đang thực hiện các tính toán trong Numpy, thì bạn có thể thích các tính năng (tài liệu) ____ của nó phù hợp với các mảng numpy được lưu trữ trong các tệp nhị phân.

Bớt tư tưởng

Tối ưu hóa các ứng dụng là vấn đề khó khăn nói chung. Nó cũng phụ thuộc rất nhiều vào nhiệm vụ trong tay cũng như loại dữ liệu. Trong bài viết này, chúng tôi đã xem xét các cách phổ biến để tìm các vấn đề sử dụng bộ nhớ và một số tùy chọn để sửa chúng. Tuy nhiên, có nhiều cách tiếp cận khác để giảm dấu chân bộ nhớ của một ứng dụng. Điều này bao gồm độ chính xác giao dịch cho không gian lưu trữ bằng cách sử dụng các cấu trúc dữ liệu xác suất như bộ lọc Bloom hoặc hyperloglog. Một tùy chọn khác là sử dụng các cấu trúc dữ liệu giống như cây như Dawg hoặc Marissa Trie rất hiệu quả trong việc lưu trữ dữ liệu chuỗi.

Python có sử dụng nhiều bộ nhớ không?

Những con số đó có thể dễ dàng phù hợp với số nguyên 64 bit, vì vậy người ta sẽ hy vọng Python sẽ lưu trữ hàng triệu số nguyên đó không quá ~ 8MB: một triệu đối tượng 8 byte. Trên thực tế, Python sử dụng nhiều RAM hơn 35 MB để lưu trữ các số này. Tại sao? Bởi vì số nguyên python là đối tượng và các đối tượng có nhiều bộ nhớ trên đầu.Python uses more like 35MB of RAM to store these numbers. Why? Because Python integers are objects, and objects have a lot of memory overhead.

Python có hết bộ nhớ không?

MemoryError có nghĩa là trình thông dịch đã hết bộ nhớ để phân bổ cho chương trình Python của bạn. Điều này có thể là do một vấn đề trong việc thiết lập môi trường Python hoặc nó có thể là một mối quan tâm với bản thân mã đang tải quá nhiều dữ liệu cùng một lúc.. This may be due to an issue in the setup of the Python environment or it may be a concern with the code itself loading too much data at the same time.

Làm cách nào để làm cho Python sử dụng ít bộ nhớ hơn?

Tệp được sử dụng nhiều nhất là đối tượng ARR chiếm 2 khối bộ nhớ với tổng kích thước 2637 MIB ...
Sử dụng trình tải dữ liệu pytorch.....
Kiểu dữ liệu được tối ưu hóa.....
Tránh sử dụng các biến toàn cầu, thay vào đó sử dụng các đối tượng cục bộ.....
Sử dụng từ khóa năng suất.....
Các phương pháp tối ưu hóa tích hợp của Python.....
Nhập chi phí báo cáo.....
Dữ liệu khối ..

Python có sử dụng ít bộ nhớ hơn Java không?

JVM có trình biên dịch JIT tối ưu hóa mã được gọi thường xuyên.Điều này cho phép các chương trình Java cạnh tranh với các chương trình C cho một số trường hợp sử dụng.Nói chung, các chương trình Java chậm hơn 3 lần so với các đối tác C.Python: Các chương trình Python cũng có nhiều bộ nhớ hơn các chương trình C/C ++ nhưng không nhiều như Java.Python programs also take more memory than C/C++ programs but not as much as Java.