Hướng dẫn does python use more memory? - python có sử dụng nhiều bộ nhớ hơn không?

Python có momeoryerror là giới hạn của hệ thống RAM của bạn mà bạn đã xác định thủ công với gói resource.

Xác định lớp của bạn với các vị trí làm cho trình thông dịch Python biết rằng các thuộc tính/thành viên trong lớp của bạn đã được sửa. Và có thể dẫn đến tiết kiệm bộ nhớ đáng kể!

Bạn có thể giảm sự sáng tạo của Dict bằng phiên dịch Python bằng cách sử dụng __slot__. Điều này sẽ nói với thông dịch viên không tạo ra dict trong nội bộ và tái sử dụng cùng một biến.

Nếu bộ nhớ được tiêu thụ bởi các quá trình Python của bạn sẽ tiếp tục phát triển theo thời gian. Đây dường như là sự kết hợp của:

  • Làm thế nào bộ phân bổ bộ nhớ C trong Python hoạt động. Đây về cơ bản là phân mảnh bộ nhớ, bởi vì việc phân bổ không thể gọi ‘miễn phí trừ khi toàn bộ khối bộ nhớ không được sử dụng. Nhưng việc sử dụng bộ nhớ thường không hoàn toàn phù hợp với các đối tượng mà bạn đang tạo và sử dụng.
  • Sử dụng một số chuỗi nhỏ để so sánh dữ liệu. Một quy trình được gọi là Interning được sử dụng nội bộ nhưng tạo ra nhiều chuỗi nhỏ mang lại tải cho trình thông dịch.

Cách tốt nhất là tạo luồng công nhân hoặc nhóm chủ đề đơn để thực hiện công việc của bạn và làm mất hiệu lực công nhân/giết để giải phóng các tài nguyên được đính kèm/sử dụng trong luồng công nhân.

Mã dưới đây tạo ra nhân viên chủ đề đơn:

__slot__ = ('dna1','dna2','lock','errorResultMap')
lock = threading.Lock()
errorResultMap = []
def process_dna_compare(dna1, dna2):
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        futures = {executor.submit(getDnaDict, lock, dna_key): dna_key for dna_key in dna1}
    '''max_workers=1 will create single threadpool'''
    dna_differences_map={}
    count = 0
    dna_processed = False;
    for future in concurrent.futures.as_completed(futures):
        result_dict = future.result()
        if result_dict :
            count += 1
            '''Do your processing XYZ here'''
    logger.info('Total dna keys processed ' + str(count))

def getDnaDict(lock,dna_key):
    '''process dna_key here and return item'''
    try:
        dataItem = item[0]
        return dataItem
    except:
        lock.acquire()
        errorResultMap.append({'dna_key_1': '', 'dna_key_2': dna_key_2, 'dna_key_3': dna_key_3,
                          'dna_key_4': 'No data for dna found'})
        lock.release()
        logger.error('Error in processing dna :'+ dna_key)
    pass

if __name__ == "__main__":
    dna1 = '''get data for dna1'''
    dna2 = '''get data for dna2'''
    process_dna_compare(dna1,dna2)
    if errorResultMap != []:
       ''' print or write to file the errorResultMap'''

Mã dưới đây sẽ giúp bạn hiểu cách sử dụng bộ nhớ:

import objgraph
import random
import inspect

class Dna(object):
    def __init__(self):
        self.val = None
    def __str__(self):
        return "dna – val: {0}".format(self.val)

def f():
    l = []
    for i in range(3):
        dna = Dna()
        #print “id of dna: {0}”.format(id(dna))
        #print “dna is: {0}”.format(dna)
        l.append(dna)
    return l

def main():
    d = {}
    l = f()
    d['k'] = l
    print("list l has {0} objects of type Dna()".format(len(l)))
    objgraph.show_most_common_types()
    objgraph.show_backrefs(random.choice(objgraph.by_type('Dna')),
    filename="dna_refs.png")

    objgraph.show_refs(d, filename='myDna-image.png')

if __name__ == "__main__":
    main()

Đầu ra để sử dụng bộ nhớ:

list l has 3 objects of type Dna()
function                   2021
wrapper_descriptor         1072
dict                       998
method_descriptor          778
builtin_function_or_method 759
tuple                      667
weakref                    577
getset_descriptor          396
member_descriptor          296
type                       180

Đọc thêm về các khe vui

Hãy nói rằng bạn muốn lưu trữ một danh sách các số nguyên trong Python:

list_of_numbers = []
for i in range(1000000):
    list_of_numbers.append(i)

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.

Hãy cùng xem những gì mà xảy ra dưới mui xe, và sau đó sử dụng Numpy có thể loại bỏ chi phí này.

Đo lường sử dụng bộ nhớ

Nếu chúng tôi lập hồ sơ đoạn mã ở trên, thì đây là phân bổ được sử dụng:

Như bạn có thể thấy nếu bạn di chuột hoặc nhấp vào khung hình:

  • ~ 8MB được phân bổ cho danh sách.
  • ~ 28MB được phân bổ cho các số nguyên.

Lưu ý bên cạnh: Bạn sẽ nhận được cùng một cách sử dụng bộ nhớ nếu bạn đã làm list(range(1000000)), nhưng tôi đã cấu trúc mã theo cách này để làm cho nó rõ ràng hơn nơi mỗi phần sử dụng bộ nhớ xuất hiện. You would get the same memory usage if you did list(range(1000000)), but I structured the code this way to make it clearer where each chunk of memory usage came form.

Danh sách lấy nhiều bộ nhớ đó là một danh sách Python đáng ngạc nhiên về cơ bản là một loạt các gợi ý cho các đối tượng Python tùy ý. Danh sách của chúng tôi có một triệu mục, con trỏ trên các máy 64 bit hiện đại mất 8 byte, vì vậy chúng tôi đã quay trở lại RAM 8MB.

Nhưng tại sao các số nguyên tự lấy 28MB?

Điều gì làm cho một số nguyên trong Python

Chúng ta có thể đo mức sử dụng bộ nhớ của các đối tượng Python trong byte bằng cách sử dụng

import objgraph
import random
import inspect

class Dna(object):
    def __init__(self):
        self.val = None
    def __str__(self):
        return "dna – val: {0}".format(self.val)

def f():
    l = []
    for i in range(3):
        dna = Dna()
        #print “id of dna: {0}”.format(id(dna))
        #print “dna is: {0}”.format(dna)
        l.append(dna)
    return l

def main():
    d = {}
    l = f()
    d['k'] = l
    print("list l has {0} objects of type Dna()".format(len(l)))
    objgraph.show_most_common_types()
    objgraph.show_backrefs(random.choice(objgraph.by_type('Dna')),
    filename="dna_refs.png")

    objgraph.show_refs(d, filename='myDna-image.png')

if __name__ == "__main__":
    main()
0:

>>> import sys
>>> sys.getsizeof(123)
28

Vì một số nguyên nhỏ sử dụng 28 byte, bây giờ chúng ta đã biết lý do tại sao một triệu số nguyên mất 28 MB RAM. Nhưng tại sao các số nguyên Python lại mất nhiều trí nhớ?

Mỗi đối tượng trong triển khai Python mặc định, CPython, cũng là một cấu trúc

import objgraph
import random
import inspect

class Dna(object):
    def __init__(self):
        self.val = None
    def __str__(self):
        return "dna – val: {0}".format(self.val)

def f():
    l = []
    for i in range(3):
        dna = Dna()
        #print “id of dna: {0}”.format(id(dna))
        #print “dna is: {0}”.format(dna)
        l.append(dna)
    return l

def main():
    d = {}
    l = f()
    d['k'] = l
    print("list l has {0} objects of type Dna()".format(len(l)))
    objgraph.show_most_common_types()
    objgraph.show_backrefs(random.choice(objgraph.by_type('Dna')),
    filename="dna_refs.png")

    objgraph.show_refs(d, filename='myDna-image.png')

if __name__ == "__main__":
    main()
1 C hoặc một trong các biến thể của nó. Ở đây, những gì
import objgraph
import random
import inspect

class Dna(object):
    def __init__(self):
        self.val = None
    def __str__(self):
        return "dna – val: {0}".format(self.val)

def f():
    l = []
    for i in range(3):
        dna = Dna()
        #print “id of dna: {0}”.format(id(dna))
        #print “dna is: {0}”.format(dna)
        l.append(dna)
    return l

def main():
    d = {}
    l = f()
    d['k'] = l
    print("list l has {0} objects of type Dna()".format(len(l)))
    objgraph.show_most_common_types()
    objgraph.show_backrefs(random.choice(objgraph.by_type('Dna')),
    filename="dna_refs.png")

    objgraph.show_refs(d, filename='myDna-image.png')

if __name__ == "__main__":
    main()
1 trông giống như:

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;

Vì vậy, bất kỳ đối tượng Python nào cũng bao gồm ở mức tối thiểu:

  • Một số lượng tham chiếu, theo dõi số lượng tài liệu tham khảo có đối tượng.
  • Một con trỏ đến loại, trong trường hợp của chúng tôi loại số nguyên.
  • Bất cứ dữ liệu nào là cần thiết để lưu trữ đối tượng.

Trên hệ điều hành 64 bit, mặc định ngày nay, hai mục đầu tiên có nghĩa là tối thiểu 16 byte chi phí cho mỗi đối tượng.

Ngay cả khi bạn có thể tìm thấy dữ liệu của mình thành một byte, điều đó không quan trọng, bạn vẫn phải chịu đựng chi phí đó.

Chuyển sang Numpy

Để tiết kiệm cho bạn chi phí đó, các mảng numpy đang lưu trữ các số don don lưu trữ tham chiếu đến các đối tượng Python, giống như một danh sách Python bình thường. Thay vào đó, numpy mảng chỉ lưu trữ các con số.

Điều đó có nghĩa là bạn không phải trả chi phí 16+ byte đó cho mỗi số trong mảng.

Ví dụ: nếu chúng ta lập cấu hình cách sử dụng bộ nhớ cho đoạn mã này:

import numpy as np

arr = np.zeros((1000000,), dtype=np.uint64)
for i in range(1000000):
    arr[i] = i

Chúng ta có thể thấy rằng việc sử dụng bộ nhớ để tạo mảng chỉ là 8MB, như chúng ta mong đợi, cộng với chi phí bộ nhớ của việc nhập Numpy:

Lưu ý bên lề: Đây không phải là cách bạn sẽ viết mã không thành ngữ hoặc hiệu quả, tôi chỉ cấu trúc mã theo cách này cho các mục đích giáo dục. This isn’t how you would write idiomatic or efficient NumPy code, I’m just structuring the code this way for educational purposes.

Numpy để giải cứu

Đi từ 8MB đến 35MB có lẽ là thứ bạn có thể sống cùng, nhưng đi từ 8GB đến 35GB có thể sử dụng bộ nhớ quá nhiều. Vì vậy, trong khi rất nhiều lợi ích của việc sử dụng Numpy là các cải tiến hiệu suất CPU mà bạn có thể có được cho các hoạt động số, một lý do khác mà nó rất hữu ích là chi phí bộ nhớ giảm.

Nếu bạn xử lý các danh sách lớn các số trong bộ nhớ, hãy đảm bảo rằng bạn đang sử dụng các mảng Numpy. Và nếu việc sử dụng bộ nhớ vẫn còn quá cao, bạn có thể bắt đầu xem xét các cách giảm sử dụng bộ nhớ nhiều hơn, như nén trong bộ nhớ.

Python có thể sử dụng bao nhiêu bộ nhớ?

Python có bộ phân bổ pymalloc được tối ưu hóa cho các đối tượng nhỏ (nhỏ hơn hoặc bằng 512 byte) với tuổi thọ ngắn.Nó sử dụng các ánh xạ bộ nhớ được gọi là đấu trường trên mạng với kích thước cố định là 256 kib.256 KiB.

Làm cách nào để giảm sử dụng bộ nhớ trăn?

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 ..

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

Ngoài ra, ngoài thực tế là mỗi phần tử trong danh sách Python là một pyobject * trỏ đến đối tượng thực tế;Mỗi con trỏ này là 64 bit trên các bản dựng Python 64 bit;Điều đó có nghĩa là mỗi tham chiếu phần tử danh sách một mình tiêu thụ bộ nhớ gấp đôi so với c int.each list element reference alone consumes twice as much memory as a C int .