在我们的网站上显示图像时,我们会通过调用来检查文件是否存在file_exists()
。如果文件丢失,我们会退回到虚拟图像。
然而,分析表明这是生成页面最慢的部分,每个文件file_exists()
最多需要1/2 毫秒。我们只测试了 40 个左右的文件,但这仍然会在页面加载时间上增加 20毫秒。
任何人都可以提出一种使这更快的方法吗?有没有更好的方法来测试文件是否存在?如果我建立某种缓存,我应该如何保持同步。
在我们的网站上显示图像时,我们会通过调用来检查文件是否存在file_exists()
。如果文件丢失,我们会退回到虚拟图像。
然而,分析表明这是生成页面最慢的部分,每个文件file_exists()
最多需要1/2 毫秒。我们只测试了 40 个左右的文件,但这仍然会在页面加载时间上增加 20毫秒。
任何人都可以提出一种使这更快的方法吗?有没有更好的方法来测试文件是否存在?如果我建立某种缓存,我应该如何保持同步。
file_exists()
应该是一个非常便宜的操作。还要注意,它file_exists
会构建自己的缓存以帮助提高性能。
使用绝对路径!如果您检查相对文件路径,PHP 会根据您的include_path
设置检查所有(!)这些目录!在检查存在之前,您可能会include_path
暂时取消设置。
realpath()
一样,但我不知道它是否更快。
但是文件访问 I/O 总是很慢。通常,硬盘访问比在处理器中计算某些东西要慢。
检查本地文件是否存在的最快方法是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
如果文件丢失,我们会退回到虚拟图像
如果您只是对回退到这个虚拟图像感兴趣,您可能需要考虑让客户端通过在未找到文件上的重定向(到虚拟图像)与服务器进行协商。
这样,您只会在客户端有一点重定向开销和不明显的延迟。至少你会摆脱对file_exists
.
只是一个想法。
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'
创建一个散列例程,将文件分片到多个子目录中。
文件名.jpg -> 012345 -> /01/23/45.jpg
此外,您可以使用 mod_rewrite 将占位符图像返回到 404 的图像目录请求。
file_exists()
由 PHP 自动缓存。我认为您不会在 PHP 中找到更快的函数来检查文件是否存在。
看到这个线程。
老问题,我要在这里添加一个答案。对于 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()。
所以这取决于你的使用场景。
如果您只检查现有的files
,请使用is_file()
.
file_exists()
检查现有文件或目录,因此可能is_file()
会快一点。
我不完全知道你想做什么,但你可以让客户处理它。
它们都在同一个目录中吗?如果是这样,可能值得获取文件列表并将它们存储在哈希中并与之进行比较,而不是所有 file_exists 查找。
如果要检查图像文件是否存在,更快的方法是使用getimagesize!
本地和远程更快!
if(!@GetImageSize($image_path_or_url)) // False means no imagefile
{
// Do something
}
2021 年,也就是问这个问题 12 年后,我有同样的用例。file_exist
在决定显示什么之前,我会循环检查大约 40 张图像。
以毫秒为单位的数字(PHP 7.4):
服务器比开发机器快 10 倍,并且与整体 UX 性能 POV 无法区分,其中 30-50 毫秒是第一个明显的阈值。
在服务器检查 40 张图像的数组时,我花了 0.4 毫秒来检查其中是否有人不存在。顺便说一句,无论某些图像是否存在,性能都没有差异。
file_exist
因此,由于磁盘性能,是否进行检查应该是毫无疑问的。检查是否需要。
怎么样glob()
?但我不确定它是否很快。
我发现每次通话 1/2 毫秒非常非常实惠。我认为没有更快的替代方案,因为文件函数非常接近处理文件操作的较低层。
但是,您可以为 file_exists() 编写一个包装器,将结果缓存到 memcache 或类似设施中。这应该可以将日常使用的时间减少到几乎没有。
您可以执行 cronjob 来定期创建图像列表并将它们存储在 DB/file/BDB/...
每半小时应该没问题,但一定要创建一个接口来重置缓存以防文件添加/删除。
然后,运行 find 也很容易。-mmin -30 -print0 在 shell 上并添加新文件。
当您将文件保存到文件夹时,如果上传成功,您可以将路径存储到 DB Table。
然后,您只需查询数据库即可找到所请求文件的路径。
我来到这个页面寻找解决方案,似乎 fopen 可以解决问题。如果您使用此代码,您可能希望对未找到的文件禁用错误日志记录。
<?php
for ($n=1;$n<100;$n++){
clearstatcache();
$h=@fopen("files.php","r");
if ($h){
echo "F";
fclose($h);
}else{
echo "N";
}
}
?>
我认为最好的方法是将图像 url 保存在数据库中,然后将其放入会话变量中,尤其是在您进行身份验证时。通过这些方式,您不必每次重新加载页面时都进行检查
我什至不确定这是否会更快,但看起来你仍然想对 soooo 进行基准测试:
构建包含所有图像路径的大型数组的缓存。
$array = array('/path/to/file.jpg' => true, '/path/to/file2.gif' => true);
根据您的要求,每小时或每天更新缓存。您将使用cron运行 PHP 脚本来执行此操作,该脚本将递归地遍历 files 目录以生成路径数组。
当你想检查一个文件是否存在时,加载你的缓存数组并做一个简单的isset()检查快速数组索引查找:
if (isset($myCachedArray[$imgpath])) {
// handle display
}
加载缓存仍然会有开销,但希望它足够小以留在内存中。如果您在一个页面上检查多个图像,您可能会注意到更显着的收益,因为您可以在页面加载时加载缓存。