2015-05-10 62 views
5

所以,我在Python 3.4中做了一個遊戲。在遊戲中,我需要跟蹤地圖。它是從(0,0)開始並以各個方向繼續,以過濾隨機方式生成的連接房間的地圖(只有下一個位置的正確匹配用於隨機列表選擇)。python內存使用量字典和變量大型數據集

我有幾個類型的房間,其中有一個名字,和門的列表:

RoomType = namedtuple('Room','Type,EntranceLst') 
typeA = RoomType("A",["Bottom"]) 
... 

對於地圖的那一刻我保持位置的字典和房間的類型:

currentRoomType = typeA 
currentRoomPos = (0,0) 
navMap = {currentRoomPos: currentRoomType} 

我有循環產生9.000.000房間,測試內存使用情況。 當我運行它時,我獲得了大約600和800Mb。 我想知道是否有一種方法來優化。

我試着用的,而不是做

navMap = {currentRoomPos: currentRoomType} 

我會做

navMap = {currentRoomPos: "A"} 

,但這並沒有在使用一個真正的變化。

現在我想知道我是否可以 - 也應該 - 保留所有類型的列表,併爲每種類型保留它發生的位置。但我不知道它是否會與python管理變量的方式有所不同。

這幾乎是一個思想實驗,但如果有任何有用的東西來自它,我可能會實現它。

+0

我想這個職位只有2-d。對 ? –

+0

字典是散列表,因此它們提供了快速查找,代價是內存使用的大量開銷。你確定你需要一個散列表嗎?我無法判斷自己,因爲你的代碼沒有展示出你可能想用你的地圖做的所有可能的事情。 –

+0

@TasosVogiatzoglou:確實,他們是。可能我會在稍後添加梯子,但目前,這是單一的水平。 – ShadowFlame

回答

4

您可以使用sys.getsizeof(object)來獲取Python對象的大小。但是,在調用容器上的sys.getsizeof時必須小心:它只給出容器的大小,而不是內容 - 請參閱this配方,以獲取容器總大小(包括內容)的解釋。在這種情況下,我們不需要太深入:我們可以手動添加容器的大小和內容的大小。

問題的類型的尺寸爲:

# room type size 
>>> sys.getsizeof(RoomType("A",["Bottom"])) + sys.getsizeof("A") + sys.getsizeof(["Bottom"]) + sys.getsizeof("Bottom") 
233 

# position size 
>>> sys.getsizeof((0,0)) + 2*sys.getsizeof(0) 
120 

# One character size 
>>> sys.getsizeof("A") 
38 

讓我們來看看不同的選擇,假設你有N個房間:從position -> room_type

  1. 字典。這涉及將N*(size(position) + size(room_type)) = 353 N字節保存在內存中。
  2. position -> 1-character string字典。這涉及在內存中保留N*158字節。
  3. 字典從type -> set of positions。這涉及保留N*120字節加上存儲字典密鑰的小開銷。

就內存使用情況而言,第三個選項顯然更好。但是,通常情況下,您有CPU內存權衡。值得簡要思考一下你可能做的查詢的計算複雜性。爲了找到給它的位置的房間,與每個三種選擇的類型,你要不斷:

  1. 查找在字典中的位置。這是一個O(1)查找,所以你總是有相同的運行時間(近似),與房間數量無關(對於大量房間)。
  2. 相同
  3. 看看每種類型,併爲每種類型詢問該位置是否在該類型的位置集合中。這是一個O(ntypes)查找,也就是說,它花費的時間與您擁有的類型數量成正比。請注意,如果您已經選擇了列表而不是集合來存儲給定類型的房間,那麼這將會增加到O(nrooms * ntypes),這會損害您的表現。

與往常一樣,優化時,考慮優化對內存使用率和CPU時間的影響很重要。這兩者往往不一致。

作爲一種替代方案,如果地圖足夠矩形,則可以考慮將類型保留在二維numpy字符數組中。我相信這會更有效率。在一個numpy的陣列中的每個字符是一個單字節,所以存儲器使用將要少得多,並且CPU時間仍然是O(1)從室的位置查找來輸入:

# Generate random 20 x 10 rectangular map 
>>> map = np.repeat('a', 100).reshape(20, 10) 
>>> map.nbytes 
200 # ie. 1 byte per character. 

一些另外小規模優化:

將房間類型編碼爲int而不是字符串。 Ints的大小爲24字節,而一個字符的字符串的大小爲38.

將位置編碼爲單個整數而不是元組。例如:

# Random position 
xpos = 5 
ypos = 92 

# Encode the position as a single int, using high-order bits for x and low-order bits for y 
pos = 5*1000 + ypos 

# Recover the x and y values of the position.  
xpos = pos/1000 
ypos = pos % 1000 

注意,這殺死可讀性,所以才值得做,如果你想擠進業績的最後一位。在實踐中,您可能希望使用2的冪而不是10的冪作爲分隔符(但10的冪有助於調試和可讀性)。請注意,這會將每個位置的字節數從120增加到24.如果確實按照此路線行事,請考慮使用__slots__定義位置類,以告知Python如何分配內存,並將xposypos屬性添加到類中。您不想用pos/1000pos % 1000陳述亂丟代碼。

+0

感謝您的好評。我不能保證矩形,因爲玩家在隨機生成的迷宮中決定自己想要去哪裏。 – ShadowFlame

+0

另外,如果地圖是動態生成的,則數組不太合適:添加行或列可能非常緩慢。 –