成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

沒(méi)有什么內(nèi)存問(wèn)題,是一行Python代碼解決不了的

開(kāi)發(fā) 后端
我們的項(xiàng)目需要存儲(chǔ)和處理一個(gè)相當(dāng)大的動(dòng)態(tài)列表,測(cè)試人員經(jīng)常向我抱怨內(nèi)存不足。但是最終,我們通過(guò)添加一行簡(jiǎn)單的代碼解決了這個(gè)問(wèn)題。

內(nèi)存不足是項(xiàng)目開(kāi)發(fā)過(guò)程中經(jīng)常碰到的問(wèn)題,我和我的團(tuán)隊(duì)在之前的一個(gè)項(xiàng)目中也遇到了這個(gè)問(wèn)題,我們的項(xiàng)目需要存儲(chǔ)和處理一個(gè)相當(dāng)大的動(dòng)態(tài)列表,測(cè)試人員經(jīng)常向我抱怨內(nèi)存不足。但是最終,我們通過(guò)添加一行簡(jiǎn)單的代碼解決了這個(gè)問(wèn)題。

結(jié)果如圖所示:

我將在下面解釋它的工作原理。

舉一個(gè)簡(jiǎn)單的“learning”示例 - 創(chuàng)建一個(gè)DataItem類,在其中定義一些個(gè)人信息屬性,例如姓名,年齡和地址。 

  1. class DataItem(object):  
  2.    def __init__(self, name, age, address):  
  3.        self.name = name  
  4.        self.age = age  
  5.        self.address = address 

小測(cè)試——這樣一個(gè)對(duì)象會(huì)占用多少內(nèi)存?

首先讓我們嘗試下面這種測(cè)試方案: 

  1. d1 = DataItem("Alex", 42, "-")  
  2. print ("sys.getsizeof(d1):", sys.getsizeof(d1)) 

答案是56字節(jié)。看起來(lái)比較小,結(jié)果令人滿意。

但是,讓我們檢查另一個(gè)數(shù)據(jù)多一些的對(duì)象: 

  1. d2 = DataItem("Boris", 24, "In the middle of nowhere")  
  2. print ("sys.getsizeof(d2):", sys.getsizeof(d2)) 

答案仍然是56。這讓我們明白這個(gè)結(jié)果并不完全正確。

我們的直覺(jué)是對(duì)的,這個(gè)問(wèn)題不是那么簡(jiǎn)單。Python是一種非常靈活的語(yǔ)言,具有動(dòng)態(tài)類型,它在工作時(shí)存儲(chǔ)了許多額外的數(shù)據(jù)。這些額外的數(shù)據(jù)本身就占了很多內(nèi)存。

例如,sys.getsizeof(“ ”)返回33,沒(méi)錯(cuò),每個(gè)空行就多達(dá)33字節(jié)!并且sys.getsizeof(1)將為此數(shù)字返回24-24個(gè)字節(jié)(我建議C程序員們現(xiàn)在點(diǎn)擊結(jié)束閱讀,以免對(duì)Python的美麗失去信心)。

對(duì)于更復(fù)雜的元素,例如字典,sys.getsizeof(dict())返回272個(gè)字節(jié),這還只是一個(gè)空字典。舉例到此為止,但事實(shí)已經(jīng)很清楚了,何況RAM的制造商也需要出售他們的芯片。

現(xiàn)在,讓我們回到回到我們的DataItem類和“小測(cè)試”問(wèn)題。

這個(gè)類到底占多少內(nèi)存?

首先,我們將以較低級(jí)別輸出該類的全部?jī)?nèi)容: 

  1. def dump(obj):  
  2.     for attr in dir(obj):  
  3.         print("  obj.%s = %r" % (attr, getattr(obj, attr))) 

這個(gè)函數(shù)將顯示隱藏在“隱身衣”下的內(nèi)容,以便所有Python函數(shù)(類型,繼承和其他包)都可以運(yùn)行。

結(jié)果令人印象深刻:

它總共占用多少內(nèi)存呢?

在GitHub上,有一個(gè)函數(shù)可以計(jì)算實(shí)際大小,通過(guò)遞歸調(diào)用所有對(duì)象的getsizeof實(shí)現(xiàn)。 

  1. def get_size(obj, seen=None):  
  2.    # From https://goshippo.com/blog/measure-real-size-any-python-object/  
  3.    # Recursively finds size of objects  
  4.    size = sys.getsizeof(obj)  
  5.    if seen is None:  
  6.        seen = set()  
  7.    obj_id = id(obj) 
  8.     if obj_id in seen:  
  9.        return 0  
  10. # Important mark as seen *before* entering recursion to gracefully handle  
  11.    # self-referential objects  
  12.    seen.add(obj_id)  
  13.    if isinstance(obj, dict):  
  14.      size += sum([get_size(v, seen) for v in obj.values()])  
  15.      size += sum([get_size(k, seen) for k in obj.keys()])  
  16.    elif hasattr(obj, '__dict__'):  
  17.      size += get_size(obj.__dict__, seen)  
  18.    elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):  
  19.      size += sum([get_size(i, seen) for i in obj]) 
  20.     return size  
  21. #讓我們?cè)囈幌拢?nbsp;
  22. d1 = DataItem("Alex", 42, "-")  
  23. print ("get_size(d1):", get_size(d1)) 
  24. d2 = DataItem("Boris", 24, "In the middle of nowhere")  
  25. print ("get_size(d2):", get_size(d2)) 

我們分別得到460和484字節(jié),這似乎更接近事實(shí)。

使用這個(gè)函數(shù),我們可以進(jìn)行一系列實(shí)驗(yàn)。例如,我想知道如果DataItem放在列表中,數(shù)據(jù)將占用多少空間。

get_size([d1])函數(shù)返回532個(gè)字節(jié),顯然,這些是“原本的”460+一些額外開(kāi)銷。但是get_size([d1,d2])返回863個(gè)字節(jié)—小于460+484。get_size([d1,d2,d1])的結(jié)果更加有趣,它產(chǎn)生了871個(gè)字節(jié),只是稍微多了一點(diǎn),這說(shuō)明Python很聰明,不會(huì)再為同一個(gè)對(duì)象分配內(nèi)存。

現(xiàn)在我們來(lái)看問(wèn)題的第二部分。

是否有可能減少內(nèi)存消耗?

答案是肯定的。Python是一個(gè)解釋器,我們可以隨時(shí)擴(kuò)展我們的類,例如,添加一個(gè)新字段: 

  1. d1 = DataItem("Alex", 42, "-")  
  2. print ("get_size(d1):", get_size(d1))  
  3. d1.weight = 66  
  4. print ("get_size(d1):", get_size(d1)) 

這是一個(gè)很棒的特點(diǎn),但是如果我們不需要這個(gè)功能,我們可以強(qiáng)制解釋器使用__slots__指令來(lái)指定類屬性列表: 

  1. class DataItem(object):  
  2.    __slots__ = ['name', 'age', 'address']  
  3.    def __init__(self, name, age, address):  
  4.        self.name = name  
  5.        self.age = age  
  6.        self.address = address 

更多信息可以參考文檔中的“__dict__和__weakref__的部分。使用__dict__所節(jié)省的空間可能會(huì)很大”。

我們嘗試后發(fā)現(xiàn):get_size(d1)返回的是64字節(jié),對(duì)比460直接,減少約7倍。作為獎(jiǎng)勵(lì),對(duì)象的創(chuàng)建速度提高了約20%(請(qǐng)參閱文章的第一個(gè)屏幕截圖)。

真正使用如此大的內(nèi)存增益不會(huì)導(dǎo)致其他開(kāi)銷成本。只需添加元素即可創(chuàng)建100,000個(gè)數(shù)組,并查看內(nèi)存消耗: 

  1. data = []  
  2. for p in range(100000):  
  3.    data.append(DataItem("Alex", 42, "middle of nowhere"))  
  4. snapshot = tracemalloc.take_snapshot()  
  5. top_stats = snapshot.statistics('lineno')  
  6. total = sum(stat.size for stat in top_stats)  
  7. print("Total allocated size: %.1f MB" % (total / (1024*1024))) 

在沒(méi)有__slots__的情況結(jié)果為16.8MB,而使用__slots__時(shí)為6.9MB。當(dāng)然不是7倍,但考慮到代碼變化很小,它的表現(xiàn)依然出色。

現(xiàn)在討論一下這種方式的缺點(diǎn)。激活__slots__會(huì)禁止創(chuàng)建其他所有元素,包括__dict__,這意味著,例如,下面這種將結(jié)構(gòu)轉(zhuǎn)換為json的代碼將不起作用: 

  1. def toJSON(self):  
  2.     return json.dumps(self.__dict__) 

但這也很容易搞定,可以通過(guò)編程方式生成你的dict,遍歷循環(huán)中的所有元素: 

  1. def toJSON(self):  
  2.        data = dict()  
  3.        for var in self.__slots__:  
  4.            data[var] = getattr(self, var)  
  5.        return json.dumps(data) 

向類中動(dòng)態(tài)添加新變量也是不可能的,但在我們的項(xiàng)目里,這不是必需的。

下面是最后一個(gè)小測(cè)試。來(lái)看看整個(gè)程序需要多少內(nèi)存。在程序末尾添加一個(gè)無(wú)限循環(huán),使其持續(xù)運(yùn)行,并查看Windows任務(wù)管理器中的內(nèi)存消耗。

沒(méi)有__slots__時(shí)

69Mb變成27Mb......好吧,畢竟我們節(jié)省了內(nèi)存。對(duì)于只添加一行代碼的結(jié)果來(lái)說(shuō)已經(jīng)很好了。

注意:tracemalloc調(diào)試庫(kù)使用了大量額外的內(nèi)存。顯然,它為每個(gè)創(chuàng)建的對(duì)象添加了額外的元素。如果你將其關(guān)閉,總內(nèi)存消耗將會(huì)少得多,截圖顯示了2個(gè)選項(xiàng):

如何節(jié)省更多的內(nèi)存?

可以使用numpy庫(kù),它允許你以C風(fēng)格創(chuàng)建結(jié)構(gòu),但在這個(gè)的項(xiàng)目中,它需要更深入地改進(jìn)代碼,所以對(duì)我來(lái)說(shuō)第一種方法就足夠了。

奇怪的是,__slots__的使用從未在Habré上詳細(xì)分析過(guò),我希望這篇文章能夠填補(bǔ)這一空白。

結(jié)論

這篇文章看起來(lái)似乎是反Python的廣告,但它根本不是。Python是非常可靠的(為了“刪除”Python中的程序,你必須非常努力),這是一種易于閱讀和方便編寫(xiě)的語(yǔ)言。在許多情況下,這些優(yōu)點(diǎn)遠(yuǎn)勝過(guò)缺點(diǎn),但如果你需要性能和效率的最大化,你可以使用numpy庫(kù)像C++一樣編寫(xiě)代碼,它可以非常快速有效地處理數(shù)據(jù)。 

 

責(zé)任編輯:龐桂玉 來(lái)源: 機(jī)器學(xué)習(xí)算法與Python學(xué)習(xí)
相關(guān)推薦

2018-12-18 10:23:45

Python代碼內(nèi)存

2022-02-23 14:37:48

代碼Pythonbug

2023-04-05 20:14:32

AIChatGPT

2016-12-02 08:53:18

Python一行代碼

2021-07-22 16:23:23

亞馬遜微軟

2016-01-06 11:40:24

2019-07-31 08:30:24

物聯(lián)網(wǎng)電池能源

2022-02-24 10:40:14

Python代碼

2020-07-27 10:37:01

編程技巧開(kāi)發(fā)

2021-11-02 16:25:41

Python代碼技巧

2025-03-05 11:00:00

JavaScript跨域前端

2025-04-17 08:05:00

JavaScript

2022-04-09 09:11:33

Python

2020-08-12 14:54:00

Python代碼開(kāi)發(fā)

2020-10-13 17:30:45

Python代碼內(nèi)存

2017-04-05 11:10:23

Javascript代碼前端

2017-04-13 19:20:18

Python代碼并行任務(wù)

2020-08-19 10:30:25

代碼Python多線程

2021-09-22 09:43:47

Python 開(kāi)發(fā)編程語(yǔ)言

2021-05-28 07:39:17

SQL代碼操作
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 久久久久久久久淑女av国产精品 | 免费一级黄色录像 | 国产在线精品一区二区三区 | 欧美亚洲综合久久 | 精品乱人伦一区二区三区 | 精品一区二区三区在线观看国产 | 91视频进入 | 久久日韩粉嫩一区二区三区 | 人人干在线 | www.av7788.com| 少妇一级淫片免费放播放 | 三级黄片毛片 | 久久亚| 超碰在线播 | 日本三级网站在线观看 | 日韩精品一区二区三区在线播放 | 91av在线免费播放 | 91免费入口 | 欧美视频网 | 国产ts人妖一区二区三区 | 久久噜噜噜精品国产亚洲综合 | 久久久久国产一区二区 | 久久国产精品视频 | 色婷婷精品久久二区二区蜜臂av | 欧美一级做性受免费大片免费 | 在线视频日韩精品 | 国产伦精品一区二区三区精品视频 | av网站观看 | 成人精品一区二区 | 天天看天天操 | 亚洲一区二区三区在线免费 | 在线免费观看亚洲 | 国产精品毛片一区二区在线看 | 国产精品一区二区三区四区 | 色婷婷一区二区三区四区 | 日韩av一区在线观看 | 在线国产一区二区 | 国产一二区视频 | 中文一区 | 国产一区二区高清在线 | 日韩一区二 |