40

在我们的网站上显示图像时,我们会通过调用来检查文件是否存在file_exists()。如果文件丢失,我们会退回到虚拟图像。

然而,分析表明这是生成页面最慢的部分,每个文件file_exists()最多需要1/2 毫秒。我们只测试了 40 个左右的文件,但这仍然会在页面加载时间上增加 20毫秒。

任何人都可以提出一种使这更快的方法吗?有没有更好的方法来测试文件是否存在?如果我建立某种缓存,我应该如何保持同步。

4

20 回答 20

34

file_exists()应该是一个非常便宜的操作。还要注意,它file_exists会构建自己的缓存以帮助提高性能。

见: http: //php.net/manual/en/function.file-exists.php

于 2009-11-10T15:32:38.857 回答
23

使用绝对路径!如果您检查相对文件路径,PHP 会根据您的include_path设置检查所有(!)这些目录!在检查存在之前,您可能会include_path暂时取消设置。

realpath()一样,但我不知道它是否更快。

但是文件访问 I/O 总是很慢。通常,硬盘访问在处理器中计算某些东西要慢。

于 2009-11-10T15:31:50.233 回答
21

检查本地文件是否存在的最快方法是stream_resolve_include_path()

if (false !== stream_resolve_include_path($s3url)) { 
  //do stuff 
}

性能结果stream_resolve_include_path()file_exists()

Test name       Repeats         Result          Performance     
stream_resolve  10000           0.051710 sec    +0.00%
file_exists     10000           0.067452 sec    -30.44%

在测试中使用绝对路径。测试源在这里。PHP版本:

PHP 5.4.23-1~dotdeb.1 (cli) (built: Dec 13 2013 21:53:21)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

于 2014-01-01T07:00:35.837 回答
11

如果文件丢失,我们会退回到虚拟图像

如果您只是对回退到这个虚拟图像感兴趣,您可能需要考虑让客户端通过在未找到文件上的重定向(到虚拟图像)与服务器进行协商。

这样,您只会在客户端有一点重定向开销和不明显的延迟。至少你会摆脱对file_exists.

只是一个想法。

于 2009-11-10T15:43:17.860 回答
7

PHP 5.6 的基准测试:

现有文件:

0.0012969970 : stream_resolve_include_path + include  
0.0013520717 : file_exists + include  
0.0013728141 : @include  

无效文件:

0.0000281333 : file_exists + include  
0.0000319480 : stream_resolve_include_path + include  
0.0001471042 : @include  

无效文件夹:

0.0000281333 : file_exists + include  
0.0000360012 : stream_resolve_include_path + include  
0.0001239776 : @include  

代码:

// microtime(true) is less accurate.
function microtime_as_num($microtime){
  $time = array_sum(explode(' ', $microtime));
  return $time;
}

function test_error_suppression_include ($file) {
  $x = 0;
  $x = @include($file);
  return $x;
}

function test_file_exists_include($file) {
  $x = 0;
  $x = file_exists($file);
  if ($x === true) {
    include $file;
  }
  return $x;
}

function test_stream_resolve_include_path_include($file) {
  $x = 0;
  $x = stream_resolve_include_path($file);
  if ($x !== false) {
    include $file;
  }
  return $x;
}

function run_test($file, $test_name) {
  echo $test_name . ":\n";
  echo str_repeat('=',strlen($test_name) + 1) . "\n";

  $results = array();
  $dec = 10000000000; // digit precision as a multiplier

  $i = 0;
  $j = 0;
  $time_start = 0;
  $time_end = 0;
  $x = -1;
  $time = 0;

  $time_start = microtime();
  $x= test_error_suppression_include($file);
  $time_end = microtime();
  $time = microtime_as_num($time_end) - microtime_as_num($time_start);

  $results[$time*$dec] = '@include';

  $i = 0;
  $j = 0;
  $time_start = 0;
  $time_end = 0;
  $x = -1;
  $time = 0;

  $time_start = microtime();
  $x= test_stream_resolve_include_path_include($file);
  $time_end = microtime();
  $time = microtime_as_num($time_end) - microtime_as_num($time_start);

  $results[$time * $dec] = 'stream_resolve_include_path + include';

  $i = 0;
  $j = 0;
  $time_start = 0;
  $time_end = 0;
  $x = -1;
  $time = 0;

  $time_start = microtime();
  $x= test_file_exists_include($file);
  $time_end = microtime();
  $time = microtime_as_num($time_end) - microtime_as_num($time_start);

  $results[$time * $dec ] = 'file_exists + include';

  ksort($results, SORT_NUMERIC);

  foreach($results as $seconds => $test) {
    echo number_format($seconds/$dec,10) . ' : ' . $test . "\n";
  }
  echo "\n\n";
}

run_test($argv[1],$argv[2]);

命令行执行:

php test.php '/path/to/existing_but_empty_file.php' 'Existing File'  
php test.php '/path/to/non_existing_file.php' 'Invalid File'  
php test.php '/path/invalid/non_existing_file.php' 'Invalid Folder'  
于 2016-08-25T16:47:46.960 回答
4

创建一个散列例程,将文件分片到多个子目录中。

文件名.jpg -> 012345 -> /01/23/45.jpg

此外,您可以使用 mod_rewrite 将占位符图像返回到 404 的图像目录请求。

于 2009-11-10T16:14:57.633 回答
3

file_exists()由 PHP 自动缓存。我认为您不会在 PHP 中找到更快的函数来检查文件是否存在。

看到这个线程

于 2009-11-10T15:32:37.873 回答
3

老问题,我要在这里添加一个答案。对于 php 5.3.8,is_file()(对于现有文件)要快一个数量级。对于不存在的文件,时间几乎相同。对于带有 eaccelerator 的 PHP 5.1,它们更接近一些。

PHP 5.3.8 w & w/o APC

time ratio (1000 iterations)
Array
(
    [3."is_file('exists')"] => 1.00x    (0.002305269241333)
    [5."is_link('exists')"] => 1.21x    (0.0027914047241211)
    [7."stream_resolve_inclu"(exists)] => 2.79x (0.0064241886138916)
    [1."file_exists('exists')"] => 13.35x   (0.030781030654907)
    [8."stream_resolve_inclu"(nonexists)] => 14.19x (0.032708406448364)
    [4."is_file('nonexists)"] => 14.23x (0.032796382904053)
    [6."is_link('nonexists)"] => 14.33x (0.033039808273315)
    [2."file_exists('nonexists)"] => 14.77x (0.034039735794067)
)

PHP 5.1 带加速器

time ratio (1000x)
Array
(
    [3."is_file('exists')"] => 1.00x    (0.000458002090454)
    [5."is_link('exists')"] => 1.22x    (0.000559568405151)
    [6."is_link('nonexists')"] => 3.27x (0.00149989128113)
    [4."is_file('nonexists')"] => 3.36x (0.00153875350952)
    [2."file_exists('nonexists')"] => 3.92x (0.00179600715637)
    [1."file_exists('exists"] => 4.22x  (0.00193166732788)
)

有几点需要注意。
1)并非所有“文件”都是文件, is_file() 测试常规文件,而不是符号链接。所以在 *nix 系统上,你不能只使用 is_file() ,除非你确定你只处理普通文件。对于上传等,这可能是一个公平的假设,或者如果服务器是基于 Windows 的,它实际上没有符号链接。否则,您将不得不测试is_file($file) || is_link($file).

2)如果文件丢失并且变得大致相等,所有方法的性能肯定会下降。

3)最大的警告。所有方法都缓存文件统计信息以加快查找速度,因此如果文件定期或快速更改、删除、重新出现、删除,则clearstatcache();必须运行以确保缓存中存在正确的文件存在信息。所以我测试了这些。我省略了所有文件名等。重要的是几乎所有时间都收敛,除了 stream_resolve_include,它的速度是 4 倍。同样,这个服务器上有加速器,所以 YMMV。

time ratio (1000x)
Array
(
    [7."stream_resolve_inclu...;clearstatcache();"] => 1.00x    (0.0066831111907959)
    [1."file_exists(...........;clearstatcache();"] => 4.39x    (0.029333114624023)
    [3."is_file(................;clearstatcache();] => 4.55x    (0.030423402786255)
    [5."is_link(................;clearstatcache();] => 4.61x    (0.030798196792603)
    [4."is_file(................;clearstatcache();] => 4.89x    (0.032709360122681)
    [8."stream_resolve_inclu...;clearstatcache();"] => 4.90x    (0.032740354537964)
    [2."file_exists(...........;clearstatcache();"] => 4.92x    (0.032855272293091)
    [6."is_link(...............;clearstatcache();"] => 5.11x    (0.034154653549194)
)

基本上,这个想法是,如果你 100% 确定它是一个文件,而不是符号链接或目录,并且很可能它会存在,那么使用is_file(). 你会看到一定的收获。如果文件在任何时候都可能是文件或符号链接,那么失败的 is_file() 14x + is_link() 14x ( is_file() || is_link()) 最终会整体慢 2 倍。如果文件的存在发生了很多变化,则使用 stream_resolve_include_path()。

所以这取决于你的使用场景。

于 2016-08-21T05:49:02.603 回答
2

如果您只检查现有的files,请使用is_file(). file_exists()检查现有文件或目录,因此可能is_file()会快一点。

于 2009-11-10T15:51:30.090 回答
2

我不完全知道你想做什么,但你可以让客户处理它

于 2010-07-26T17:03:27.150 回答
1

它们都在同一个目录中吗?如果是这样,可能值得获取文件列表并将它们存储在哈希中并与之进行比较,而不是所有 file_exists 查找。

于 2009-11-10T15:34:27.427 回答
1

如果要检查图像文件是否存在,更快的方法是使用getimagesize

本地和远程更快!

if(!@GetImageSize($image_path_or_url)) // False means no imagefile
 {
 // Do something
 }
于 2015-02-11T10:27:04.067 回答
1

2021 年,也就是问这个问题 12 年后,我有同样的用例。file_exist在决定显示什么之前,我会循环检查大约 40 张图像。

以毫秒为单位的数字(PHP 7.4):

  • 在本地开发机器(Win10、WAMP、三星 SSD)上:每张图像大约 0.1 (1/10) 毫秒,文件夹中大约有 1000 张图像;
  • 在服务器上(非常基本的廉价服务器,VPS 1 Intel Xeon,RAM 2GB,SSD,Ubuntu,LAMP):每张图像大约 0.01 (1/100) 毫秒,文件夹中有 14,000 张图像;

服务器比开发机器快 10 倍,并且与整体 UX 性能 POV 无法区分,其中 30-50 毫秒是第一个明显的阈值。

在服务器检查 40 张图像的数组时,我花了 0.4 毫秒来检查其中是否有人不存在。顺便说一句,无论某些图像是否存在,性能都没有差异。

file_exist因此,由于磁盘性能,是否进行检查应该是毫无疑问的。检查是否需要。

于 2021-03-29T07:57:11.870 回答
0

怎么样glob()?但我不确定它是否很快。

http://www.php.net/manual/en/function.glob.php

于 2009-11-10T15:37:44.377 回答
0

我发现每次通话 1/2 毫秒非常非常实惠。我认为没有更快的替代方案,因为文件函数非常接近处理文件操作的较低层。

但是,您可以为 file_exists() 编写一个包装器,将结果缓存到 memcache 或类似设施中。这应该可以将日常使用的时间减少到几乎没有。

于 2009-11-10T15:39:36.347 回答
0

您可以执行 cronjob 来定期创建图像列表并将它们存储在 DB/file/BDB/...

每半小时应该没问题,但一定要创建一个接口来重置缓存以防文件添加/删除。

然后,运行 find 也很容易。-mmin -30 -print0 在 shell 上并添加新文件。

于 2011-04-19T08:08:17.720 回答
0

当您将文件保存到文件夹时,如果上传成功,您可以将路径存储到 DB Table。

然后,您只需查询数据库即可找到所请求文件的路径。

于 2012-01-26T10:41:38.237 回答
0

我来到这个页面寻找解决方案,似乎 fopen 可以解决问题。如果您使用此代码,您可能希望对未找到的文件禁用错误日志记录。

<?php
for ($n=1;$n<100;$n++){
clearstatcache();
$h=@fopen("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>
于 2014-09-21T20:23:38.203 回答
0

我认为最好的方法是将图像 url 保存在数据库中,然后将其放入会话变量中,尤其是在您进行身份验证时。通过这些方式,您不必每次重新加载页面时都进行检查

于 2017-05-09T02:40:53.287 回答
-1

我什至不确定这是否会更快,但看起来你仍然想对 soooo 进行基准测试:

构建包含所有图像路径的大型数组的缓存。

$array = array('/path/to/file.jpg' => true, '/path/to/file2.gif' => true);

根据您的要求,每小时每天更新缓存。您将使用cron运行 PHP 脚本来执行此操作,该脚本将递归地遍历 files 目录以生成路径数组。

当你想检查一个文件是否存在时,加载你的缓存数组并做一个简单的isset()检查快速数组索引查找:

if (isset($myCachedArray[$imgpath])) {
    // handle display
}

加载缓存仍然会有开销,但希望它足够小以留在内存中。如果您在一个页面上检查多个图像,您可能会注意到更显着的收益,因为您可以在页面加载时加载缓存。

于 2009-11-10T15:51:27.353 回答