计算在代码中打开的文件大小的最快方法(PHP)

Posted

技术标签:

【中文标题】计算在代码中打开的文件大小的最快方法(PHP)【英文标题】:Fastest way to calculate the size of an file opened inside the code (PHP) 【发布时间】:2011-07-13 05:58:12 【问题描述】:

我知道php 中有很多内置函数可用于获取文件大小,其中一些是:filesize、stat、ftell 等。

我的问题在于ftell,这很有趣,它会从文件中返回文件指针的整数值。

是否可以使用ftell 函数获取文件的大小?如果是,那告诉我怎么做?

场景:

    系统(代码)以“a”模式打开现有文件以附加内容。 文件指针指向行尾。 系统将内容更新到文件中。 系统使用ftell 计算文件大小。

【问题讨论】:

【参考方案1】:

fstat 确定文件大小没有任何杂技:

$f = fopen('file', 'r+');
$stat = fstat($f);
$size = $stat['size'];

ftell 不能在使用 append("a") 标志打开文件时使用。此外,您必须先使用fseek($f, 0, SEEK_END) 查找文件末尾。

【讨论】:

谢谢,fseek 上的信息成功了。我将fseekftell 结合使用,这是最快的。【参考方案2】:

ftell() 可以告诉您文件中应该有多少字节,但不能告诉您实际有多少Sparse files 占用的磁盘空间比寻找到最后并告诉将返回的值更少。

【讨论】:

compressed files 和 inline files 占用的磁盘空间也更少。【参考方案3】:

我写了一个基准来改进这个话题,为了避免人们争论存在某种 php/cache,我在另一个进程中创建了独特的文件。

这是一个我毫无疑问的新基准。

测试忽略 fopen 和 close 时间,因为用户要求最快的方法来计算已打开文件的大小。 每个测试运行 200 个文件。

在单独进程中创建文件的代码是本文的第一条评论。

<?php
class timeIt

    static private $times   = [];
    static function new()
    
        self::$times[] = hrtime(true);
    
    static function stop()
    
        self::$times[] = -1;
    
    static function dif()
    
        $dif    = 0;
        $sum    = 0;
        $i      = count(self::$times) - 1;

        if (self::$times[$i] === -1)
            unset(self::$times[$i--]);
        
        for ($i = count(self::$times) - 1; $i > 0; --$i) 
            if (self::$times[$i - 1] === -1) 
                $sum    += $dif;
                $dif    = 0;
                --$i;
                continue;
            
            $dif    += self::$times[$i] - self::$times[$i - 1];
        
        return $sum + $dif;
    
    static function printNReset()
    
        echo "diffTime:" . self::dif() . "\n\n";
        self::reset();
    
    static function reset()
    
        self::$times    = [];
    

function fseek_size_from_current($handle)

    $current  = ftell($handle);
    fseek($handle, 0, SEEK_END);
    $size   = ftell($handle);
    fseek($handle, $current);
    
    return $size;

function fseek_size_from_start($handle)

    fseek($handle, 0, SEEK_END);
    $size   = ftell($handle);
    fseek($handle, 0);
    
    return $size;


function uniqueProcessId()

    return (string) hrtime(true);


function getUniqueForeignProcessFiles($quantity, $size)

    $returnedFilenames   = $filenames = [];
    while ($quantity--)
        $filename   = uniqueProcessId();
        $filenames[$filename]   = $size;
        $returnedFilenames[]    = __DIR__ . DIRECTORY_SEPARATOR . $filename;
    

    $data       = base64_encode(json_encode($filenames));
    $foreignCgi = __DIR__ . DIRECTORY_SEPARATOR . "createFileByNames.php";
    $command    = "php $foreignCgi $data";
    if (shell_exec($command) !== 'ok')
        die("An error ocurred");

    return $returnedFilenames;

const FILESIZE  = 20 * 1024 * 1024;

foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename)
    $handle = fopen($filename, 'r');
    timeIt::new();
    $size   = fstat($handle)['size'];
    timeIt::new();
    timeIt::stop();
    fclose($handle);
    unlink($filename);

echo "**fstat**\n";
timeIt::printNReset();

foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename)
    $handle = fopen($filename, 'r');
    timeIt::new();
    $size   = fseek_size_from_start($handle);
    timeIt::new();
    timeIt::stop();
    fclose($handle);
    unlink($filename);

echo "**fseek with static/defined**\n";
timeIt::printNReset();


foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename)
    $handle = fopen($filename, 'r');
    timeIt::new();
    $size   = fseek_size_from_current($handle);
    timeIt::new();
    timeIt::stop();
    fclose($handle);
    unlink($filename);

echo "**fseek with current offset**\n";
timeIt::printNReset();


foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename)
    $handle = fopen($filename, 'r');
    timeIt::new();
    $size   = filesize($filename);
    timeIt::new();
    timeIt::stop();
    fclose($handle);
    unlink($filename);

echo "**filesize after fopen**\n";
timeIt::printNReset();

foreach(getUniqueForeignProcessFiles(200, FILESIZE) as $filename)
    timeIt::new();
    $size   = filesize($filename);
    timeIt::new();
    timeIt::stop();
    unlink($filename);

echo "**filesize no fopen**\n";
timeIt::printNReset();

20MB 文件的结果,时间以纳秒为单位

fstat diffTime:2745700

使用静态/定义的 fseek diffTime:1267400

fseek 与当前偏移量 diffTime:983500

fopen 后的文件大小 diffTime:283052500

filesize no fopen diffTime:4259203800

1MB 文件的结果,以纳秒为单位:

fstat diffTime:1490400

使用静态/定义的 fseek diffTime:706800

fseek 与当前偏移量 diffTime:837900

fopen 后的文件大小 diffTime:22763300

filesize no fopen diffTime:216512800

以前这个答案有另一个基准,我删除了算法以让这个答案更清晰。 该算法使用由自己的进程创建的文件,假设是:

ftell + fseekfstat['size']一半 时间,即使在另一个函数内部并且调用这两个函数两次。 fstat 是 速度较慢,因为它包含的信息不仅仅是文件大小, 因此,如果您需要代码旁边的其他信息,请检查 更改,只需坚持 fstat。

当前的基准测试表明该假设是有效的,即: **对于 1-20MB 的文件,fseek + ftell++ 比 fstat 快 2-2.8 倍。

随意运行您的基准测试并分享您的结果。

【讨论】:

DIR . DIRECTORY_SEPARATOR . $fileName, str_repeat('a', $mbyte + $byte)); foreach($files as $filename => $size) createFilled($filename, 0, $size);回声'好的';【参考方案4】:

感谢@Phihag,您在fseekftell 上提供的信息让我能够以更好的方式计算尺寸。在此处查看代码:http://pastebin.com/7XCqu0WR

<?php
$fp = fopen("/tmp/temp.rock", "a+");

fwrite($fp, "This is the contents");

echo "Time taken to calculate the size by filesize function: ";
$t = microtime(true);
$ts1 = filesize("/tmp/temp.rock") . "\n";
echo microtime(true) - $t . "\n";

echo "Time taken to calculate the size by fstat function:";
$t = microtime(true);
$ts1 = fstat($fp) . "\n";
$size = $ts1["size"];
echo microtime(true) - $t . "\n";

echo "Time taken to calculate the size by fseek and ftell function: ";
$t = microtime(true);
fseek($fp, 0, SEEK_END);
$ts2 = ftell($fp) . "\n";
echo microtime(true) - $t . "\n";

fclose($fp);

/**
OUTPUT:

Time taken to calculate the size by filesize function:2.4080276489258E-5
Time taken to calculate the size by fstat function:2.9802322387695E-5
Time taken to calculate the size by fseek and ftell function:1.2874603271484E-5

*/
?>

【讨论】:

@S Rakesh 请注意,这个基准测试完全错误:首先,您测量的东西几乎无法测量。如果有的话,您应该测量 100K+ 运行。 10^-5s 大于某些定时器的分辨率。此外,您不计算fopen 花费的时间。 (顺便说一句,我的建议是不是 filesize,而是fstat,它也在句柄上运行),所以你将苹果与橙子进行比较(访问句柄与路径文件系统)无论如何。此外,您没有考虑缓存。自然,当您调用fseek 时,文件大小已经确定并缓存了。 @Phihag 我同意计时器很长,但我只是想找出计算打开文件大小的最佳方法。我确实运行了 100 个请求的基准。 100个并发级别,我还是觉得ftell是最好的。 @S Rakesh Best 不必等于最快(如果您追求速度,就不会编写 php 代码)。请注意,您仍在以filesizefstat/ftell 的形式比较苹果和橙子。另请注意,结果取决于一般计算机和缓存设置。例如,如果您的 CPU 缓存已经预热并包含 inode 结构,filesize(它必须首先找到 inode)会慢很多。另一方面,如果您处于必须从 HDD 读取文件的 inode(可能包含目录)的设置中,... ...filesize 将比fopen+fseek+ftell 快。总而言之,既然您无论如何都在编写 php,我建议您只在重要的情况下确保代码的清晰性和性能。 我检查了一个更大的集合,结果非常相似:filesize - 43.9655s / fstat - 44.6673s / ftell - 44.5511s

以上是关于计算在代码中打开的文件大小的最快方法(PHP)的主要内容,如果未能解决你的问题,请参考以下文章

在本地复制大文件的最快方法

在不知道文件描述符的情况下检测文件大小不为零的最快方法是啥?

获取 GCS 中文件夹的文件数和总大小的最快方法?

怎么修改php上传文件文件大小限制

怎么修改php上传文件文件大小限制第一次

增长 numpy 数值数组的最快方法