这次优化将时间进一步缩短到 17 分钟 30 秒,节省了大约 2% 的时间 , 虽然这个提升并不是非常显著 。
执行类型转换PHP是一种动态类型语言,我在编程初期非常欣赏它这一特点,因为它简化了许多问题 。然而,另一方面,明确变量类型能帮助解释引擎更高效地执行代码 。
$temp = (float)substr($data, $pos + 1, -1);
令人惊讶的是,这个简单的类型转换把脚本执行时间缩短至 13 分钟 32 秒,性能提升达到了惊人的 **21% **!
文章插图
18 | $station = &$stations[$city];| // ...23 | } elseif ($temp > $station[1]) {
在优化后,第 18 行显示数组访问的 CPU 时间消耗从 11% 减少,这是因为减少了在 PHP 的哈希映射(关联数组的底层数据结构)中搜索键的次数 。第 23 行的 CPU 时间从约 32% 减少到约 15% 。这是因为避免了类型转换的开销 。在优化之前 , $temp、$station[0]和$station[1]是字符串类型,因此 PHP 在每次比较时必须将它们转换为浮点数 。
引入 JIT在优化过程中,我还尝试启用了 PHP 的 JIT(即时编译器) , 它是 OPCache 的一部分 。默认情况下,OPCache 在 CLI(命令行界面)模式下被禁用 , 因此需通过将opcache.enable_cli 设置为 on来启用 。此外 , 虽然JIT默认为开启状态,但由于缓冲区大小默认设置为0,实际上处于禁用状态 。通过将opcache.jit-buffer-size设置为10M,我有效地启用了 JIT 。
启用 JIT 后,脚本执行时间惊人地缩减至 7 分钟 19 秒,速度提升了 45.9% 。
进一步优化通过这系列优化 , 我将脚本的执行时间从最初的 25 分钟大幅降低到了约 7 分钟 。在这个过程中,我注意到使用fgets()读取一个 13GB 的文件时,竟然分配了大约 56GiB 每分钟的 RAM,这显然是不合理的 。经过调查,我发现省略fgets()的长度参数可以大量减少内存分配:
while ($data = https://www.isolves.com/it/cxkf/yy/php/2024-04-23/fgets($fp)) {// 替代之前的while ($data = fgets($fp, 999)) {
这个简单变化虽然只使性能提高了约 1%,但将内存分配从每分钟 56GiB 降至每分钟 6GiB,显著减少了内存占用 。这一改进虽然对执行时间影响不大,但减少内存消耗对于大规模数据处理仍然是一个重要的优化方向 。以上优化展示了在 PHP 性能调优中考虑各种因素的重要性,包括代码逻辑优化、类型明确、JIT编译以及内存管理等,共同作用下可以显著提升应用性能 。
还能更快吗?到目前为止,我使用的单线程方法,与许多PHP程序默认的单线程方式相符,但通过使用parallel 扩展 , PHP 实际上能在用户空间内实现多线程操作 。
性能分析明确指出,在 PHP 中进行数据读取成为了性能瓶颈 。虽然从 fgetcsv() 切换到 fgets() 并手动进行字符串分割有所改进 , 但这种方式仍旧相对耗时 。因此,我们考虑采用多线程的方式来并行地读取和处理数据 , 并在之后将各个工作线程的中间结果合并起来 。
<?php$file = 'measurements.txt';$threads_cnt = 16;/** * 计算并返回每个线程应处理的文件块的起始和结束位置 。* 这些位置将基于 n 字符进行对齐,因为我们使用 `fgets()` 进行读取 , * 它会读取直到遇到 n 字符为止 。* * @return array<int, array{0: int, 1: int}> */function get_file_chunks(string $file, int $cpu_count): array {$size = filesize($file);if ($cpu_count == 1) {$chunk_size = $size;} else {$chunk_size = (int) ($size / $cpu_count);}$fp = fopen($file, 'rb');$chunks = [];$chunk_start = 0;while ($chunk_start < $size) {$chunk_end = min($size, $chunk_start + $chunk_size);if ($chunk_end < $size) {fseek($fp, $chunk_end);fgets($fp); // 将文件指针移动到下一个 n 字符$chunk_end = ftell($fp);}$chunks[] = [$chunk_start,$chunk_end];$chunk_start = $chunk_end;}fclose($fp);return $chunks;}/** * 该函数负责打开指定的 `$file` 文件,并从 `$chunk_start` 开始读取处理数据 , * 直到达到 `$chunk_end` 。* * 返回的结果数组以城市名作为键 , 其值为一个数组,包含最低温度(键 0)、最高温度(键 1)、 * 温度总和(键 2)及温度计数(键 3) 。* * @return array<string, array{0: float, 1: float, 2: float, 3: int}> */$process_chunk = function (string $file, int $chunk_start, int $chunk_end): array {$stations = [];$fp = fopen($file, 'rb');fseek($fp, $chunk_start);while ($data = https://www.isolves.com/it/cxkf/yy/php/2024-04-23/fgets($fp)) {$chunk_start += strlen($data);if ($chunk_start > $chunk_end) {break;}$pos2 = strpos($data, ';');$city = substr($data, 0, $pos2);$temp = (float)substr($data, $pos2 + 1, -1);if (isset($stations[$city])) {$station = &$stations[$city];$station[3]++;$station[2] += $temp;if ($temp
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- MongoDB索引使用总结
- 皮衣染色了用什么可以消除 白色皮衣染色了处理的最好办法
- 冬笋如何处理剥壳 冬笋如何处理剥
- 敌百虫的作用,鱼缸敌百杀虫剂使用方法
- 制作火漆印章有危害么 火漆印章使用方法
- 吸油纸怎么正确使用 油烟机吸油纸怎么正确使用
- 洗手间臭味大怎么处理 洗手间臭味大的处理方法
- 离谱!尼古拉斯.凯奇非婚儿子殴打亲妈,还曾因家暴被处理
- 过期未拆封的食用油可以吃吗 过期未开封食用油怎么处理
- Excel要咋得才可以用宏,excel表格批次管理怎么使用宏