PHP exec()性能

Phi*_*ipp 17 php performance runtime exec

以下PHP代码确实返回了大约3.5秒的运行时间(多次测量并取平均值):

$starttime = microtime(true);
exec('/usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg');
$endtime = microtime(true);
$time_taken = $endtime-$starttime;
Run Code Online (Sandbox Code Playgroud)

当我在ssh终端上运行相同的命令时,运行时间减少到大约0.6秒(使用命令行工具测量time).

imagemagick库的版本是

Version: ImageMagick 6.7.0-10 2012-12-18 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2011 ImageMagick Studio LLC
Features: OpenMP
Run Code Online (Sandbox Code Playgroud)

这个时差的原因是什么?

在stackoverflow上对类似问题的一个答案是,开销来自必须启动线程/ shell的Web服务器.这可能是真的吗?我认为线程是轻量级的,并且不需要花很长时间才能启动/终止.

在调用之前exec我设置了imagemagick使用的线程数(因为这是/在OpenMP中的错误?,参考)到1 exec('env MAGICK_THREAD_LIMIT=1');.无论我设置什么值,PHP的运行时都没有太大变化MAGICK_THREAD_LIMIT.无论如何,在这个版本中OpenMP上似乎没有错误,因为命令行执行的运行时是可以的.

任何关于如何改进上述命令的运行时间的建议将不胜感激.

非常感谢您的帮助.

Gus*_*ram 12

当您通过键盘或ssh登录到Unix机器时,您将创建一个shell的新实例.shell通常类似于/bin/sh/bin/bash.shell允许您执行命令.

使用时exec(),它还会创建一个shell的新实例.该实例执行您发送给它的命令,然后退出.

当您创建shell命令的新实例时,它具有自己的环境变量.所以,如果你这样做:

exec('env MAGICK_THREAD_LIMIT=1');
exec('/usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg');
Run Code Online (Sandbox Code Playgroud)

然后创建两个shell,第一个shell中的设置永远不会到达第二个shell.要在第二个shell中获取环境变量,您需要这样的东西:

exec('env MAGICK_THREAD_LIMIT=1; /usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg');
Run Code Online (Sandbox Code Playgroud)

现在,如果您认为shell本身可能是问题,因为制作shell需要很长时间,请使用您知道几乎没有时间的东西进行测试:

$starttime = microtime(true);
exec('echo hi');
$endtime = microtime(true);
$time_taken = $endtime-$starttime;
Run Code Online (Sandbox Code Playgroud)

此时,您知道尝试找到一些方法来使shell更快地实例化.

希望这可以帮助!


小智 8

我已经编程了超过56年的计算机,但这是我第一次遇到这样的bug.所以我花了将近一个星期的时间试图理解从php通过exec执行perl程序而不是直接在命令行执行perl程序时执行速度更快的7倍.作为这项工作的一部分,我也一直在网上提出这个问题.这是我发现的:(1)这是一个在2002年首次报道的错误,并且在随后的11年中没有得到修复.(2)该bug与apache与php交互的方式有关,因此这两个组织都将其转移到另一个组织.(3)执行,系统或任何替代方案的错误都是相同的.(4)错误不取决于所执行的程序是perl,exe还是其他.(5)UNIX和Windows上的错误是相同的.(6)该bug与imagemagick或一般的图像无关.我在一个完全不同的环境中遇到了这个bug.(7)该bug与fork,shell,bash等的启动时间无关.(8)通过更改apache服务的所有者不修复错误.(9)我不确定,但我认为这与调用子程序的开销大大增加有关.当我遇到这个问题时,我有一个可以在40秒内执行的perl程序,但通过exec需要304秒.我的最终解决方案是弄清楚如何优化我的程序,使其在0.5秒内直接执行或在3.5秒内通过exec执行.所以我从来没有解决过这个问题.

  • 终于有人了!!...当每个人都不断用“必须启动 shell”来解释这种现象时,时间显然是成比例的,而不只是“0.5 秒加上相同的时间”,这真是太烦人了。 shell 启动开销情况。现在,使用文件重定向我设法获得了一些速度(例如 dir > some.txt 然后从 php 读取文件),但它仍然不够好。如果您找到真正的答案,请告诉我! (2认同)

Mih*_*ncu 5

@Philipp 因为你有 SSH 并且你的服务器允许访问exec()我假设你也有对机器的完全 root 访问权限。

推荐用于单个文件处理

拥有对机器的 root 访问权限意味着您可以更改/etc/php5/php.ini内存限制设置。

即使没有直接访问/etc/php5/php.ini您,也可以php.ini通过php.ini在您的项目目录中创建一个新文件来检查您的服务器是否支持覆盖指令。

即使不允许覆盖,您也可以从.htaccessif AllowOverrideis更改您的内存设置All

另一种更改内存限制的方法是在 PHP 运行时使用ini_set('memory_limit', 256);.

推荐用于批处理文件

运行 convert via 的唯一好处exec()是,如果您不打算从中获取结果exec()并允许它异步运行:

exec('convert --your-convert-options > /dev/null 2>/dev/null &');
Run Code Online (Sandbox Code Playgroud)

如果您尝试批量处理许多文件,并且不想等待它们完成处理并且不需要确认每个已处理的文件,则上述方法通常很有用。

性能说明

使用上面的代码exec运行async处理单个文件将比在 PHP 中使用 GD/Imagick 花费更多的处理器时间和更多的内存。时间/内存将被不同的进程使用,不会影响 PHP 进程(使访问者感觉站点移动得更快),但内存消耗是存在的,并且在处理许多连接时会很重要。


Abu*_*ayd 5

这不是 PHP 错误,与 Apache/Nginx 或任何网络服务器无关。

我最近遇到了同样的问题,并查看了 PHP 源代码来检查 exec() 实现。

本质上,PHP 的 exec() 调用 Libc 的 popen() 函数。

这里的罪魁祸首是 C 的 popen(),它看起来很慢。在谷歌上快速搜索“c popen Slow”会向您显示很多像您这样的问题。

我还发现有人用 C 实现了一个名为 popen_noshell() 的函数来克服这个性能问题:

https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/

下面的屏幕截图显示了 popen() 和 popen_noshell() 的速度差异:

速度差

PHP 的 exec() 使用常规的 popen() - 上面屏幕截图右侧的那个。可以看到,执行C的popen()时系统使用的CPU非常高。

我看到这个问题有两种解决方案:

  1. 创建一个实现popen_noshell的 PHP 扩展
  2. PHP 团队请求创建一组新的函数 popen_noshell()、exec_noshell() 等...我想这不太可能发生...

附加说明:

在搜索这个时,我发现了与 C 函数同名的 PHP 函数:popen()

这很有趣,因为可以通过以下方式异步执行外部命令: pclose(popen('your command', 'r'));

本质上与 exec('your command &'); 具有相同的效果