计算在代码中打开的文件大小的最快方法(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
上的信息成功了。我将fseek
与ftell
结合使用,这是最快的。【参考方案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 + fseek 是 fstat['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,您在fseek
和ftell
上提供的信息让我能够以更好的方式计算尺寸。在此处查看代码: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 代码)。请注意,您仍在以filesize
到fstat
/ftell
的形式比较苹果和橙子。另请注意,结果取决于一般计算机和缓存设置。例如,如果您的 CPU 缓存已经预热并包含 inode 结构,filesize
(它必须首先找到 inode)会慢很多。另一方面,如果您处于必须从 HDD 读取文件的 inode(可能包含目录)的设置中,...
...filesize
将比fopen+fseek+ftell
快。总而言之,既然您无论如何都在编写 php,我建议您只在重要的情况下确保代码的清晰性和性能。
我检查了一个更大的集合,结果非常相似:filesize - 43.9655s / fstat - 44.6673s / ftell - 44.5511s以上是关于计算在代码中打开的文件大小的最快方法(PHP)的主要内容,如果未能解决你的问题,请参考以下文章