Производительность PHP 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;
Когда я запускаю ту же команду через 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, заключался в том, что накладные расходы возникают из-за того, что веб-сервер должен запустить thread/shell. Неужели это действительно причина? Я думал, что потоки левого веса и не занимают много времени, чтобы начать/прекратить работу.
До вызова exec
я устанавливаю количество потоков, используемых imagemagick (поскольку это было/является ошибкой в OpenMP?, Ссылка) на 1 с помощью exec('env MAGICK_THREAD_LIMIT=1');
. Время выполнения из PHP мало меняет, независимо от того, какое значение я установил для MAGICK_THREAD_LIMIT
. Во всяком случае, в этой версии, похоже, нет ошибки в OpenMP, потому что время выполнения командной строки в порядке.
Приветствуются любые предложения о том, как я мог улучшить время выполнения вышеуказанной команды.
Большое спасибо за вашу помощь.
Ответы
Ответ 1
Когда вы входите на компьютер Unix, либо на клавиатуре, либо через ssh, вы создаете новый экземпляр оболочки. Оболочка обычно похожа на /bin/sh
или /bin/bash
. Оболочка позволяет выполнять команды.
Когда вы используете exec()
, он также создает новый экземпляр оболочки. Этот экземпляр выполняет команды, которые вы ему отправили, а затем завершает работу.
Когда вы создаете новый экземпляр команды оболочки, у нее есть собственные переменные среды. Поэтому, если вы это сделаете:
exec('env MAGICK_THREAD_LIMIT=1');
exec('/usr/local/bin/convert 1.pdf -density 200 -quality 85% 1.jpg');
Затем вы создаете две оболочки, а параметр в первой оболочке никогда не попадает во вторую оболочку. Чтобы включить переменную окружения во вторую оболочку, вам нужно что-то вроде этого:
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;
В этот момент вы знаете, как попытаться найти способ ускорить создание оболочки.
Надеюсь, это поможет!
Ответ 2
Я программировал компьютеры более 56 лет, но это первый раз, когда я столкнулся с такой ошибкой. Таким образом, я потратил почти неделю, пытаясь понять 7X худшую скорость выполнения при выполнении Perl-программы с php через exec и выполнение программы perl непосредственно в командной строке. В рамках этих усилий я также все время рассматривал эту проблему в Интернете. Вот что я нашел:
(1) Это ошибка, которая была впервые опубликована в 2002 году и не была исправлена в последующие 11 лет.
(2) Ошибка связана с тем, как apache взаимодействует с php, поэтому обе эти организации передают доллар другим.
(3) Ошибка одинакова для exec, system или любой из альтернатив.
(4) Ошибка не зависит от того, является ли исполняемая программа perl, exe или что-то еще.
(5) Ошибка в UNIX и Windows одинакова.
(6) Ошибка не имеет ничего общего с imagemagick или с изображениями вообще. Я столкнулся с ошибкой в совершенно другой обстановке.
(7) Ошибка не имеет ничего общего с временем запуска fork, shell, bash, что угодно.
(8) Ошибка не исправлена путем изменения владельца службы apache.
(9) Я не уверен, но я думаю, что это связано с огромным увеличением накладных расходов при вызове подпрограмм.
Когда я столкнулся с этой проблемой, у меня была программа perl, которая выполнялась бы через 40 секунд, но через exec потребовалось 304 секунды. Моим окончательным решением было выяснить, как оптимизировать мою программу, чтобы она выполнялась через 0,5 секунды напрямую или через 3,5 секунды через exec. Поэтому я никогда не решал проблему.
Ответ 3
Когда вы вызываете exec
, php не создает поток, он создает новый дочерний процесс. Создание нового процесса - большие накладные расходы.
Однако, когда вы подключаетесь к ssh, вы просто передаете команду для выполнения. Вы не являетесь владельцем этой программы, поэтому она выполняется как пользователь, с которым вы связаны. Для exec
это пользователь, который запускает PHP.
Ответ 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
run async
для обработки одного файла будет стоить больше времени процессора и больше памяти, чем использование GD/Imagick в PHP. Время/память будет использоваться другим процессом, который не влияет на процесс PHP (заставляя посетителей чувствовать, что сайт движется быстрее), но потребление памяти существует, и когда дело доходит до обработки многих подключений, которые будут учитываться.
Ответ 5
Я столкнулся с этой проблемой, команда графической обработки, которая при запуске через командную строку занимает около 0,25 секунды, занимает около 0,3 секунды при вызове через exec() в PHP. По большому счету, похоже, что большинство людей считают, что это проблема с apache или PHP. Затем я попробовал запустить команду через CGI script, в обход всего PHP, и я получил тот же результат.
Казалось, что проблема должна быть apache, поэтому я установил lighttpd и получил тот же результат!
После некоторых размышлений и экспериментов я понял, что это должна быть проблема с приоритетом процессора. Поэтому, если вы хотите, чтобы ваши команды запускались со скоростью, аналогичной командной строке, она должна выполняться следующим образом.
exec('echo "password" | sudo -S nice -n -20 command')
ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: Я знаю, что к этому будут допущены всевозможные возражения безопасности. Я просто хотел сосредоточиться просто в ответе, что все, что вам нужно сделать, это добавить приятное перед вашей командой.