【问题标题】:Is it faster to check a file's existence prior to loading, or to catch the exception when it doesn't exist?在加载之前检查文件是否存在,或者在异常不存在时捕获异常是否更快?
【发布时间】:2012-08-07 18:05:31
【问题描述】:

有人建议我使用第二个,try-except 变体,但我也想知道其他人的想法:下面两个(如果有的话)哪个过程更省时?

procedure LoadImage(img: TImage; filename: string);
begin
  if fileexists(filename) then
    img.Picture.Loadfromfile(filename)
  else
    img.Picture.Loadfromfile('default.jpg')
end;

procedure LoadImage(img: TImage; filename: string);
begin
  try
    img.Picture.Loadfromfile(filename)
  except
    img.Picture.Loadfromfile('default.jpg')
  end
end;

【问题讨论】:

  • 它们不相等。第一个至少有两个问题:1)竞争条件 2)如果文件存在,但无法加载,则会抛出异常。
  • 第一个图像多久存在一次?第一个代码优化错误案例,第二个代码优化成功案例。
  • 看在上帝的份上,您正在加载图像.. 不要对速度快几个数量级的代码进行微优化。
  • 不是非此即彼,您应该检查文件是否存在并捕获错误。我真的不喜欢通过 try excepts 定义程序流。
  • 只是一个建议,如果您打算多次加载Default.jpg,那么最好预加载一次并保持加载,每次加载时使用已加载的版本需要,而不是一遍又一遍地从文件中加载它。

标签: performance delphi if-statement try-except


【解决方案1】:

您的问题的一个问题是您没有指定所需的语义。有两种可能的解释,哪种解决方案更好,取决于那个选择。我认为无法加载后备是一个致命错误。

  1. 如果第一个文件确实存在,则加载它,否则加载第二个。如果第一个文件存在,但无法加载,则显示错误。
  2. 如果第一个文件无法加载,则回退到第二个。

如果您想要 1) 的语义,您的第一个代码就可以了。

如果您想要 2) 的语义,两者都不好。第一个代码没有这些语义,因为:

  1. 它有一个竞争条件。如果图像在检查文件是否存在和加载之间被删除,则会失败。
  2. 如果文件存在,但无法加载,则会抛出异常。如果文件不是有效图像或无法打开,则可能会发生这种情况。

第二个使用异常处理常见情况,这很糟糕。

所以要实现我要使用的第二种语义:

procedure LoadImage(img: TImage; filename: string);
var success:boolean;
begin
  success := false;
  if FileExists(filename)
    then try
      img.Picture.LoadFromFile(filename);
      success := true;
    except
    end
  if not success
    then img.Picture.LoadFromFile('default.jpg');
end;

在打开之前检查是否存在使错误情况更快,但成功情况更慢。所以哪个更快取决于你的使用情况。

我个人会使用第三个变体而不是第二个,即使图像只是偶尔丢失,因为我相信正常运行的应用程序不应该抛出异常。如果基准测试显示它很明显,我只会关心性能问题。

您还应该考虑一个更有针对性的例外条款。捕捉所有异常的毯子是不好的风格。不幸的是,我找不到一个明确的规范,TPicture.LoadFromFile 抛出了哪些异常,所以我现在将留在一揽子条款中。

【讨论】:

  • 现在您首先从文件名加载,然后放弃它以加载 default.jpg。缺少else
  • 我不会使用空的 except 子句,那是自找麻烦。
  • NEVER EVER EVER EVER EVER EVER EVER EVER EVER EVER EVER EVER 离开且除了子句为空。永远!
  • @NickHodges 该代码的作用与它仍然包含try LoadFromFile(filename); except LoadFromFile('default.jpg'); end; 时的作用完全相同。我同意except end 很糟糕,但except LoadFromFile('default.jpg'); end 也同样糟糕。
  • 我推荐了更具体的异常过滤,所以它只捕获 IO 相关的异常。但是我在文档中找不到说明该方法可能抛出哪些异常的声明。你有一个很好的来源吗?但是将其留空并不比原始问题中所做的更糟糕。
【解决方案2】:

忘记效率。代码可读性更重要。过早的优化是万恶之源。

第一个意图很明确。每个人都可以很容易地弄清楚发生了什么。

第二个让我停下来走“什么....?”

您绝不希望您的代码引起第二反应。

【讨论】:

  • 清除不正确的代码永远不应优先于不太清晰的正确代码。不,我不是说第二个是正确的。
  • @hvd:“永远不应该优先考虑清晰的错误代码而不是不太清晰的正确代码。” - 那不用说了。但当然,正确的清晰的代码是最好的。
  • 完全同意。我发表评论是因为第一个和第二个都不是,我读到你的回答是说第一个 is 正确。
  • 如果您认为第一个正确,则取决于所需的语义。 Fallback-on-error 和 fallback-on-missing-file 是不一样的,不知道 OP 想要哪一个。
  • 您还没有回答问题。抱怨一开始就提出了这个问题是可以的,但如果抱怨不伴随着答案,那么无论它的论点多么好,都应该在评论中。
【解决方案3】:

如果时间效率是您唯一的标准,那么第一个会更快,因为异常处理会消耗 CPU。

FileExists() 使用一个 WinApi 调用,因此速度很快,但它只检查文件是否存在。 如果文件存在但格式错误或被其他线程阻塞,您将获得未处理的异常。

【讨论】:

  • 你确定FileExists()的开销小于异常处理的开销吗?即使路径可能是慢速服务器的 UNC 路径? (差异可能足够小以至于哪个更快并不重要。)
  • 只有在实际抛出异常时,异常处理才是昂贵的。 try .. except 代码本身不会减慢执行速度。
  • @mjn 至少在旧版本的 delphi 中输入try...except 子句确实会减慢执行速度。与 IO 的成本相比可能可以忽略不计,但您不希望 try except 处于紧密循环中,即使它实际上从未抛出。
  • 在 Delphi 4 和 Delphi 2005 中,@Codes 输入一个 try-except 块涉及两个堆栈推送和一个 mov,我认为从那以后它没有改变。你说几岁?
  • 可能是 Delphi 4 或 6。据我所知,try-except(或者可能是 try-finally)涉及调用与结构化异常处理相关的昂贵 Windows API 函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 1970-01-01
  • 2016-01-08
相关资源
最近更新 更多