在我们网站上显示图片时,我们使用 file_exists()
来检查文件是否存在。如果文件不存在,我们将使用一个虚拟图片。
然而,分析表明这是生成我们的页面最慢的部分之一,file_exists()
每个文件需要 1/2 毫秒 的时间。虽然我们只测试了大约40个文件,但这仍然使页面加载时间增加了20毫秒。
有谁能想出一种更快的方法吗? 是否有更好的方法来测试文件是否存在?如果我建立一个缓存,如何保持它同步。
在我们网站上显示图片时,我们使用 file_exists()
来检查文件是否存在。如果文件不存在,我们将使用一个虚拟图片。
然而,分析表明这是生成我们的页面最慢的部分之一,file_exists()
每个文件需要 1/2 毫秒 的时间。虽然我们只测试了大约40个文件,但这仍然使页面加载时间增加了20毫秒。
有谁能想出一种更快的方法吗? 是否有更好的方法来测试文件是否存在?如果我建立一个缓存,如何保持它同步。
file_exists()
应该是一个非常廉价的操作。请注意,file_exists
会构建自己的缓存以提高性能。
请使用绝对路径!根据您的include_path
设置,如果您使用相对文件路径,则PHP会检查所有这些目录!在检查存在性之前,您可能需要临时取消设置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)
版权所有 (c) 1997-2013 PHP Group
Zend引擎 v2.4.0,版权所有 (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'
关于这个老问题,我来添加一个答案。对于php 5.3.8版本,is_file()函数(检查文件是否存在)的速度快了一个数量级。对于不存在的文件,时间几乎相同。对于安装了eaccelerator的PHP 5.1版本,它们的速度更接近。
PHP 5.3.8 带 & 不带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搭配eaccelerator
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倍。同样,这个服务器上有eaccelerator,所以你的情况可能不一样。
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)
)
is_file()
。你会看到明显的收益。如果文件随时可能是文件或符号链接,则失败的is_file() || is_link()
需要14倍的时间,总体上会变慢2倍。如果文件的存在经常发生变化,则使用stream_resolve_include_path()。文件
,请使用is_file()
。file_exists()
检查现有的文件或目录,因此is_file()
可能会更快一些。