PHP加载大文件时require和file_get_contents的性能对比

在开发过程中发现,用require来加载一个很大(几百K,甚至几兆)的配置文件时,会造成响应超时。如果把这个配置文件的内容序列化后,用file_get_contents获取文件然后反序列化的方法来加载,就会快很多。

经过近两周的研究,大概知道了其中的原因。

首先,还从PHP的流程说起,PHP其实有两个流程,一个是启动的流程,一个是响应请求的流程。PHP作为Apache的一个模块,向Apache注册了两个函数,一个是Aapche启动的时候运行的函数:sapi_startup;一个是Apache接收到请求的时候调用的函数:php_handler

启动的流程:

Apache启动 

    ->  sapi_startup

         -> php_module_startup (PHP启动总开关)

             -> zend_startup (启动Zend引擎,包括初始化全局变量,初始化 compile 和 execute 函数


相应请求的流程:

Apache收到请求

    ->  sapi_startup

         -> zend_activate (包括初始化编译器、初始化执行器、启动扫描器)

             -> zend_compiler (语法分析、语意分析、生成opcode)

                 -> zend_execute (执行每个opcode)

                     -> zend_deactive(清理本次请求用到的数据)

如果遇到 require 或者 include 之类的函数时,会 从 zend_execute 阶段重新回到 zend_compiler 阶段,开始解释PHP,执行PHP的过程。

除了 zend_compiler 和 zend_execute 阶段之外,require 和 file_get_contents 的开销基本是一样的。

而且我们服务器上安装了apc扩展,就是说 zend_compiler 阶段可以认为两者也是一样的。

那他们的性能九差在zend_execute阶段了。

首先,让我们用vld扩展查看一下两个文件生成的opcode的数量,因为这个是execute的输入。

结果显示,require 生成的opcode数量为2万多个,大多是 ADD_ARRAY_ELEMENT,就是构造数据;而file_get_contents生成的opcode只有6个;

然后再来对比执行的效率:

这两个函数的执行可以分成两部分:读取文件和构造配置文件里面的数组;

先说读取文件,require读取的机制是,以8192字节大小的buffer循环将文件读入内存;而file_get_contents使用的是mmap,直接将文件映射到了虚拟内存当中。这样的话,require会比file_get_contents多出大量的系统调用。而file_get_contents无需作这么多用户态和内核态的切换工作。这一步,file_get_contents胜出一筹;

再来看构造数组,require构造的机制是生成2万多个opcode,然后一次执行这些opcode;而file_get_contents使用的是unserialize函数,他对传入的文本进行解析,然后逐级构造成数组。他们构造数组的思路是一样的,但是require每增加一级数据的开销要比unserialize大;这一局也是 file_get_contents 略优;

但是,file_get_contents 在PHP内部是函数调用,而require是一个内置的opcode,所以调用file_get_contents时的开销要比require略大;

所以,小文件的时候,file_get_contents 读取文件时 内存映射的优势发挥不出来,两者部分伯仲;大文件的时候,由于require要2K2K的循环调用read系统调用,就降低了他的性能。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
PHP 中,可以使用 file_get_contents() 函数读取文本文件,但是它不能直接读取 .doc 文件,因为 .doc 文件不是纯文本格式。如果你想读取 .doc 文件,可以使用第三方库来实现。 其中一个常用的库是 PHPWord,它可以帮助你读取和写入 Microsoft Word 文档。你可以使用 Composer 进行安装: ``` composer require phpoffice/phpword ``` 然后可以使用以下代码读取 .doc 文件: ```php require_once 'vendor/autoload.php'; $phpWord = \PhpOffice\PhpWord\IOFactory::load('path/to/document.doc'); // 获取整个文档内容 $fullText = $phpWord->getText(); // 获取第一段内容 $section = $phpWord->getSection(0); $firstParagraph = $section->getElements()[0]; $firstTextRun = $firstParagraph->getElements()[0]; $firstText = $firstTextRun->getText(); ``` 在上面的代码中,我们使用 `PhpOffice\PhpWord\IOFactory::load()` 方法加载 .doc 文件,并使用 `$phpWord->getText()` 方法获取整个文档的文本内容。如果你只想获取第一段内容,可以使用 `$phpWord->getSection(0)` 获取第一段,然后使用 `$section->getElements()[0]` 获取第一个元素,即第一个段落,再使用 `$firstParagraph->getElements()[0]` 获取该段落的第一个元素,即第一个文本运行,最后使用 `$firstTextRun->getText()` 获取文本内容。 需要注意的是,PHPWord 仅支持读取 .docx 格式的文件,如果你需要读取 .doc 格式的文件,需要将其转换为 .docx 格式。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值