【问题标题】:PHP exec() performancePHP exec() 性能
【发布时间】:2012-12-26 00:29:09
【问题描述】:

以下 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;

当我在 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

这个时差的原因是什么?

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

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

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

非常感谢您的帮助。

【问题讨论】:

  • 我不知道你是否知道,但你的 PHP 版本很可能有 ImageMagick 扩展:php.net/imagick
  • 我通过命令行运行它的原因是因为 php.ini 内存限制。考虑到运行时,使用 ImageMagick 扩展是否更好?
  • @nkr PHP::GD 作为默认 Web 服务器配置比 PHP::imagick 更常见。
  • @MihaiStancu:我听说 GD 提供的图像质量很差,因为@Philipp 说他尝试通过exec 使用 ImageMagick,他应该尝试将它与 PHP 扩展一起使用。但总的来说,你是对的,GD 更频繁地可用。

标签: php performance runtime exec


【解决方案1】:

我从事计算机编程已超过 56 年,但这是我第一次遇到这样的错误。因此,我花了将近一周的时间试图了解从php 通过exec 执行perl 程序与直接在命令行执行perl 程序相比,执行速度要差7 倍。作为这项工作的一部分,我还仔细研究了这个问题在网络上被提出的所有时间。这是我发现的:

(1) 这是 2002 年首次报告的错误,在随后的 11 年中未得到修复。

(2) 该错误与apachephp 交互的方式有关,因此这两个组织都将责任推给了对方。

(3) exec、system 或任何替代品上的错误相同。

(4) 该错误与执行程序是否为perlexe 或其他无关。

(5) UNIX 和 Windows 上的 bug 相同。

(6) 该错误与imagemagick 或一般图像无关。我在完全不同的设置中遇到了这个错误。

(7) 该错误与 fork、shell、bash 等的启动时间无关。

(8) 更改apache 服务的所有者并不能修复该错误。

(9) 我不确定,但我认为这与调用子例程的开销大大增加有关。

当我遇到这个问题时,我有一个 perl 程序将在 40 秒内执行,但通过 exec 需要 304 秒。我的最终解决方案是弄清楚如何优化我的程序,使其在 0.5 秒内直接执行或通过exec 在 3.5 秒内执行。所以我从来没有解决过这个问题。

【讨论】:

  • 终于有人了!!...当每个人都用“必须启动一个shell”来解释这种现象时,时间显然是成比例的,而不仅仅是“0.5秒加上相同的时间”,这真是太烦人了",就像在 shell-starting-overhead 的情况下一样。现在,使用文件重定向我设法获得了一些速度(比如 dir > something.txt,然后从 php 读取文件),但它仍然不够好。如果您找到真正的答案,请告诉我!
  • @dkellner 看看这个答案:stackoverflow.com/a/48505455/1098534。它适用于我的情况(从 php 运行 git 命令),但我正在寻找除 sudo 之外的另一种方法来优先处理进程,从而更快地执行。
【解决方案2】:

这不是 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 非常高。

我看到了这个问题的 2 个解决方案:

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

补充说明:

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

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

与 exec('your command &'); 效果基本相同

【讨论】:

  • 这似乎不太可能成为问题,因为 PHP 的 exec() 的执行时间与普通 shell 的执行时间成正比增长。
【解决方案3】:

我遇到过这个问题,当通过命令行运行时大约需要 0.025 秒的图形处理命令在 PHP 中通过 exec() 调用时大约需要 0.3 秒。经过大量研究,似乎大多数人认为这是 apache 或 PHP 的问题。然后我尝试通过 CGI 脚本运行命令,完全绕过 PHP,得到了相同的结果。

看来问题肯定是apache,所以我安装了lighttpd,得到了同样的结果!

经过一番思考和实验,我意识到这一定是处理器优先级的问题。因此,如果您希望命令以与命令行相似的速度运行,则必须按如下方式执行。

exec('echo "password" | sudo -S nice -n -20 command')

请注意:我知道对此会有各种各样的安全异议。我只是想简单地专注于你所要做的就是在你的命令之前添加 nice 。

【讨论】:

    【解决方案4】:

    当您通过键盘或通过 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');
    

    然后你创建了两个 shell,第一个 shell 中的设置永远不会到达第二个 shell。要将环境变量放入第二个 shell,您需要这样的东西:

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

    现在,如果您认为外壳本身可能是问题所在,因为制作外壳需要很长时间,请使用您知道几乎不会花费时间的东西对其进行测试:

    $starttime = microtime(true);
    exec('echo hi');
    $endtime = microtime(true);
    $time_taken = $endtime-$starttime;
    

    到那时,您就知道要尝试找到一些方法来使 shell 实例化更快。

    希望这会有所帮助!

    【讨论】:

    • echo 示例在我的机器上执行 0.005 秒
    【解决方案5】:

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

    推荐用于单个文件处理

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

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

    即使不允许覆盖,如果AllowOverrideAll,您也可以从.htaccess 更改内存设置。

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

    推荐用于批处理文件

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

    exec('convert --your-convert-options > /dev/null 2>/dev/null &');
    

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

    性能说明

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

    【讨论】:

      【解决方案6】:

      当你调用exec 时,php 不会创建线程,它会创建一个新的子进程。创建一个新进程是很大的开销。

      但是,当您使用 ssh 连接时,您只是传递了要执行的命令。您不是该程序的所有者,因此它作为您连接的用户执行。对于exec,它是运行 PHP 的用户。

      【讨论】:

      • 而且当你在shell中输入命令时,shell也会创建一个新的子进程。所以这本身不可能是原因。
      • 以apache和普通用户运行程序是不一样的。我添加了一些关于为什么ssh 技术执行得更快的更多信息。
      • 我也认为创建一个进程不需要 3 秒
      • @shiplu.mokadd.im:实际上是完全一样的(除了有效权限、环境变量等)。额外的文字并没有真正添加任何东西。
      • 这不是真正的原因。这是一个持续的减速,不仅仅是在命令启动时。从 shell 运行 1s 的东西将从 php 运行 7s,如果它在 shell 中是 2s,它将是 14 thru php,所以这不仅仅是一个启动延迟。这更像是摩擦,一直在拉回。我很高兴知道到底发生了什么。
      猜你喜欢
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-10
      • 2015-05-20
      • 1970-01-01
      • 2012-06-01
      相关资源
      最近更新 更多