Сколько не говори о PHP все равно к нему прилипло не мало мифов. Сегодня я развею несколько мифов используя PECL расширение Alternative PHP Cache. Это не много больше чем расширение — это кешер опт кода.
Начнем развеивание мифов с того, что сегодня без PHP акселераторов почти никто не работает. Кто-то может называть их костылями, но факт того, что голый php работает «немного расточительно» просто обязал разработчиков задуматься, над оптимизацией процесса обработки PHP приложений. Итак посмотрим на пример конкурирования приложения 1 раз:
Результатом будет:
Как я и говорил, по сути мы 1 раз сконструировали скрипт и не тратим лишний раз память и ресурсы на инициализацию переменных и конфигурацию переменных. 208 байт кстати выделяется независимо от количества констант и данных в них.
Как мы обычно перекидываем информацию? обычно с помощью БД или записываем данные в фаил, или используем относительно медленный Memceched.
Мы будем перекидывать информацию используя:
Проведем синтетический тест чтобы понять насколько быстро мы будем перекидывать информацию:
Тест возвращает вот такие результаты;
Как видите сами скорость работы с памятью apc очень даже приемлемая и сопоставима с работой класса в php. Тут стоит передать привет классу APCIterator, который более продвинуто работает с локами, более быстро вытаскивает большие объемы данных, и может доставать переменные с подошью регулярок, которые кстати работают на уровне C.
apc_store(),apc_fetch(),apc_add()
Эта троица способна работать с объектами в том числе которые уже инициализированные. Однако класс объекта должен быть подгружен либо автоматический либо принудительно.
Проведем ряд тесов которые покажут, что из этого мы можем выжать:
Результат очень интересный:
Мы можем работать с уже инициализированными объектами, и более того мы можем сразу применить метод и получить ответ не используя лишнюю память. Насколько это практично может подсказать только реальное применение этой технологии.
Используя те или иные подходы можно сэкономить и память и процессорное время. Кроме того мы можем быстро обмениваться данными между воркерами, что позволяет PHP не отставать от асинхронных языков.
Миф 1. PHP нужно каждый раз конфигурировать приложение
Начнем развеивание мифов с того, что сегодня без PHP акселераторов почти никто не работает. Кто-то может называть их костылями, но факт того, что голый php работает «немного расточительно» просто обязал разработчиков задуматься, над оптимизацией процесса обработки PHP приложений. Итак посмотрим на пример конкурирования приложения 1 раз:
Код
$constants = array(
"REVATIVE_PATH"=>"",
"DB_PREFIX"=>"diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_diafan_",// для наглядности примера
"DB_CHARSET"=>"utf8",
"ADMIN_FOLDER"=>"admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/admin/",
"USERADMIN"=>true,
"CACHE_MEMCACHED"=>true,
"CACHE_MEMCACHED_HOST"=>"localhost",
"CACHE_MEMCACHED_PORT"=>"11211",
"TIMEZONE"=>"Europe/Moscow",);
apc_define_constants('numbers', $constants);// первый раз читаем конфигурацию
?>
<?php //Второй раз загружаем конфигурацию\
function mUsage($usage, $base_memory_usage, $k) {
echo "\n";
echo 'Разница использования памяти внутри скрипта в байтах: ' ,$usage - $base_memory_usage, $k;
echo "\n";
}
$base_memory_usage = memory_get_usage();
apc_load_constants('numbers');
mUsage(memory_get_usage(), $base_memory_usage,' - загрузился доступ к ссылкам на константы.');
$base_memory_usage = memory_get_usage();
echo REVATIVE_PATH, DB_PREFIX, DB_CHARSET, ADMIN_FOLDER, USERADMIN, CACHE_MEMCACHED, CACHE_MEMCACHED_HOST, CACHE_MEMCACHED_PORT, TIMEZONE;
mUsage(memory_get_usage(), $base_memory_usage , ' - были использованны только ссылки.');
$base_memory_usage = memory_get_usage();
$m= REVATIVE_PATH.DB_PREFIX.DB_CHARSET.ADMIN_FOLDER.USERADMIN.CACHE_MEMCACHED.CACHE_MEMCACHED_HOST.CACHE_MEMCACHED_PORT.TIMEZONE;
mUsage(memory_get_usage(), $base_memory_usage, ' - мы записали константы в нашу переменную.');
?>
Результатом будет:
Разница использования памяти внутри скрипта в байтах: 208 - загрузился доступ к ссылкам на константы.
...
Разница использования памяти внутри скрипта в байтах: 0 - были использованы только ссылки.
Разница использования памяти внутри скрипта в байтах: 504 - мы записали константы в нашу переменную.
Как я и говорил, по сути мы 1 раз сконструировали скрипт и не тратим лишний раз память и ресурсы на инициализацию переменных и конфигурацию переменных. 208 байт кстати выделяется независимо от количества констант и данных в них.
Миф 2. В PHP нельзя вот так вот просто взять и перекинуть информацию из воркера в воркер
Как мы обычно перекидываем информацию? обычно с помощью БД или записываем данные в фаил, или используем относительно медленный Memceched.
Мы будем перекидывать информацию используя:
apc_store(); // переназначает значение переменой
apc_fetch(); // возвращает значение переменной
apc_add(); // если не определена переменная то присваивает ей значение
Проведем синтетический тест чтобы понять насколько быстро мы будем перекидывать информацию:
Код теста
$a = microtime(1);
$b = microtime(1);
class q{
public $i;
public function c($i, $e) {
$this->i[$i]=$e;
}
public function r($i) {
return $this->i[$i];
}
public function i($i, $e = 1) {
$this->i[$i]++;
}}
function c($w, $e) {
global $$w;
$$w = $e;
}
function r($w) {
global $$w;
return $$w;
}
function i($w, $e = 1) {
global $$w;
++$$w;}
$base_memory_usage = memory_get_usage();
// Для чистоты эксперемента сделаем вот так
$q=new q();
//apc_store('object q',$q);
memoryUsage(memory_get_usage(), $base_memory_usage,' — инициализзация класса');
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for (c('i', 0); r('i') < 1000000; i('i')) {
c('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.r('i'));
}
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — потребление памяти при использовании финкций и глобальной переменной');
echo 'function: ',$b — $a;
echo "\n";
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for ($q->c('i', 0); $q->r('i') < 1000000; $q->i('i')) {
$q->c('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.$q->r('i'));
}
apc_store('object q',$q);
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — потребление памяти при использовании уже инициализированного оъекта');
echo 'object: ', $b — $a;
echo "\n";
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for (apc_store('i', 0); apc_fetch('i') < 1000000; apc_inc('i')) {
apc_store('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.apc_fetch('i'));
}
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — память внутри воркера не используется');
echo ':apc: ',$b — $a;
$b = microtime(1);
class q{
public $i;
public function c($i, $e) {
$this->i[$i]=$e;
}
public function r($i) {
return $this->i[$i];
}
public function i($i, $e = 1) {
$this->i[$i]++;
}}
function c($w, $e) {
global $$w;
$$w = $e;
}
function r($w) {
global $$w;
return $$w;
}
function i($w, $e = 1) {
global $$w;
++$$w;}
$base_memory_usage = memory_get_usage();
// Для чистоты эксперемента сделаем вот так
$q=new q();
//apc_store('object q',$q);
memoryUsage(memory_get_usage(), $base_memory_usage,' — инициализзация класса');
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for (c('i', 0); r('i') < 1000000; i('i')) {
c('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.r('i'));
}
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — потребление памяти при использовании финкций и глобальной переменной');
echo 'function: ',$b — $a;
echo "\n";
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for ($q->c('i', 0); $q->r('i') < 1000000; $q->i('i')) {
$q->c('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.$q->r('i'));
}
apc_store('object q',$q);
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — потребление памяти при использовании уже инициализированного оъекта');
echo 'object: ', $b — $a;
echo "\n";
$base_memory_usage = memory_get_usage();
$a = microtime(1);
for (apc_store('i', 0); apc_fetch('i') < 1000000; apc_inc('i')) {
apc_store('is', 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'.apc_fetch('i'));
}
$b = microtime(1);
memoryUsage(memory_get_usage(), $base_memory_usage,' — память внутри воркера не используется');
echo ':apc: ',$b — $a;
Тест возвращает вот такие результаты;
Разница использования памяти внутри скрипта в байтах: 224 - инициализация класса
Разница использования памяти внутри скрипта в байтах: 552 - потребление памяти при использовании функций и глобальной переменной
function: 4.7004411220551
Разница использования памяти внутри скрипта в байтах: 960 - потребление памяти при использовании уже инициализированного объекта
object: 4.1361658573151
Разница использования памяти внутри скрипта в байтах: 0 - память внутри воркера не используется
:apc: 3.9975609779358
Как видите сами скорость работы с памятью apc очень даже приемлемая и сопоставима с работой класса в php. Тут стоит передать привет классу APCIterator, который более продвинуто работает с локами, более быстро вытаскивает большие объемы данных, и может доставать переменные с подошью регулярок, которые кстати работают на уровне C.
Миф 3. В PHP каждый раз инициализируются объекты и по другому никак
apc_store(),apc_fetch(),apc_add()
Эта троица способна работать с объектами в том числе которые уже инициализированные. Однако класс объекта должен быть подгружен либо автоматический либо принудительно.
Проведем ряд тесов которые покажут, что из этого мы можем выжать:
Код
class q{
public $i;
public $n;
public function c($i, $e) {
$this->i[$i]=$e;
}
public function qq($i,$n) {
$this->n=$n;
return $this->qqq($i);
}
public function qqq($i) {
$this->i[$i]=$this->i[$i] + $this->n;
return $this->i[$i];
}
public function r($i) {
return $this->i[$i];
}
public function i($i, $e = 1) {
$this->i[$i]++;
}}
$q=new q();
$q->c('i',1000000);
apc_store('object q',$q);
$base_memory_usage = memory_get_usage();
print_r(apc_fetch('object q'));
memoryUsage(memory_get_usage(), $base_memory_usage,' - Смотрим на обьект до применения метода');
$base_memory_usage = memory_get_usage();
print_r(apc_fetch('object q')->qq('i',123));
memoryUsage(memory_get_usage(), $base_memory_usage,' - Достаем результат сразу применяя метод.');
$base_memory_usage = memory_get_usage();
print_r(apc_fetch('object q'));
memoryUsage(memory_get_usage(), $base_memory_usage,' - Смотрим на обьект после применения метода');
$base_memory_usage = memory_get_usage();
$new = apc_fetch('object q');
memoryUsage(memory_get_usage(), $base_memory_usage,' - Записываем объект в переменную для полноценно работы с обьектом');
Результат очень интересный:
q Object
(
[i] => Array
(
[i] => 1000000
)
[n] =>
)
Разница использования памяти внутри скрипта в байтах: 0 - Смотрим на объект до применения метода
1000123
Разница использования памяти внутри скрипта в байтах: 128 - Достаем результат сразу применяя метод.
q Object
(
[i] => Array
(
[i] => 1000000
)
[n] =>
)
Разница использования памяти внутри скрипта в байтах: 0 - Смотрим на объект после применения метода
Разница использования памяти внутри скрипта в байтах: 992 - Записываем в переменную для работы с объектом
Мы можем работать с уже инициализированными объектами, и более того мы можем сразу применить метод и получить ответ не используя лишнюю память. Насколько это практично может подсказать только реальное применение этой технологии.
Используя те или иные подходы можно сэкономить и память и процессорное время. Кроме того мы можем быстро обмениваться данными между воркерами, что позволяет PHP не отставать от асинхронных языков.