以下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上似乎没有错误,因为命令行执行的运行时是可以的。
任何关于如何改进上述命令的运行时间的建议将不胜感激。
非常感谢您的帮助。
@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过程的不同进程使用(使访问者感觉网站移动得更快),但存在内存消耗,以及处理许多将计数的连接。
当你调用exec
php时不会创建一个线程,它会创建一个新的子进程。创建一个新流程是一个很大的开销。
但是,当您使用ssh连接时,您只是传递一个命令来执行。您不是该程序的所有者,因此它以您连接的用户身份执行。对于exec
来说,它是运行PHP的用户。
这不是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非常高,如您所见。
我看到这个问题的2个解决方案:
附加说明:
在搜索时,我发现了与C的名字相同的PHP函数:popen()
有趣的是因为可以异步执行外部命令:pclose(popen('your command','r'));
这基本上与exec('你的命令&')具有相同的效果;
我遇到过这个问题,一个图形处理命令,通过命令行运行大约需要.025秒,在PHP中通过exec()调用大约需要0.3秒。经过大量研究,似乎大多数人认为这是apache或PHP的问题。然后我尝试通过CGI脚本运行命令,完全绕过PHP,我得到了相同的结果。
因此问题似乎必须是apache,所以我安装了lighttpd,并得到了相同的结果!
经过一番思考和实验,我意识到这一定是处理器优先级的问题。因此,如果您希望命令以与命令行类似的速度运行,则必须按如下方式执行。
exec('echo "password" | sudo -S nice -n -20 command')
请注意:我知道会有各种各样的安全异议。我只想简单地关注答案,你要做的就是在你的命令之前加上好的。