2017-07-21 18 views
5

ile karşılaştırıldığında püskürtme yapmak Bu bana tamamen şaşırtmış.Python OrderDict()

asset_hist = [] 
for key_host, val_hist_list in am_output.asset_history.items(): 
    for index, hist_item in enumerate(val_hist_list): 
     #row = collections.OrderedDict([("computer_name", key_host), ("id", index), ("hist_item", hist_item)]) 
     row = {"computer_name": key_host, "id": index, "hist_item": hist_item} 
     asset_hist.append(row) 

Bu kod, yorumlanmış koleksiyon çizgileriyle mükemmel bir şekilde çalışır. Ancak, row = dict satırını yorumladığımda ve koleksiyon satırındaki yorumu kaldırdığımda işler çok garip olur. Asset_hist için bu satırların yaklaşık 4 milyonu üretilir ve eklenir.

Yani, row = dict komutunu kullandığımda, tüm döngü yaklaşık 10 milisaniyede bitiyor, hızlı yıldırım. Sipariş edilen sözlüğü kullandığımda 10 dakikadan fazla bekledim ve hala bitmedi. Şimdi, OrderDict'ın bir diksiyondan biraz daha yavaş olması gerektiğini biliyorum, ama en azından 10x daha yavaş olması gerekiyordu ve matematik tarafından bu fonksiyonda 100,000 kat daha yavaş.

Neler olduğunu görmek için endeksi en düşük döngüde yazdırmaya karar verdim. İlginç bir şekilde, konsol çıkışında bir sıçramayı fark ettim. İndeks ekranda çok hızlı yazdırılır ve devam etmeden önce yaklaşık 3-5 saniye boyunca durur.

am_output.asset_history bir anahtar, ana bilgisayar ve her satır dizelerin bir listesi olan bir sözlüktür. Örneğin.

am_output.asset_history = {"host1": ["string1", "string2", ...], "host2": ["string1", "string2", ...], ...}

DÜZENLEME: OrderedDict bu VM Server

Toplam Bellek ile Sputter Analizi: Sadece 8GB ... daha provissioned almak gerekir.

DÖNGÜ NUM

184796 (~ 5 saniye bekleyin, ~% 60 bellek kullanımı)

634481 (~ 5 saniye bekleyin, ~% 65 bellek kullanımı)

1197564 (~ 5 saniye bekleme , ~% 70 bellek kullanımı)

1899247 (~ 5 saniye bekleyin, ~% 75 bellek kullanımı)

2777296 (~ 5 saniye bekleyin, ~% 80 bellek kullanımı)

012.351.

3873730 (UZUN WAIT ... 20 dakika bekledi ve !,% 88,3 bellek kullanımı, süreç hala çalışıyorsa vazgeçti) bekleme her çalışmaya değişiklikleri olur

.

EDIT: Tekrar çalıştırın, bu sefer daha önce durduğu noktaya yakın 3873333'te durun. Satır oluşturduktan sonra durdu, eklemeye çalışırken ... Bu son denemeyi fark etmedim ama o zamanlar da vardı ... sorun, satır çizgisi değil, ek satır ile ... ben hala şaşkın. , OrderedDict ([('bilgisayar_adı', 'bg-fd5612ea'):

3873333: İşte bu doğru uzun durağı önce üretilen satır (baskı açıklamaya satır eklendi) var ... hostname masum korumak için değiştirilmiştir ('id', 1), ('hist_item', 'sys1 Normalizatör (sys1-4): Etki Alanı Adı, sys1 Adından' gg-fd5612ea '.')])

+1

Gözlemlediğiniz şeyin, 'Dict' ve 'OrderedDict' davranışlarından kaynaklandığına inanmak zor, başka bir şeyin değişmesi mümkün mü? Ayrıca birisinin bu –

+1

'u yeniden üretmek için mücadele edeceğini düşünüyorum, öncelikle büyük sözlüğü üretmeleri gerekecek, muhtemelen rasgele dize üreticileri yeterli olacaktır. Ama ben tamamen ciddiyim, # aşağı doğru hareket ettiğimde bu olur. Aynı zamanda dikkat çekicidir: sıralı bir dict kullanıldığında bu döngüde sıkışıp kaldığında ve kontrolü durdurmak için C + tuşuna bastığımda, hiçbir şey olmuyor ... Kontrol için + Z'ye basmalıyım. – gunslingor

+0

Hangi Python sürümü? En yeni olarak, OrderedDict basit bir şekilde, uygulama detayı ile sipariş verileceği için, dict için bir takma addır. – RemcoGerlich

cevap

1

Kendi testleriniz olarak ispatlanamaz. Hafızanız bitti. CPython 3'te bile.6 (burada dict no'lu dizinin gerçekte bir dil garantisi olmadığı halde sipariş edildiği), OrderedDict, dict; Siparişi korumak ve kolay yinelemeyi desteklemek, move_to_end vb. ile yeniden sipariş vermek için yan bant bağlantılı bir liste ile uygulanmaktadır. Sadece sys.getsizeof ile kontrol ederek kesin sonuçlar verebilir (tam sonuçlar Python sürümüne göre farklılık gösterecek ve bitwidth, 32 vs 64 bit oluşturacaktır)): kayıtlı verileri göz ardı edilmesi

>>> od = OrderedDict([("a", 1), ("b", 2), ("c", 3)]) 
>>> d = {**od} 
>>> sys.getsizeof(od) 
464 # On 3.5 x64 it's 512 
>>> sys.getsizeof(d) 
240 # On 3.5 x64 it's 288 

, OrderedDict için havai burada neredeyse iki katı düz dict arasında. Bu öğelerin 4 milyonunu yapıyorsanız, makinemde 850 MB'ın üzerinde bir eğim yükü ekleyecektir (hem 3,5 hem de 3.6'da).

Bu, sisteminizdeki diğer tüm programların ve Python programınızın birleşimi, makinenize tahsis edilen RAM'i aşması olasılığından dolayı, takas atma takılı kalmanıza neden olabilir. Özellikle, yeni girişler için asset_hist'un genişlemesi gerektiğinde, büyük bölümlerinde (kullanım sıkıntısı çekildi) ve döngüsel bir çöp toplama çalıştırması her tetiklendiğinde (tam bir GC yaklaşık 70.000 tahsisatta) büyük olasılıkla sayfalara ihtiyaç duyuyor. ve varsayılan olarak ayrılırlar), tüm OrderedDict s döngüleri dışında hala başvurulan olup olmadığını kontrol etmek için geri paged olsun (GC, GC via gc.disable() döngüsel devre dışı bırakarak ana sorun olup olmadığını kontrol edebilirsiniz).

Özel kullanım durumunuz göz önünde bulundurulduğunda, hem dict hem de OrderedDict'dan kaçınmanızı önemle tavsiye ederim. dict, hatta Python 3.6'daki daha ucuz bir formun üst kısmı bile, tam olarak üç sabit tuştan oluşan bir setiniz olduğunda aşırı derecede zor. Bunun yerine, ad veya dizin tarafından adlandırılan hafif nesneler için tasarlanan use collections.namedtuple (düzenli olarak tuple s gibi davranır, ancak her bir değere adlandırılmış bir özellik olarak erişime izin verir), bu da programınızın bellek maliyetini önemli ölçüde azaltacaktır (ve olası hız). bellek bir sorun olmadığı zaman bile. Örneğin

:

from collections import namedtuple 

ComputerInfo = namedtuple('ComputerInfo', ['computer_name', 'id', 'hist_item']) 

asset_hist = [] 
for key_host, val_hist_list in am_output.asset_history.items(): 
    for index, hist_item in enumerate(val_hist_list): 
     asset_hist.append(ComputerInfo(key_host, index, hist_item)) 
kullanımda

Tek fark sen row.computer_name ile row['computer_name'] değiştirin ya da tüm değerler gerekiyorsa, düzenli tuple, örneğin gibi açmaktır olmasıdır comphost, idx, hist = row. Geçici olarak OrderedDict'a ihtiyacınız varsa (bunları her şey için saklamayın), 'u namedtuple ile aynı eşleme ile birlikte almak için row._asdict() numaralı telefonu arayabilirsiniz, ancak normal olarak gerekli değildir. Hafıza tasarrufu anlamlıdır; Sistemimde, namedtuple öğesinin üç öğesi, her bir öğe başına düşen yükü 72 0te, 3,6 dict öğesinin bile üçte birinden daha az ve 3.6 OrderedDict (ve namedtuple öğesinin üçte birinden 72 bayta kalıyor, burada dict/OrderedDict, 3.6 öncesi) daha büyüktür. Bundan bile daha fazlasını kurtarabilir; tuple s (ve namedtuple uzantısına göre), tek bir bitişik C struct olarak atanırken, dict ve şirket en az iki tahsisattır (her biri yapının dinamik olarak yeniden boyutlandırılabilir bölümleri için nesne yapısı, biri veya daha fazlası). ödenek yükü ve hizalama masraflarını ödemek.

Her iki durumda da dört milyondan sıralı senaryosu için, namedtuple kullanılarak (değerlerin maliyet ötesinde) etrafında 275 toplam MB, vs 915 yükünü (3.6) ödeyerek anlamına gelecektir - dict ve 1770 için 1100 (3.5) MB (3.6) - OrderedDict için 1950 (3.5) MB. 8 GB'lık bir sistemden bahsederken, yükünüzü 1.5 GB değerinde tıraş etmek büyük bir gelişme.