做异常降低性能?

时间:2010-10-15 20:51:31

标签: c# .net exception-handling

我的应用程序遍历目录树,并在每个目录中尝试打开具有特定名称的文件(使用File.OpenRead())。如果此调用抛出FileNotFoundException,则它知道该文件不存在。在此之前我是否愿意进行File.Exists()调用以检查文件是否存在?这会更有效吗?

11 个答案:

答案 0 :(得分:24)

<强>更新

我在一个循环中运行这两个方法并按时计时:

void throwException()
{
    try
    {
        throw new NotImplementedException();
    }
    catch
    {
    }
}

void fileOpen()
{
    string filename = string.Format("does_not_exist_{0}.txt", random.Next());
    try
    {
        File.Open(filename, FileMode.Open);
    }
    catch
    {
    }
}

void fileExists()
{
    string filename = string.Format("does_not_exist_{0}.txt", random.Next());
    File.Exists(filename);
}

Random random = new Random();

结果没有附加调试器并运行发布版本

Method          Iterations per second
throwException                  10100
fileOpen                         2200
fileExists                      11300

抛出异常的成本远高于我的预期,并且在不存在的文件上调用FileOpen似乎比检查不存在的文件的存在要慢得多。

如果文件通常不存在,则检查文件是否存在似乎更快。我想在相反的情况下 - 当文件通常存在时,你会发现捕获异常的速度更快。如果性能对您的应用程序至关重要,我建议您对实际数据的两个apporaches进行基准测试。

正如其他答案中所提到的,请记住,即使你在打开它之前检查文件的存在,如果有人在你的存在检查之后但在打开之前删除了文件,你应该注意竞争条件。你仍然需要处理异常。

答案 1 :(得分:10)

不,不要。如果使用File.Exists,则会引入并发问题。如果你写了这段代码:

if file exists then 
    open file

然后如果另一个程序在你检查File.Exists和实际打开文件之间删除了你的文件,那么程序仍会抛出异常。

其次,即使文件存在,这并不意味着您实际上可以打开该文件,您可能没有打开该文件的权限,或者该文件可能是只读文件系统,因此您无法打开写模式等

文件I / O比异常要昂贵得多,不需要担心异常的性能。

编辑: 在Linux下用Python标记异常与存在

import timeit
setup = 'import random, os'

s = '''
try:
    open('does not exist_%s.txt' % random.randint(0, 10000)).read()
except Exception:
    pass
'''
byException = timeit.Timer(stmt=s, setup=setup).timeit(1000000)

s = '''
fn = 'does not exists_%s.txt' % random.randint(0, 10000)
if os.path.exists(fn):
    open(fn).read()
'''
byExists = timeit.Timer(stmt=s, setup=setup).timeit(1000000)

print 'byException: ', byException   # byException:  23.2779269218
print 'byExists: ', byExists  # byExists:  22.4937438965

答案 2 :(得分:7)

这种行为真的很特别吗?如果需要,您应该使用if语句进行测试,而不是使用异常。性能不是此解决方案的唯一问题,并且从您尝试执行的操作的声音来看,性能不应成为问题。因此,风格和良好的方法应该是这个解决方案的关注项目。

因此,总而言之,由于您希望某些测试失败,请使用File.Exists检查而不是在事后捕获异常。当然,您仍然应该捕获可能发生的其他异常。

答案 3 :(得分:5)

取决于!

如果该文件存在的可能性很高(你知道这适用于你的场景,但作为一个类似desktop.ini的例子)我宁愿直接尝试打开它。 无论如何,在使用File.Exist的情况下,你需要将File.OpenRead放在try / catch中以用于并发原因并避免任何运行时异常,但是如果有文件存在的话,它会大大提高你的应用程序性能低。 Ostrich algorithm

答案 4 :(得分:5)

运行目录搜索,找到它,然后尝试打开它不是最有效的吗?

Dim Files() as string = System.IO.Directory.GetFiles("C:\", "SpecificName.txt", IO.SearchOption.AllDirectories)

然后你会得到一个你知道存在的字符串数组。

哦,作为对原始问题的回答,我会说是的,try / catch会引入更多的处理器周期,我还假设IO偷看实际上需要的时间比处理器周期的开销要长。

首先运行存在,然后是打开的第二个,是2个IO功能,而不是试图打开它。实际上,我认为整体性能将是对处理器上的处理器时间与硬盘驱动器速度的判断。如果你有一个较慢的处理器,我会选择检查,如果你有一个快速处理器,我可能会选择try / catch。

答案 5 :(得分:4)

File.Exists是一个很好的第一道防线。如果该文件不存在,那么如果您尝试打开它,则可以保证获得异常。存在检查比投掷和捕获异常的成本便宜。 (也许不会便宜多少,但有点。)

还有另一个考虑因素:调试。当你在调试器中运行时,抛出和捕获异常的成本更高,因为IDE已经挂钩了异常机制,增加了你的高架。如果你已经检查了Debug&gt;中的任何“Break on thrown”复选框。例外,那么任何可避免的异常都会成为巨大的痛点。仅仅因为这个原因,我会争取在可能的情况下防止例外。

但是,你还需要try-catch,原因在于其他答案。 File.Exists调用仅仅是一种优化;它不会使您免于因时间,权限,太阳耀斑等而需要捕获异常。

答案 6 :(得分:3)

我不知道效率,但我更喜欢File.Exists检查。问题是可能发生的所有其他事情:错误的文件句柄等。如果您的程序逻辑知道有时文件不存在并且您希望对现有文件和不存在文件具有不同的行为,请使用文件。存在。如果它不存在与其他与文件相关的异常相同,则只使用异常处理。

答案 7 :(得分:3)

是的,你应该使用File.Exists。例外情况应该用于特殊情况,而不是控制程序的正常流程。在您的情况下,不存在的文件不会发生异常。因此,您不应该依赖例外。

更新:

所以每个人都可以自己尝试,我会发布我的测试代码。对于非现有文件,依赖File.Open为您抛出异常比使用File.Exists检查要差50倍。

class Program
{
   static void Main(string[] args)
   {
      TimeSpan ts1 = TimeIt(OpenExistingFileWithCheck);

      TimeSpan ts2 = TimeIt(OpenExistingFileWithoutCheck);

      TimeSpan ts3 = TimeIt(OpenNonExistingFileWithCheck);

      TimeSpan ts4 = TimeIt(OpenNonExistingFileWithoutCheck);
   }

   private static TimeSpan TimeIt(Action action)
   {
      int loopSize = 10000;

      DateTime startTime = DateTime.Now;
      for (int i = 0; i < loopSize; i++)
      {
         action();
      }

      return DateTime.Now.Subtract(startTime);
   }

   private static void OpenExistingFileWithCheck()
   {
      string file = @"C:\temp\existingfile.txt";
      if (File.Exists(file))
      {
         using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
         {
         }
      }
   }

   private static void OpenExistingFileWithoutCheck()
   {
      string file = @"C:\temp\existingfile.txt";
      using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
      {
      }
   }

   private static void OpenNonExistingFileWithCheck()
   {
      string file = @"C:\temp\nonexistantfile.txt";
      if (File.Exists(file))
      {
         using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
         {
         }
      }
   }

   private static void OpenNonExistingFileWithoutCheck()
   {
      try
      {
         string file = @"C:\temp\nonexistantfile.txt";
         using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
         {
         }
      }
      catch (Exception ex)
      {
      }
   }
}

在我的电脑上:

  1. ts1 = .75秒(附带或不附带调试器)
  2. ts2 = .56秒(无论是否附带调试器)
  3. ts3 = .14秒(无论是否附带调试器)
  4. ts4 = 14.28秒(附带调试器)
  5. ts4 = 1.07(未附带调试器)
  6. 更新:

    我添加了关于是否附加了dubgger的详细信息。我测试了调试和发布版本,但唯一有所作为的是在调试器附加时最终抛出异常的一个函数(这是有道理的)。尽管如此,使用File.Exists检查是最好的选择。

答案 8 :(得分:1)

我会说,一般来说,例外会“增加”系统的整体“性能”!

在您的示例中,无论如何,最好使用File.Exists ...

答案 9 :(得分:1)

首先使用File.Exists的问题是它也会打开文件。所以你最终打开文件两次。我没有测量它,但我想这个额外的文件打开比偶尔的例外更昂贵。

如果File.Exists检查改进了性能取决于文件存在的概率。如果它可能存在,那么不要使用File.Exists,如果它通常不存在,额外的检查将提高性能。

答案 10 :(得分:0)

异常的开销很明显,但与文件操作相比并不重要。