PHP是否会将连续的fseek命令静默地优化为一个fseek命令?

7
我正在使用Windows 7 - 64位系统,最新的XAMPP版本带有32位PHP版本。
在测试一个非常大的文件(大于PHP_MAX_INT 2147483647)时,通过 http://php.net/manual/en/function.fseek.php#112647,我现在相当确定,在执行文件指针之前,后续的fseeks会被累加起来。
我有两个问题:
  1. 我能用合理的方法打破这个累计吗(或只能使用上面链接中提到的解决方法)?
  2. 这个聚合是在PHP中发生的(正如我所认为的,但我不知道在PHP的哪里),还是在Windows 7中发生的?
回答自己:在我的系统上,尝试两个多重查找的解决方法都没有起作用。相反,它们将文件指针设置为 PHP_MAX_INT 以下的不同位置。 (仅限32位PHP可查找到PHP_MAX_INT + 8192。从那里读取仍然是可能的,但我不知道有多远。)因此,在我的特定情况下,问题已经过时了,因为无论你做什么,32位PHP只能查找到PHP_MAX_INT + 8192。我留下这个问题,因为有两个人投票支持,可能对通用答案感兴趣。
我在这里提交了一个错误报告:
https://bugs.php.net/bug.php?id=69213
结果:使用64位PHP构建可能会起作用,但我没有尝试过。

注意:我没有像其他人建议的那样尝试使用SplFileObject,因为我相信php手册,SplFileObject应该只是通常的seek等命令上面的一层。而且我已经针对这个特定任务迁移到了perl。(即使php bug方面的人也理解了这一点。)只有当64位php构建成为主流时,我才会部署它。而且我不会完全迁移到perl来读写几个非常大的文件。(我很难看到SplFileObject在概念上有任何优势。虽然维护者被称为“热情洋溢”。) - John
2个回答

1
它并没有。实际上它做了一些更愚蠢的事情。以下是来自PHP源代码的片段:
      switch(whence) {
          case SEEK_CUR:
              offset = stream->position + offset;
              whence = SEEK_SET;
              break;
      }

这是 PHP 的 fseek 实现细节。这里发生的情况是:如果您告诉 PHP 从当前位置开始查找,它会将其转换为从文件开头进行“等效”查找。只有在偏移计算不会溢出时才起作用;如果溢出了,那么 offset 是一个带符号整数,因此行为未定义。
好吧,这是因为 PHP 在内部缓冲流,所以它们需要做一些事情。但它不一定非得这样做。
您最好尝试使用实际执行您要求的语言来完成您的工作。

这不应该是个问题,因为读写点的改变会被减少到相对于当前位置的偏移,并且磁盘系统不会执行不必要的寻道操作;它只会将磁头定位到需要读写的位置。在这个意义上,仅进行寻道而没有读写就只是改变有符号整数。 - Rob_vH
1
摘下你的内核着色眼镜。这些语义意味着即使文件系统和操作系统支持,也没有PHP程序可以在文件中寻找超过ZEND_LONG_MAX字节。因为PHP对SEEK_CUR的实现在正式的C标准意义上包含未定义的行为。在这种情况下,PHP将不会按照程序员告诉它要做的去执行,这是不可接受的 - Alex
@Alex:这符合我通过尝试不同的目标查找值所发现的内容(请参见我链接的PHP错误报告中的最后一条)。因此,我接受了这个答案。由于存在错误(或旧代码),如果其他级别存在其他优化或错误,则目前对最终用户没有任何效果,因为这些其他级别永远不会得到假定的值。我认为应该是这样。我不太懂这个。正如所写的那样,我使用perl来处理单个大文件任务。:) 也许您可以编写一个带有您发现的代码行的错误报告,并且可能会得到改进。 :) - John
@Alex:顺便说一下,32位的PHP只承诺在其能够工作的范围内工作。 :) 我很好奇在php手册网页上发布的链接不起作用的解决方法为什么会获得这么多赞。也许它在以前的版本中有效。或者当PHP与其他编译器一起编译时。- 无论如何,这太困难了。再加上在Windows上编写和读取UTF文件名的问题,PHP对我失去了一些吸引力。 - John
1
@Alex 我的答案是基于对PHP源代码的阅读,没有考虑除此之外的因素,除了磁盘系统会优化掉不必要的磁盘寻道。 ZEND_LONG_MAX 是一个长整型,在64位系统上将是64位的。同样地,正如我在我的答案中指出的那样,在PHP源代码中,zend_fseek映射到seek或_seeki64,具体取决于系统。因此,PHP应该能够寻找虚拟内存空间中的任何位置,从而寻找操作系统可以寻找的任何文件位置。 - Rob_vH

0

如果聚合发生,可能需要作为操作码优化或通过缓冲区在低级别上发生。

我可以在低级别上回答。在php中,fseek()是使用php流实现的。它在ext/standard/file.h中声明,在.c中定义。它的实现调用php_stream_seek(),后者调用streams.c中的_php_stream_seek()。这个的底层实现是通过普通流包装器处理的,在这种情况下,寻求调用zend_seek或zend_fseek,它们又映射到32位或64位seek_seeki64 c调用。

所以...如果有任何聚合发生,似乎必须在操作码优化甚至更低层次的操作系统或硬件中进行。硬盘实现乱序获取以减少磁头寻道距离,文件系统缓冲系统可能能够减少没有副作用的寻道。如果您担心磁盘读取时间,第一个自动处理此问题。如果您担心可能会使内存崩溃(在缓冲区中不必要地寻找很远的距离),则可以考虑另一种方法。请参见:http://www.cs.iit.edu/~cs561/cs450/disksched/disksched.html 了解有关磁盘如何避免浪费寻道时间的更多信息。

希望这可以帮助到您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接