做异常降低性能?

ako*_*nsu 18 .net c# exception-handling

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

Mar*_*ers 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();
Run Code Online (Sandbox Code Playgroud)

这些是未附加调试器并运行发布版本的结果:

Method          Iterations per second
throwException                  10100
fileOpen                         2200
fileExists                      11300

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

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

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

  • 但是在框架知道它需要抛出异常之前,它必须做一些工作.你是说那项工作不是I/O工作吗? (4认同)
  • @Marc:好点.实际上理论上非投掷的`File.TryOpenRead`在性能和清洁设计方面都是最好的解决方案,但不幸的是BCL团队没有提供. (2认同)
  • 但是所有文件检查都在同一个目录中,因此缓存可能在这里起着重要作用.但是因为在成功的情况下File.Exits和File.Open之间存在相同的缓存,所以它不应该改变结论. (2认同)

Lie*_*yan 10

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

if file exists then 
    open file
Run Code Online (Sandbox Code Playgroud)

然后如果另一个程序在你检查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
Run Code Online (Sandbox Code Playgroud)


Mic*_*eyn 7

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

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

  • 无论如何,他需要抓住并处理异常.因为Exists会创建一个竞争条件(从而使代码更难理解),并且File.OpenRead还有其他方法可以失败. (4认同)

Xaq*_*ron 5

这取决于 !

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


Jru*_*rud 5

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

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

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

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

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