Neden sözlüğünde anahtarın bulunup bulunmadığını kontrol etmek neden olmasa da, istisnayı yakalamak daha hızlıdır?


234

Kodu hayal edin:

public class obj
{
    // elided
}

public static Dictionary<string, obj> dict = new Dictionary<string, obj>();

Yöntem 1

public static obj FromDict1(string name)
{
    if (dict.ContainsKey(name))
    {
        return dict[name];
    }
    return null;
}

Yöntem 2

public static obj FromDict2(string name)
{
    try
    {
        return dict[name];
    }
    catch (KeyNotFoundException)
    {
        return null;
    }
}

Bu 2 işlevin performansında bir fark olup olmadığını merak ettim, çünkü birincisi ikincisinden daha YAVAŞ OLMALIDIR - sözlüğün bir değer içerip içermediğini iki kez kontrol etmesi gerektiğinden, ikinci işlevin yalnızca sözlüğe erişmesi gerekir bir kez ama WOW, aslında tam tersi:

1000 000 değer için döngü (100 000 mevcut ve 900 000 mevcut değil):

ilk fonksiyon: 306 milisaniye

ikinci fonksiyon: 20483 milisaniye

Neden?

DÜZENLEME: Bu sorunun altındaki yorumlarda da görebileceğiniz gibi, mevcut olmayan anahtarların olması durumunda ikinci işlevin performansı aslında birinciden biraz daha iyidir. Ancak en az 1 veya daha fazla mevcut olmayan anahtar olduğunda, ikincisinin performansı hızla düşer.


39
İlki neden daha yavaş olmalı ? Aslında, ilk bakışta, daha hızlı olması gerektiğini söyleyebilirim ContainsKey, bekleniyor O(1)...
Patryk Ćwiek


8
@Petr İstisna atmayla ilgili O(1), sözlükteki aramadan çok daha fazla talimat var ... Özellikle iki O(1)işlem yapmak hala asimptotik olduğundan O(1).
Patryk Ćwiek

9
Aşağıdaki iyi yanıtta belirtildiği gibi, istisnalar atmak pahalıdır. İsimleri şunu önermektedir: istisnai durumlar için ayrılması amaçlanmıştır . Var olmayan anahtarlar için sözlüğü milyonlarca kez sorguladığınız bir döngü çalıştırıyorsanız, sıra dışı bir durum olmaktan çıkar. Bir sözlüğü anahtarlar için sorguluyorsanız ve anahtarların bulunmaması nispeten yaygın bir durumsa, önce kontrol etmek mantıklıdır.
Jason R

6
Unutmayın, sadece bir milyon eksik değer kontrol etme maliyetini bir milyon istisna atmakla karşılaştırdınız. Ancak iki yöntem de mevcut bir değere erişme maliyetinde farklılık gösterir . Eksik anahtarları nadir yeterli değilse, istisna yöntemi, yüksek maliyetine rağmen, daha hızlı bitmiş olacak zaman anahtar yoktur.
alexis

Yanıtlar:


404

Bir yandan, istisnaları atmak doğal olarak pahalıdır , çünkü yığının açılması gerekir.
Diğer yandan, bir sözlükteki bir değere anahtarıyla erişmek ucuzdur, çünkü bu hızlı, O (1) bir işlemdir.

BTW: Bunu yapmanın doğru yolu TryGetValue

obj item;
if(!dict.TryGetValue(name, out item))
    return null;
return item;

Bu, sözlüğe iki kez değil, yalnızca bir kez erişir. Anahtar yoksa
gerçekten geri dönmek istiyorsanız null, yukarıdaki kod daha da basitleştirilebilir:

obj item;
dict.TryGetValue(name, out item);
return item;

Bunun nedeni, işler TryGetValuesetleri itemiçin nullhiçbir tuşa eğer namevar.


4
Testimi cevaba göre güncelledim ve bazı nedenlerden dolayı, önerilen fonksiyon daha hızlı olmasına rağmen, aslında çok önemli değil: 264 ms orijinal, 258ms bir tane önerdi
Petr

52
@Petr: Evet, önemli değil, çünkü sözlüğe erişmek çok hızlı, bir veya iki kez yapmanız gerçekten önemli değil. Bu 250 msn büyük olasılıkla test döngüsünün içinde harcanır.
Daniel Hilgarth

4
Bunu bilmek iyidir, çünkü bazen istisna atmanın, varolan dosya veya boş gösterici gibi bir durumu, bu durumların ortak olup olmadığına bakılmaksızın ve performans maliyetini dikkate almadan daha iyi veya daha temiz bir yol olduğu izlenimi edinir.
LarsH

4
@LarsH aynı zamanda ne yaptığınıza da bağlıdır. Bunun gibi basit mikro karşılaştırmalar, döngüleriniz başladıktan sonra her yinelemeye bir istisna atan dosya veya veritabanı etkinlikleri dahil olmak üzere istisnalar için gerçekten büyük cezalar gösterirken, performans için çok az önemlidir. 1. ve 2. tabloları karşılaştırın: codeproject.com/Articles/11265/…
Dan Is Fiddling By Firelight

8
@LarsH Bir dosyaya (veya başka bir harici kaynağa) erişmeye çalışırken, denetim ve gerçek erişim girişimi arasındaki durumu değiştirebileceğini de unutmayın. Bu durumlarda, istisnaları kullanmak doğru yoludur. Daha fazla bilgi için Stephen C'nin bu soruya verdiği cevaba bakınız .
yoniLavi

6

Sözlükler süper hızlı anahtar aramaları yapmak için özel olarak tasarlanmıştır. Karma tablolar olarak uygulanırlar ve ne kadar çok girdi diğer yöntemlere göre o kadar hızlı olurlar. İstisna motorunu kullanmak, yalnızca yönteminiz bunu yapmak için tasarladığınız şeyi yapamadığı zaman yapılmalıdır, çünkü size hataları işlemek için çok fazla işlevsellik veren geniş bir nesne kümesidir. Bir keresinde tüm kütüphane sınıfını bir kez denemek bloklarla çevrili bir kez inşa ettim ve 600'den fazla istisnadan her biri için ayrı bir satır içeren hata ayıklama çıktısını görmek için dehşete düştüm!


1
Dil uygulayıcıları optimizasyon çabalarını nereye harcayacaklarına karar verirken, karma tablolar öncelik kazanacaktır çünkü sık sık, sık sık darboğaz olabilecek iç döngülerde kullanılırlar. İstisnaların yalnızca olağandışı ("istisnai", tabiri caizse) durumlarda daha az sıklıkta kullanılması beklenir, bu nedenle performans için genellikle önemli sayılmazlar.
Barmar

"Karma tablolar olarak uygulanır ve ne kadar çok girdi diğer yöntemlere göre o kadar hızlı olur." şüphesiz ki kovalar dolarsa bu doğru değildir?!?!
AnthonyLambert

1
@AnthonyLambert Söylemeye çalıştığı şey, bir hashtable aramanın O (1) zaman karmaşıklığına sahipken, ikili bir arama ağacı aramasının O (log (n)); eleman sayısı asimptotik olarak arttıkça ağaç yavaşlar, ancak hashtable artmaz. Bu nedenle, hashtable'ın hız avantajı, çok yavaş olmasına rağmen, eleman sayısı ile artar.
Doval

@AnthonyLambert Normal kullanımda, bir Sözlük'ün hashtable'ında çok az çarpışma vardır. Bir hashtable kullanıyorsanız ve kovalarınız dolarsa, çok fazla girişiniz (veya çok az sayıda kova) var. Bu durumda, özel bir hashtable kullanma zamanı.
Mart'ta AndrewS
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.