以下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上对类似问题的一个答案是,开销来自必须启动线程/ shell的Web服务器。这可能是真的吗?我认为线程是轻量级的,并且不需要花很长时间才能启动/终止。
在调用exec
之前,我将imagemagick使用的线程数(因为这是/在OpenMP中的错误,Reference)设置为exec('env MAGICK_THREAD_LIMIT=1');
的1。无论我为MAGICK_THREAD_LIMIT
设置什么值,PHP的运行时间都没有太大变化。无论如何,在这个版本的OpenMP中似乎没有错误,因为命令行执行的运行时没问题。
我将非常感谢有关如何改进上述命令的运行时间的任何建议。
非常感谢你的帮助。
答案 0 :(得分: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');
然后创建两个shell,第一个shell中的设置永远不会到达第二个shell。要在第二个shell中获取环境变量,您需要这样的内容:
exec('env MAGICK_THREAD_LIMIT=1; /usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg');
现在,如果你认为shell本身可能是问题,因为制作shell需要很长时间,用你知道几乎没时间的东西测试它:
$starttime = microtime(true);
exec('echo hi');
$endtime = microtime(true);
$time_taken = $endtime-$starttime;
此时你知道尝试找到一些方法让shell更快地实例化。
希望这有帮助!
答案 1 :(得分:7)
我已经编程了超过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执行。所以我从来没有解决过这个问题。
答案 2 :(得分:4)
@Philipp,因为你有SSH,并且由于你的服务器允许访问exec()
,我将假设你也拥有对该机器的完全root权限。
拥有对计算机的root权限意味着您可以更改/etc/php5/php.ini
内存限制设置。
即使没有直接访问/etc/php5/php.ini
,您也可以通过在项目目录中创建新的php.ini
文件来检查您的服务器是否支持覆盖php.ini
指令。
即使不允许覆盖,如果.htaccess
为AllowOverride
,您也可以从All
更改内存设置。
另一种更改内存限制的方法是在PHP运行时使用ini_set('memory_limit', 256);
设置它。
通过exec()
运行转换的唯一好处是,如果您不打算从exec()
获取结果并允许它以异步方式运行:
exec('convert --your-convert-options > /dev/null 2>/dev/null &');
如果您尝试批量处理许多文件,上述方法通常很有帮助,您不想等待它们完成处理,也不需要确认每个文件已被处理。
使用上面的代码使exec
运行async
来处理单个文件将比在PHP中使用GD / Imagick花费更多的处理器时间和更多的内存。时间/内存将由不影响PHP进程的不同进程使用(使访问者感觉网站移动得更快),但存在内存消耗以及处理许多将计数的连接时。
答案 3 :(得分:3)
当你致电exec
php没有创建一个帖子时,它会创建一个新的子进程。创建一个新流程是一个很大的开销。
但是当你使用ssh连接时,你只是传递一个命令来执行。您不是该程序的所有者,因此它以您连接的用户身份执行。对于运行PHP的用户exec
。
答案 4 :(得分: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/
以下是显示速度差异vs popen()和popen_noshell()的屏幕截图:
PHP的exec()使用常规的popen() - 上面屏幕截图右侧的那个。执行C&#popen()时系统使用的CPU非常高,如您所见。
我看到了这个问题的两个解决方案:
附加说明:
在搜索时,我发现了与C&#39的同名PHP函数:popen()
有趣的是因为可以异步执行外部命令:pclose(popen('你的命令',' r'));
其实质上与exec相同('你的命令&');
答案 5 :(得分:0)
我遇到过这个问题,一个图形处理命令,当通过命令行运行需要大约.025秒,在PHP中通过exec()调用大约需要0.3秒。经过大量研究,似乎大多数人认为这是apache或PHP的问题。然后我尝试通过CGI脚本运行命令,完全绕过PHP,我得到了相同的结果。
因此问题似乎必须是apache,所以我安装了lighttpd,并得到了相同的结果!
经过一番思考和实验后,我意识到这一定是处理器优先级的问题。因此,如果您希望命令以与命令行类似的速度运行,则必须执行如下操作。
exec('echo "password" | sudo -S nice -n -20 command')
请注意:我知道会有各种各样的安全异议。我只想简单地关注答案,你要做的就是在你的命令之前加上好的。