PHP:如何编写高性能脚本
PHP 很可能是一种类似脚本的语言,并因速度慢而受到批评,但它仍然是使用最广泛的 Web 服务器语言之一。 随着时间的推移,使用 PHP 的主要网络公司都在寻求优化引擎。 概念的引入 垃圾收集器 在 5.3 版中是一个显着的进步。 但是有很多方法可以'优化脚本的性能.
主要用在简单页面生成的场景下,这些优化虽然能带来一定的收益,但对用户来说不够明显。 使用 Nginx 优化数据库、Web 服务器,使用 HHVM 甚至 HippyVM 优化引擎将使您能够简单地加速页面渲染并以更简单的方式优化对查询的回答时间. 尽管如此,我们有时开发 PHP 脚本的目的是 重度治疗,或者网络服务器不能做任何事情。
在这篇文章中,我将详细介绍 三个优化领域 我最近将其应用于必须处理包含大量信息的 CSV 或 XLS 文件的脚本。 内存使用量达到1GB无忧,可持续使用1/2小时以上。
在我向您介绍可以让您执行以下操作的三个 PHP 概念之前优化执行时间 以及占用的内存量,知道在抱怨 X 语言之前,脚本的算法负责处理的很大一部分。 C++ 可能比 PHP 快无限,但如果你的 C++ 算法不好,它不会立即解决你的问题。
释放其变量,限制内存消耗
在 PHP 5.3 发布之前,PHP 的问题是 过多的内存消耗. 此外,我经常听说 PHP 脚本的内存消耗永远无法减少……如果这在一段时间内是正确的,幸运的是它不再是正确的。 这种管理使用了我们将在后面看到的垃圾收集器的概念。
一个好习惯丢了...
在某些语言中,这是强制性的。 在 PHP 中,这个概念已经完全被遗忘了! 这个小的本机函数 unset() 是为了强大的实用程序. 它等同于 C++ 中的 free() 函数并允许 解除分配 因此 立即释放内存 被变量使用。 变量被完全销毁。 这最初将 PHP 从未使用的变量中释放出来。 这还有另一个好处,可以帮助我们更好地构建算法。 当然,当一个方法结束时,变量会自动释放,但你会在下面看到,这允许你优化“垃圾收集器”工作的时间,或者在它被停用的情况下简单地完成它的工作。 因此也有 速度方面的进步. 相信我,GC 会消耗大量资源来释放内存。
正如我在上一段中所说,在函数或方法的末尾,PHP 会删除未使用的变量。 但是在脚本处理大量数据的情况下,一个函数可以管理其他函数。 与某些语言一样,如果它是一个主函数,您可能倾向于在将变量传递给其他函数之前将变量从函数中存储回来。 您很快就会得到大量数据。 一旦不再使用,“随身携带”就没有意义了, 我们取消分配它.
最近,在编写一个脚本时,我正在提取一个大于 15 MB 的整个文件,一旦我使用了它,我就删除了它并允许 获得记忆 !
了解垃圾收集器
正是在研究内存管理时,我偶然发现了一位同事的文章。 在这篇现在有点旧的文章中,Pascal Martin 解释了不再是一个的新奇事物,即垃圾收集器。
什么是垃圾收集器?
对于那些不知道或听说过但从未有机会使用它的人来说,垃圾收集器在法语中被翻译为“垃圾收集器”, 低级功能 它在时间间隔 X 停止正在运行的进程,并对脚本分配的内存执行扫描,以检测哪些变量不再可访问以将其删除。
我不明白,早些时候有人告诉我,在方法结束时 PHP 会破坏变量。
这不完全正确。 与 C 或 C++(PHP 的父级)一样,在方法结束时,PHP 退出并丢失它对对象的引用。 在C中,只有指针的概念存在,在C++中,指针和引用的概念并存。
一旦对该对象的引用丢失,一旦垃圾收集器启动,后者将确定该变量不再可访问并将其销毁。
这个概念在 JAVA 以及其他更新的语言中非常存在,因此具有更高的层次。
垃圾收集器带来了什么?
GC 提供了相当大的开发灵活性。 很明显,用 C 开发比用 Java、PHP 或其他语言开发更具技术性。 尽管如此,有些方面现在已经被遗忘,例如内存管理,有时我们的代码会引起共鸣。 如果这为我们的开发带来了灵活性,它也会减慢我们程序的执行速度。
有了这些工具,我们不能忘记的是,我们总能以某种方式控制内存。 意志的问题,还是必要的……
最后,我认为这类工具非常实用,但不能代替开发者给出的说明。 就我而言,我发现 C++ 的概念是 shared_ptr
更有趣,如果 Zend 决定在某些情况下使用这种方法,将来可以提供巨大的性能提升。
在 PHP 中?
从 PHP 5.3 开始,添加了四个函数。
- gc_enable
- gc_enabled
- gc_disable
- gc_collect_cycles
我留给您阅读文档的空闲时间,但为了快速起见,它们允许您激活 GC,知道它是否处于活动状态,停用它并手动启动收集。
Pascal Martin 在我上面链接到您的帖子中发布了一个脚本,他在其中执行电池测试。 我们在测试报告中可以清楚的看到,没有GC的版本内存消耗巨大,甚至达到极限而崩溃。
基准
这是执行的代码。 这摘自 Pascal Martin 的博客,尤其是他的文章。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
程 Node {
国家 $父节点;
国家 $childNodes = 排列();
功能 Node() {
$这->nodeValue = str_repeat(“0123456789”, 128);
}
}
功能 创建关系() {
$父母= 新 节点();
$孩子= 新 节点();
$parent->childNodes[] = $child;
$child->parentNode = $parent;
}
用于 ($i = 0; $我 500000; $i++) {
创建关系();
// 这部分在第二次测试时执行
if ($选项[“gc_manual”]) {
gc_collect_cycles();
}
}
|
我分别进行三个测试:
- 我使用基本选项执行此测试。 垃圾收集器已启用,PHP 会定期运行它。 脚本有持续时间 7.55秒 并且使用的内存已经达到 莫20.98.
- 我通过禁用垃圾收集器并在每次循环时调用它来执行相同的测试。 剧本时长3.79秒 并且使用的内存达到峰值 柯244.77.
- 我通过禁用垃圾收集器并且从不手动收集来执行第三个测试。 因此,内存必须强烈填满。 剧本时长4.46秒 并且内存已经达到 围棋1.98.
通过这个测试,我们可以清楚地看到内存管理的重要性。 在我们这个极端的例子中,我们可以清楚地看到它的重要性。 一方面,垃圾收集器在内存消耗方面做了很多工作,但这往往会减慢脚本的执行速度。 通过比较第一个测试(使用 GC)和第三个没有管理的测试,我们可以很好地看到它。
我们的性能工作是在测试 2 上完成的。没有自动内存管理,在此脚本中优化了手动管理。 我们设法将处理速度提高了近两倍 (7.55 秒/3.79 秒 = 1.99)。 这样, 我们还将内存限制为 245 KB 针对 21 MB 的自动管理。 这是一个几乎为 88 的系数 ((20.98 * 1024) / 244.77 = 87.77)。
总结
尽管上面的示例将内存管理推向了极限,但此测试旨在向我们展示它的极限。 在我们的脚本中研究内存管理很重要. 在某些情况下,支出可能会令人印象深刻。 处理时间、内存以及与之相关的一切都可以节省下来。
理解和使用参考
正如我之前告诉您的,PHP 通过引用实现变量传递,并且正如我在上面告诉您的那样,这种类型的传递或多或少等同于通过指针传递。
- 了解按引用传递
- 了解指针传递
这是一个重要的部分,不仅对于 PHP 因为这个概念很久以前就存在了,而且对于 IT 也是如此。 基本上,当你声明一个函数时,这段话是通过复制来完成的。// 通过公共复制函数 foo($arg) {...}
这意味着 一些优点和缺点 取决于你的代码。 对象被复制,这意味着分配的内存将增加传递对象的权重。 如果你传递一个布尔值,这可能很荒谬,但如果你传递一个数组,这可能更重要。
1
2
3
4
5
6
7
8
9
10
11
|
国家 功能 FOO() {
$a = 1;
酒吧($一);
回音 $一个; //$a 为 1
}
国家 功能 酒吧($arg) {
$参数= 2;
}
|
在这种情况下,我们看到该值没有被修改,如果我们使用变量 $a 作为基础并且我们不想触及它的值,这可能会很有趣。 我们可以在这里谈论一个在 PHP 中不存在的概念,常量变量 (const)。
1
2
3
4
5
6
7
8
9
10
11
12
|
国家 功能 FOO() {
$a = 1;
$a = bar($a);
回音 $一个; //$a 为 2
}
国家 功能 酒吧($arg) {
$参数= 2;
回报 $参数;
}
|
我确定您已经编写过这样的方法。 在这种情况下,这显然是一个错误。 当然,语法是完全正确的,但是已经为参数的副本进行了分配,并且已经进行了变量的释放。 昂贵的内存调用和不必要的处理时间。 下面的表格是等效的,但速度要快得多。
1
2
3
4
5
6
7
8
9
10
11
|
国家 功能 FOO() {
$a = 1;
酒吧($一);
回音 $一个; //$a 为 2
}
国家 功能 酒吧(&$参数) {
$参数= 2;
}
|
在这里,我们进行了完全相同的处理,但它更快、消耗更少。
这种优化执行起来非常简单,甚至可以使您的代码更易于阅读。 有几种语法可以用来进行引用传递。
- 通过引用传递参数 (我们刚刚看到的)
- 函数按引用返回 (此处不详述,因为PHP引擎对这部分进行了优化,我并没有感觉到有什么令人信服的收获)
- 通过引用复制变量,不是很有用但很强大。 我让您阅读非常详细的 PHP 文档。 你可以很容易地学到我遗漏的东西🙂
其他优化
由于上面的三个优化,你应该没有问题加速你的处理。 但是,还有其他项目。 我列出了有关您可以执行的不同优化的有趣文章。
- PHP 字符串连接与数组内爆
总结
我刚刚向您详细介绍的三个部分构成了一个很好的选择,可以根据我的口味优化其脚本并避免重新开始开发 更快的语言 作为一种编译语言。
我在我的一个脚本上做了这三遍,除此之外,我设法做到了 内存消耗减少 3 大约和 实现速度增益 2.