Наша команда работает над созданием плагинов WordPress и предоставляет размещенные экземпляры на нескольких независимых серверах. Наша установка WordPress управляется Git, на всех серверах установлена одна и та же установка источника и WordPress, только домены и фактические данные в базе данных различаются. Для каждой установки MySql работает на одном хосте. WordPress работает исключительно на каждом сервере.
Однако после развертывания этой установки на Windows Server 2008 RC2 мы заметили резкую разницу в производительности по сравнению с нашими другими серверами: время генерации страницы увеличивается с avg. От 400 до 4000-5000мс для страниц, сгенерированных с помощью PHP. Для статических ресурсов, поставляемых только Apache, скорость примерно такая же, как у linux.
Поэтому мы предприняли некоторые шаги, чтобы сузить проблему:
- Убедитесь, что нет антивирусного программного обеспечения или других объектов домена Windows, мешающих
- Собирайте данные профилирования для идентификации таймеров во время выполнения сценария
- Тестирование различных серверных и аппаратных настроек
- Дважды проверьте конфигурацию Apache и PHP для очевидных ошибок конфигурации.
После некоторого профилирования мы быстро заметили, что оценка регулярных выражений на наших машинах Windows ужасно медленная. Оценка 10.000 Регулярных выражений (preg_match
) занимает около 90 мс в Linux и 3000 мс в Windows.
Профилирование, системные тесты и детали конфигурации приведены ниже. Мы не хотим оптимизировать этот скрипт (который мы знаем, как это сделать). Мы хотим, чтобы сценарий работал примерно на той же скорости в окнах, что и на Linux (с той же настройкой относительно opcache/...). Не нужно также оптимизировать объем памяти сценария.
Обновление: через некоторое время системы, похоже, исчерпали память, вызывая исключения из памяти и случайные распределения. См. Ниже для более подробной информации. На данный момент исправление Apache/PHP исправило проблему.
Трассировка в _get_browser
:
File (called from)
require wp-blog-header.php (index.php:17)
wp (wp-blog-header.php:14)
WP->main (functions.php:808)
php::do_action_ref_array (class-wp.php:616)
php::call_user_func_array (wp-includes/plugin:507)
wp_slimstat::slimtrack (php::internal (507))
wp_slimstat::_get_browser (wp-slimstat.php:385)
Обновление 2: Некоторая причина, по которой я не могу вспомнить, мы вернулись к активации PHP как модуля Apache на наших серверах (то же самое, что давало плохую производительность). Но сегодня они бегут невероятно быстро (~ 1сек/запрос). Добавление Opcache сводится к ~ 400ms/req. Apache/PHP/Windows остались прежними.
1) Результаты профилирования
Профилирование выполнялось с помощью XDebug на всех машинах. Обычно мы собрали всего несколько прогонов - этого было достаточно, чтобы выявить место, где большую часть времени (50% +) было потрачено: метод [get_browser][1]
плагина WordPress wp-slimstats
:
protected static function _get_browser(){
// Load cache
@include_once(plugin_dir_path( __FILE__ ).'databases/browscap.php');
// browscap.php contains $slimstat_patterns and $slimstat_browsers
$browser = array('browser' => 'Default Browser', 'version' => '1', 'platform' => 'unknown', 'css_version' => 1, 'type' => 1);
if (empty($slimstat_patterns) || !is_array($slimstat_patterns)) return $browser;
$user_agent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'';
$search = array();
foreach ($slimstat_patterns as $key => $pattern){
if (preg_match($pattern . 'i', $user_agent)){
$search = $value = $search + $slimstat_browsers[$key];
while (array_key_exists(3, $value) && $value[3]) {
$value = $slimstat_browsers[$value[3]];
$search += $value;
}
break;
}
}
// Lots of other lines to relevant to the profiling results
}
Эта функция, аналогичная PHP get_browser
обнаруживает возможности браузера и ОС. Большая часть времени выполнения скрипта проводится в этом цикле foreach
, оценивая все эти preg_match
(~ приблизительно 8000 - 10000 на запрос страницы). Это занимает около 90 мс в Linux и 3000 мс в Windows. Результаты были одинаковыми для всех тестируемых установок (на рисунке показаны данные двух исполнений):
Конечно, загрузка двух огромных массивов занимает некоторое время. Оценка регулярных выражений. Но мы ожидаем, что они возьмут примерно то же самое время на Linux и Windows. Это результат профилирования в linux vm (только для одной страницы). Разница довольно очевидна:
Другим убийцей времени был Object-Cache WordPress:
function get( $key, $group = 'default', $force = false, &$found = null ) {
if ( empty( $group ) )
$group = 'default';
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) )
$key = $this->blog_prefix . $key;
if ( $this->_exists( $key, $group ) ) {
$found = true;
$this->cache_hits += 1;
if ( is_object($this->cache[$group][$key]) )
return clone $this->cache[$group][$key];
else
return $this->cache[$group][$key];
}
$found = false;
$this->cache_misses += 1;
return false;
}
Время расходуется внутри самой этой функции (3 исполнения скриптов):
В linux:
Последним настоящим убийцей было время перевести. Каждый перевод, загруженный из памяти, занимает от 0.2 мс до 4 мс в WordPress:
В linux:
2) Протестированные системы
Чтобы убедиться, что виртуализация или Apache действительно влияют на это, мы протестировали это на нескольких настройках. Антивирус был отключен во всех настройках:
- Linux Debian, Apache 2 и PHP на современных стабильных выпусках. Это то же самое для разработчиков, работающих на своих виртуальных машинах, как для промежуточных/живых серверов. Действуя как эталонная система желаемой производительности. Либо запустите в нашем офисе, либо на каком-то хостинге (совместное пространство). У систем Windows было от 4 до 8 ГБ оперативной памяти, в то время как использование памяти составляло 50%. Виртуализации никогда не запускают Windows и Apache одновременно.
- Life-Servers, работающие на T-Systems (управляемые виртуальные серверы), на VMWare Player
- Win 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 как модуль fastcgi
- Win 2008 R2. Apache 2.2.25 + PHP 5.5.1 NTS, VC11 как модуль fastcgi
- Win 2008 R2. Apache 2.2.25 + PHP 5.5.1 NTS, VC11 как модуль apache
- Win 2008 R2, Apache 2.2.25 + PHP 5.5.11 TS, VC11 в качестве модуля apache (тот быстрый, который я упомянул в обновлении 2)
- На локальном компьютере Host: OpenSuse, Virtualization: VMWare, так же, как @T-Systems. Чтобы не повлиять на их инфраструктуру:
- Win 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 как модуль fastcgi
- Win 2008 R2. IIS7 + PHP 5.4.26 NTS, VC9 как модуль fastcgi (с и без wincache)
- Win 2012. IIS * + PHP 5.5.10 NTS, VC11 как модуль fastcgi (с и без wincache)
- На локальной машине без виртуализации
- Win 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 как модуль fastcgi
Результаты профилирования, как упоминалось выше, были одинаковыми для разных систем (выход ~ 10%). Windows всегда была значительным фактором медленнее, чем Linux.
Использование новой установки WordPress & Slimstats привело к прибл. те же результаты. Переписывание кода здесь не является вариантом.
Обновление. Между тем мы обнаружили две другие системы Windows (как Windows 2008 R2, VM и Phys), где этот полный стек работает довольно быстро. Тем не менее, такая же конфигурация.
Обновление 2: Запуск PHP в качестве модуля apache на Life-Servers был немного быстрее, чем метод fastcgi: до ~ 2 секунд, на 50% меньше.
Запуск памяти
Через некоторое время наш Live-сервер перестает работать, вызывая эти исключения из памяти:
PHP Fatal error: Out of memory (allocated 4456448) (tried to allocate 136 bytes)
PHP Fatal error: Out of memory (allocated 8650752) (tried to allocate 45 bytes)
PHP Fatal error: Out of memory (allocated 6815744) (tried to allocate 24 bytes)
Это происходит случайно. Очевидно, что менеджер памяти Zend не может выделить больше памяти, хотя скриптам будет разрешено это сделать. В то время, когда инцидент, на сервере было около 50% свободной памяти (2GB+). Таким образом, на сервере фактически не хватает бара. Перезапуск Apache/PHP исправил эту проблему на данный момент.
Не уверен, что эта проблема связана с проблемами производительности здесь. Тем не менее, поскольку оба вопроса, по-видимому, связаны с памятью, он включен здесь. Особенно мы попытаемся воспроизвести настройки Windows-тестов, обеспечивающие достойную производительность.
3) Конфигурация Apache и PHP
... вероятно, не имеют общих ошибок. Буферизация вывода включена (по умолчанию), многобайтовое переопределение отключено,... Если какие-либо опции представляют интерес, мы с радостью их предоставим.
Вывод httpd.exe -V
Server version: Apache/2.4.7 (Win32)
Apache Lounge VC10 Server built: Nov 26 2013 15:46:56
Server Module Magic Number: 20120211:27
Server loaded: APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture: 32-bit
Server MPM: WinNT
threaded: yes (fixed thread count)
forked: no
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses disabled)
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/apache"
-D SUEXEC_BIN="/apache/bin/suexec"
-D DEFAULT_PIDLOG="logs/httpd.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error.log"
-D AP_TYPES_CONFIG_FILE="conf/mime.types"
-D SERVER_CONFIG_FILE="conf/httpd.conf"
Конфигурация mpm_winnt_module
:
<IfModule mpm_winnt_module>
ThreadsPerChild 150
ThreadStackSize 8388608
MaxConnectionsPerChild 0
</IfModule>
Выдержка из php.ini:
realpath_cache_size = 12M
pcre.recursion_limit = 100000
4) Текущая подозреваемая причина
Старое предположение:
Все три примера сильно зависят от больших массивов и операций с строками. Что-то вроде обычной фабрики. Поскольку реализация работает нормально в Linux, мы подозреваем, что это проблема с памятью в Windows. Учитывая отсутствие взаимодействия с базами данных в местах с пин-точками, мы не подозреваем, что проблема с базой данных или сервером <-> PHP. Как-то взаимодействие с PHP-памятью кажется медленным. Может быть, кто-то мешает памяти на Windows сделать доступ значительно медленнее?
Старое предположение 2:
Поскольку один и тот же стек отлично работает на других машинах Windows, мы предполагаем, что проблема находится где-то в конфигурации Windows.
Новое предположение 3:
На самом деле, я не уверен в своих предположениях. Зачем запускать PHP намного медленнее, чем fastcgi, тогда как модуль apache>
Любые идеи о том, как проверить это или найти настоящую проблему здесь? Любая помощь или руководство для решения этой проблемы приветствуется.