Pythonと時間とメモリの最適化

前書き

多くの場合、Pythonの実行速度は遅くなります。この理由でPythonの使用を拒否する人もいますが、時間とメモリ使用量の両方についてPythonコードを最適化する方法はいくつかあります。 





現実の問題に役立ついくつかの方法を共有したいと思います。私はwin10x64を使用しています。





Pythonでメモリを節約する

例として非常に現実的な例を見てみましょう。商品のリストがある店舗があるとします。そのため、これらの商品を扱う必要がありました。最善の選択肢は、すべての商品がデータベースに保存されているが、突然問題が発生したため、すべての商品をメモリにアンロードして処理することにしました。そして、合理的な疑問が生じます。これほど多くの商品を扱うのに十分なメモリがあるのでしょうか。





まず、ストアを担当するクラスを作成しましょう。nameとlistGoodsの2つのフィールドのみがあり、それぞれストアの名前と製品のリストを担当します。





class ShopClass:
    def __init__(self, name=""):
        self.name = name
        self.listGoods = []
      
      



次に、ストアに商品を入力します(つまり、listGoodsフィールドに入力します)。これを行うために、1つの製品に関する情報を担当するクラスを作成します(このような例ではデータクラスを使用します)。





#    dataclass,   
# pip install dataclasses
#     
# from dataclasses import dataclass 
@dataclass
class DataGoods:
    name:str
    price:int
    unit:str
      
      



. 200 3 :





shop = ShopClass("MyShop")
for _ in range(200):
    shop.listGoods.extend([
        DataGoods("", 20000, "RUB"),
        DataGoods("", 45000, "RUB"),
        DataGoods("", 2000, "RUB")
    ])
      
      



, ( pympler):





from pympler import asizeof
print(" :", asizeof.asizeof(shop))
>>>  : 106648
      
      



, 106. , , , 600 , , , . , , , , , , . ( , ).





. Python , , . , python __dict__ , . , .





shop = ShopClass("MyShop")
print(shop.__dict__)  
>>> {'name': 'MyShop', 'listGoods': []}

shop.city = ""
print(shop.__dict__) 
>>> {'name': 'MyShop', 'listGoods': [], 'city': ''}
      
      



. , . python’e __slots__, __dict__. __dict__ , , . :





class ShopClass:
    __slots__ = ("name", "listGoods")
    def __init__(self, name=""):
        self.name = name
        self.listGoods = []
@dataclass
class DataGoods:
    __slots__ = ("name", "price", "unit")
    name:str
    price:int
    unit:str
      
      



.





from pympler import asizeof
print(" :", asizeof.asizeof(shop))
>>>  : 43904
      
      



, , , 2.4 ( ,  Python, ). , . , , , :





shop = ShopClass("MyShop")
shop.city = ""
>>> AttributeError: 'ShopClass' object has no attribute 'city'
      
      



, - , __dict__ ptyhon' , . timeit, __slots__ (__dict__):





import timeit
code = """
class ShopClass:
    #__slots__ = ("name", "listGoods")
    def __init__(self, name=""):
        self.name = name
        self.listGoods = []
@dataclass
class DataGoods:
    #__slots__ = ("name", "price", "unit")
    name:str
    price:int
    unit:str
shop = ShopClass("MyShop")
for _ in range(200):
    shop.listGoods.extend([
        DataGoods("", 20000, "RUB"),
        DataGoods("", 45000, "RUB"),
        DataGoods("", 2000, "RUB")
])
"""
print(timeit.timeit(code, number=60000))
>>> 33.4812513
      
      



__slots__ (#__slots__ = ("name", "price", "unit") -> __slots__ = ("name", "price", "unit") # __slots__ = ("name", "listGoods") -> __slots__ = ("name", "listGoods")):





#  __slots__   
print(timeit.timeit(code, number=60000))
>>> 28.535005599999998
      
      



, 15% ( , ).





, , , .





python , (, ), C/C++ .





, .





Cython

Cython , Python, . , Python , ( 20.000.000 ):





import time
class ShopClass:
   __slots__ = ("name", "listGoods")
   def __init__(self, name=""):
      self.name = name
      self.listGoods = []
@dataclass
class DataGoods:
   __slots__ = ("name", "price", "unit")
   name: str
   price: int
   unit: str
shop = ShopClass("MyShop")
t = time.time()
for _ in range(200*100000):
   shop.listGoods.extend([
      DataGoods("", 20000, "RUB"),
      DataGoods("", 45000, "RUB"),
      DataGoods("", 2000, "RUB")
   ])
print("   PYTHON:", time.time()-t)
>>>    PYTHON: 44.49887752532959
telephoneSum, televizorSum, tosterSum = 0, 0, 0
t = time.time()
for goods in shop.listGoods:
   if goods.name == "":
      telephoneSum += goods.price
   elif goods.name == "":
      televizorSum += goods.price
   elif goods.name == "":
      tosterSum += goods.price
print("    PYTHON:", time.time() - t)
>>>     PYTHON: 13.135360717773438
      
      



, . cython. cython_npm (. ): pip install cython-npm. , cython_code cython_data.pyx ( cython .pyx).





cython:





cdef class CythonShopClass:
   cdef str name
   cdef list listGoods

   def __init__(self, str name):
       self.name = name
       self.listGoods = []
      
      



cython , ( , , ). cdef < > < > . cython. my_def() cdef, def, python . .pyx (# cython: language_level=3).





# cython: language_level=3
#      
cdef class CythonDataGoods:
   cdef str name
   cdef int price
   cdef str unit
   def __init__(self, str name, int price, str unit):
       self.name = name
       self.price = price
       self.unit = unit
cdef int c_testFunc():
    cdef CythonShopClass shop
    cdef CythonDataGoods goods
    cdef int i, t, telephoneSum, televizorSum, tosterSum
    size, i, telephoneSum, televizorSum, tosterSum = 0, 0, 0, 0, 0
    shop = CythonShopClass("MyShop")
    t = time.time()
    for i in range(200*100000):
       shop.listGoods.extend([
           CythonDataGoods("", 20000, "RUB"),
           CythonDataGoods("", 45000, "RUB"),
           CythonDataGoods("", 2000, "RUB")
       ])
    print("   CYTHON:", time.time()-t)
    t = time.time()
    for goods in shop.listGoods:
        if goods.name == "":
            telephoneSum += goods.price
        elif goods.name == "":
            televizorSum += goods.price
        elif goods.name == "":
            tosterSum += goods.price
    print("    CYTHON:", time.time() - t)
    return 0
def my_def():
    data = c_testFunc()
    return data
      
      



main.py cython . :





from cython_npm.cythoncompile import export
from cython_npm.cythoncompile import install
import time
      
      



cython python





export('cython_code/cython_data.pyx')
import cython_code.cython_data as cython_data
      
      



cython





if __name__ == "__main__":
   a = cython_data.my_def()
      
      



. , . cython, , :





>>>    CYTHON: 4.082242012023926
      
      



:





>>>     CYTHON: 1.0513946056365967
      
      



, 44 4 , 11 . 13 1 , 13 .





, cython - , , , . , , cython 100 .





Python

, - , . , , . , , . :





shop = ShopClass("MyShop")
t = time.time()
getGoods = lambda index: {0: ("", 20000, "RUB"), 
                          1: ("", 45000, "RUB"), 
                          2:("", 2000, "RUB")}.get(index) 
shop.listGoods = [DataGoods(*getGoods(i%3)) for i in range(200*100000)]
print("   PYTHON:", time.time()-t)
>>>     PYTHON: 19.719463109970093
      
      



2 , python. python - , , .





PyPy

, cython , ( ), . , . PyPy, python, JIT . PyPy , , . python PyPy . 





PyPy . , cmd , pypy3.exe, . cmd :





, 19 python’ 4.5 , 4 .





. , , python , .





. Numba, NumPy, Nim multiprocessing. , . , python .





コードを最適化するための機能の選択に進む前に、純粋なPythonでコードの内部最適化を実行し、ループ内のループ内のループを最大限に取り除き、手でメモリをクリーンアップする必要があります。コード実行中に不要な要素を削除します。コードを別の言語に書き直すことですべての問題が解決することを期待しないでください。コードのボトルネックを探し、アルゴリズムで最適化するか、言語自体のトリックを使用することを学びます。








All Articles