Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: file_exists vs stream_resolve_include_path - What Performs Better?

It seems as of late there has been a fair amount of wondering on by php developers on whether it is better to use file_exists() or stream_resolve_include_path() when doing checks of whether or not a file exists (be it for including them, caching systems, etc).

It got me wondering if anybody out there has done any benchmark testing on which of these is a better option, for both page load time, server performance and memory usage.

I could not find anything here at SO that addressed this issue so figured it would be time for us to do so.

like image 861
Abela Avatar asked Oct 11 '13 19:10

Abela


1 Answers

I have done a little benchmark, but before results, let's see how these functions work. You can read the PHP source code here. There is a french version of this answer, written earlier in the week, good timing ;).

I will talk about is_file() too, as it is defined into the same core function in the source. By core function, I say the C source, not accessible from PHP language into your scripts.

Of what I understand, file_exists() and is_file() are children of the core function php_stat(). This is the highly simplified pseudo-code of the process:

function php_stat($file)
{
    'file_exists'
        ↳ virtual_file_ex($file)
            ↳ virtual_access($file)
                'Windows'
                    ↳ tsrm_win32_access($file)
                        ↳ return access($file)
                'Other systems'
                    ↳ return access($file)
    'is_file'
        ↳ return $file.st_mode == S_IFREG
}

And the pseudo-code of the stream_resolve_include_path() process:

function stream_resolve_include_path($file)
{
    zend_resolve_path($file)
        ↳ php_resolve_path_for_zend($file)
            ↳ php_resolve_path($file)
                ↳ tsrm_realpath($file)
                    ↳ return estrdup($file)
}

From here, without numeric result of a benchmark, you can see how one function is expensive in resource.


The code for the benchmark:

function bench_file($file) {
    $res = array();
    $max = 1000000;

    // is_file()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( is_file($file) ) {
            //
        }
    }
    $res[] = microtime(1);

    clearstatcache();

    // file_exists()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( file_exists($file) ) {
            //
        }
    }
    $res[] = microtime(1);

    clearstatcache();

    // stream_resolve_include_path()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( stream_resolve_include_path($file) !== false ) {
            //
        }
    }
    $res[] = microtime(1);

    printf(
        'is_file = %f, file_exists = %f, stream_resolve_include_path = %f',
        $res[1] - $res[0], $res[3] - $res[2], $res[5] - $res[4]
    );

}

Let's test with an existante file (1) and a inexistant one (2):

1 : is_file = 0.218582, file_exists = 0.742195, stream_resolve_include_path = 1.626521
2 : is_file = 0.458983, file_exists = 0.644638, stream_resolve_include_path = 5.623289

Results speak for themselves ;)


Benchmark v2 - just an easier way to add new functions to test.

function micro($func, $file) {
    $max = 1000000;
    $start = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( $func($file) ) {
            //
        }
    }
    $end = microtime(1);
    clearstatcache();
    return $end - $start;
}

function bench_file($file) {
    $res = array(
        'is_file' => micro('is_file', $file),
        'file_exists' => micro('file_exists', $file),
        'stream_resolve_include_path' => micro('stream_resolve_include_path', $file)
    );
    $ret = '';
    foreach ( $res as $key => $value ) {
        $ret .= sprintf('%s = %f, ', $key, $value);
    }
    return trim($ret, ', ');
}

echo '<pre>', bench_file('file-ok'), "\n", bench_file('file-ko'), '</pre>';

Results:

is_file = 0.295752, file_exists = 0.852082, stream_resolve_include_path = 1.759607
is_file = 0.527770, file_exists = 0.724793, stream_resolve_include_path = 5.916151

There is a little cost to call $funct(), this explains the slightly higher numbers.

like image 153
Tiger-222 Avatar answered Oct 31 '22 14:10

Tiger-222