11

我有一个 Vector2 的 Generated 列表,我必须检查字典以查看它们是否存在,这个函数在每个滴答声中执行。

以这种方式运行最快/更好?

    public static bool exists(Vector2 Position, Dictionary<Vector2, object> ToCheck)
    {
        try
        {
            object Test = ToCheck[Position];
            return (true);
        }
        catch 
        {
            return (false);
        }           
    }

还是我应该坚持规范?

    public static bool exists(Vector2 Position, Dictionary<Vector2, object> ToCheck)
    {
        if (ToCheck.ContainsKey(Position))
        {
            return (true);
        }
        return (false);
    }

感谢您的输入:)

旁注:(此时键的值无关紧要,否则我将使用 TryGetValue 而不是 ContainsKey)

4

4 回答 4

28

我知道这是一个老问题,但只是添加一些经验数据......

在包含 10,000 个条目的字典上运行 50,000,000 次查找并比较完成的相对时间:

..如果每次查找都成功:

  • 直接(未经检查)运行需要 1.2 秒
  • 受保护的 (ContainsKey) 运行需要 2 秒
  • 处理(try-catch)运行需要 1.21 秒

..如果每 10,000 次查找中有 1 次失败:

  • 受保护的 (ContainsKey) 运行需要 2 秒
  • 处理(try-catch)运行需要 1.37 秒

..如果每 10,000 次查找中有 16 次失败:

  • 受保护的 (ContainsKey) 运行需要 2 秒
  • 处理(try-catch)运行需要 3.27 秒

..如果每 10,000 次查找中有 250 次失败:

  • 受保护的 (ContainsKey) 运行需要 2 秒
  • 处理(try-catch)运行需要 32 秒

..因此,受保护的测试将增加恒定的开销,仅此而已,并且如果它从不失败,try-catch 测试的运行速度几乎与没有测试一样快,但会与失败的数量成比例地降低性能。

我用来运行测试的代码:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
   class Program
   {
      static void Main(string[] args)
      {  Test(0);
         Test(1);
         Test(16);
         Test(250);
      }

      private static void Test(int failsPerSet)
      {  Dictionary<int, bool> items = new Dictionary<int,bool>();

         for(int i =  0; i < 10000; i++)
            if(i >= failsPerSet)
               items[i] = true;

         if(failsPerSet == 0)
            RawLookup(items, failsPerSet);

         GuardedLookup(items, failsPerSet);

         CaughtLookup(items, failsPerSet);

      }

      private static void RawLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Raw     (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            if(items[pick])
               found++;
         }

         Console.WriteLine(DateTime.Now - start);
      }

      private static void GuardedLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Guarded (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            if(items.ContainsKey(pick))
               if(items[pick])
                  found++;
         }

         Console.WriteLine(DateTime.Now - start);
      }

      private static void CaughtLookup
      (  Dictionary<int, bool> items
      ,  int             failsPerSet
      ){ int                   found = 0;
         DateTime              start ;

         Console.Write("Caught  (");
         Console.Write(failsPerSet);
         Console.Write("): ");

         start = DateTime.Now;
         for(int i = 0; i < 50000000; i++)
         {  int pick = i % 10000;
            try
            {  if(items[pick])
                  found++;
            }
            catch
            {  
            }
         }

         Console.WriteLine(DateTime.Now - start);
      }

   }
}
于 2012-11-02T12:05:47.300 回答
18

一定要用ContainsKey支票;异常处理会增加很大的开销

抛出异常会对性能产生负面影响。对于经常失败的代码,您可以使用设计模式来最大程度地减少性能问题。

异常不适用于您可以检查的条件。

我建议阅读有关异常的 MSDN 文档,特别是有关异常处理的文档。

于 2012-09-14T00:45:57.573 回答
0

切勿将 try/catch 用作常规程序路径的一部分。它确实很昂贵,并且应该只捕获您无法阻止的错误。ContainsKey 是这里的方法。

旁注:不,你不会。如果该值很重要,请使用 ContainsKey 检查它是否存在并检索它(如果存在)。不尝试/抓住。

于 2012-09-14T00:48:24.730 回答
0

旁注:(此时键的值无关紧要,否则我将使用 TryGetValue 而不是 ContainsKey)

您接受的答案是正确的,但只是补充一下,如果您只关心键而不关心值,也许您正在寻找 aHashSet而不是 a Dictionary

此外,您的第二个代码片段是一种从字面上添加零值的方法。只需使用ToCheck.ContainsKey(Position),不要创建仅调用该方法并返回其值但不执行其他任何操作的方法。

于 2012-09-14T02:26:39.923 回答