[英]Why is my code so slow?
我編寫了以下擴展方法以從字典中獲取元素, 如果不存在鍵,則為null :
public static TValue ItemOrNull<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
{
try
{
return dict[key];
}
catch (KeyNotFoundException ex)
{
return default(TValue);
}
}
我注意到我的程序運行非常緩慢,因此我使用高精度計時器類將問題歸結為此擴展方法。 我連續獲得約100次類似的結果:
DebugTimer.ResetTimer();
dict1.ItemOrNull(key);
dict2.ItemOrNull(key);
DebugTimer.StopTimer();
需要大約1億1百萬個滴答聲(在我的處理器上超過0.03秒)。 而更詳細的版本:
DebugTimer.ResetTimer();
if (dict1.ContainsKey(key))
y = dict1[key];
if (dict2.ContainsKey(key))
z = dict2[key];
DebugTimer.StopTimer();
MessageBox.Show(y.ToString(), z.ToString()) // so the compiler doesn't optimize away y and z
大約需要6,000滴答聲(少於0.000002秒)。
是否有人清楚我的擴展方法版本比冗長版本花費的時間長4個數量級?
不要捕獲用於流控制的異常-不僅是它可能導致性能問題(盡管它們並沒有像大多數人想象的那樣糟糕-就像Eric所說的那樣,大多數人對異常性能的恐懼來自使用調試器)。 它更多地是關於異常的邏輯性質。 就我個人而言, 即使它們本質上是免費的,我也不會以這種方式使用它們 。
這里有什么不好的事嗎? 用戶要求此密鑰的值以任何方式無效嗎? 絕對不是-該方法的全部目的是提供默認值。 缺少鑰匙並不罕見-因此您應該尋找一種無例外的工作方式。
現在Dictionary<,>
已經有一種方法可以讓您在鍵存在的情況下獲取值,並讓您知道是否找到了該鍵: TryGetValue
。
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dict,
TKey key)
{
TValue ret;
// We don't care about the return value - we want default(TValue)
// if it returns false anyway!
dict.TryGetValue(key, out ret);
return ret;
}
擴展方法只是編譯為常規的靜態方法調用,因此對它們沒有性能差異。
您可能還想添加一個重載,如果找不到該鍵,則允許用戶表達默認值以返回:
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dict,
TKey key, TValue defaultValue)
{
TValue ret;
return dict.TryGetValue(key, out value) ? ret : defaultValue;
}
順便說一下,我已經調整了您的方法的名稱以匹配TryGetValue
。 顯然,您不必遵循它-這只是一個建議。
那是因為您沒有在詳細版本中使用異常。 制作一個使用ContainsKey
的擴展方法並進行比較。
澄清一下:異常可能非常緩慢。 這是不依賴它們來控制正常工作流程的原因之一。 它們用於實際上是意料之外的和不需要的情況。
對於FW3.5 +,建議使用TryGetValue
。
這里有兩個問題:
Dictionary.TryGetValue
),則計時會彼此接近。 字典是一個哈希表,因此Dictionary.Contains可以快速運行(在此稱為第一步),它不需要花費任何費用,因為它直接檢查內存中與鍵相關的值。 [鍵->哈希,然后檢查內存]
但是嘗試功能的第一個功能已經檢查過! 在返回值鍵之前,對鍵進行哈希處理並檢查內存。 不久,它已經包含Dictionary.Contains的步驟。
您的代碼運行緩慢的原因是“ try”塊(其中包括Dictionary的步驟)。包含缺少的bool值,這會導致陷入catch塊。 這些浪費您的時間。 上面的答案是正確且更詳細的。
您只需將時間值添加到我的Professors語句中,“異常處理是一個代價高昂的過程!請不要在Catch塊中編寫所有邏輯,請嘗試進行編碼,以便進行避免異常的檢查。” 使用異常處理來處理意外的異常,並不是顯而易見的是,當該項不在字典中時,它將崩潰!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.