Заметки, Проекты → Сравниваем классы в php

Давно хотел сравнить между собой обычные методы классов и статичные, но все руки не доходили. Сегодня вот столкнулся с приватным фреймворком, в котором ВСЕ методы и параметры статичные. Вообще все, т.е. нет ни одного обычного объекта! Откладывать сравнение было уже нельзя, чем я и занялся утром. Результаты неоднозначные, даже странные для меня, местами. В общем, «непоняно» что и как… На Ваш суд выношу результаты тестов, а Вы попробуйте мне объяснить почему результаты именно такие. Кстати, я не буду сравнивать размер используемой памяти, т.к. тут как-раз все понятно — статичные методы и свойства явно будут меньше кушать памяти. Это понятно.

Итак, начнем. Для начала, попробуем сравнить между собой «сферических конец в вакууме», т.е. классы-пустышки. Код у нас получится примерно такой:

<?php

class ClassStatic {

	public static function test() {
		;
	}

}

class ClassDinamic {

	public function test() {
		;
	}

}

$static = array();
$dinamic = array();

for ($count = 0; $count < 100; $count++) {
	$start = microtime(TRUE);
	for ($i = 0; $i <= 1000; $i++)
		ClassStatic::test();
	$static[$count] = microtime(TRUE) - $start;

	$start = microtime(TRUE);
	$obj = new ClassDinamic();
	for ($i = 0; $i <= 1000; $i++)
		$obj -> test();
	$dinamic[$count] = microtime(TRUE) - $start;
}

echo 'Static: min - ' . min($static) . '; max - ' . max($static) . '; avg - ' . array_sum($static) / 100 . "<br />\n";
echo 'Dinamic: min - ' . min($dinamic) . '; max - ' . max($dinamic) . '; avg - ' . array_sum($dinamic) / 100 . "<br />\n";

Результаты пустышек довольно предсказуемы (опять-таки, для меня) — статичные методы медленнее обычных, примерно на 13%:

Static:
min: 0.00373196601868; max: 0.00434994697571; avg: 0.0038482427597

Dinamic:
min: 0.00326490402222; max: 0.00377583503723; avg: 0.00335712432861

Но, тестировать пустышки бесполезно, верно? Попробуем что-то делать и сравнить. Я выбрал md5 от случайных чисел:

<?php

class ClassStatic {

	public static function test() {
		return md5(mt_rand(1, 99999));
	}

}

class ClassDinamic {

	public function test() {
		return md5(mt_rand(1, 99999));
	}

}

$static = array();
$dinamic = array();

for ($count = 0; $count < 100; $count++) {
	$start = microtime(TRUE);
	for ($i = 0; $i <= 1000; $i++)
		ClassStatic::test();
	$static[$count] = microtime(TRUE) - $start;

	$start = microtime(TRUE);
	$obj = new ClassDinamic();
	for ($i = 0; $i <= 1000; $i++)
		$obj -> test();
	$dinamic[$count] = microtime(TRUE) - $start;
}

echo 'Static: min - ' . min($static) . '; max - ' . max($static) . '; avg - ' . array_sum($static) / 100 . "<br />\n";
echo 'Dinamic: min - ' . min($dinamic) . '; max - ' . max($dinamic) . '; avg - ' . array_sum($dinamic) / 100 . "<br />\n";

Результат не сильно отличается, что довольно предсказуемо. Но средняя разница во времени выполнения сократилась до 6%:

Static:
min: 0.00973296165466; max: 0.0118901729584; avg: 0.0100108170509

Dinamic:
min: 0.00917601585388; max: 0.0104711055756; avg: 0.00944202184677

Стало уже немного интереснее, т.к. я ожидал немного иного — ожидал, что разница увеличится не в пользу static. Этого не произошло. Ладно, идем дальше. До этого тестил вызов одного метода, теперь захотелось проверить, как отразится добавление параметра класса на скорость выполнения (забегая вперед, скажу, что на скорость практически не влияет видимость параметра). Убираю md5 и считаю числа — так правильнее, на мой взгляд (да и быстрее)):

<?php

class ClassStatic {

	protected static $result = 0;

	public static function test() {
		self::$result += mt_rand(1, 99999);
	}

}

class ClassDinamic {

	protected $result = 0;

	public function test() {
		$this -> result += mt_rand(1, 99999);
	}

}

$static = array();
$dinamic = array();

for ($count = 0; $count < 100; $count++) {
	$start = microtime(TRUE);
	for ($i = 0; $i <= 1000; $i++)
		ClassStatic::test();
	$static[$count] = microtime(TRUE) - $start;

	$start = microtime(TRUE);
	$obj = new ClassDinamic();
	for ($i = 0; $i <= 1000; $i++)
		$obj -> test();
	$dinamic[$count] = microtime(TRUE) - $start;
}

echo 'Static: min - ' . min($static) . '; max - ' . max($static) . '; avg - ' . array_sum($static) / 100 . "<br />\n";
echo 'Dinamic: min - ' . min($dinamic) . '; max - ' . max($dinamic) . '; avg - ' . array_sum($dinamic) / 100 . "<br />\n";

Результаты следующие:

Static:
min: 0.00786805152893; max: 0.0100691318512; avg: 0.00815812349319

Dinamic:
min: 0.00634098052979; max: 0.0100259780884; avg: 0.00662218570709

Средняя разница составила почти 19%. Это нормально… Идем дальше: добавляем еще по одному методу:

<?php

class ClassStaticTwo {

	public static function operation() {
		return mt_rand(1, 99999);
	}

}

class ClassStatic {

	protected static $result = 0;

	protected static function operation() {
		return mt_rand(1, 99999);
	}

	public static function test() {
		self::$result += self::operation();
	}

}

class ClassDinamic {

	protected $result = 0;

	protected function operation() {
		return mt_rand(1, 99999);
	}

	public function test() {
		$this -> result += $this -> operation();
	}

}

$static = array();
$dinamic = array();

for ($count = 0; $count < 100; $count++) {
	$start = microtime(TRUE);
	for ($i = 0; $i <= 1000; $i++)
		ClassStatic::test();
	$static[$count] = microtime(TRUE) - $start;

	$start = microtime(TRUE);
	$obj = new ClassDinamic();
	for ($i = 0; $i <= 1000; $i++)
		$obj -> test();
	$dinamic[$count] = microtime(TRUE) - $start;
}

echo 'Static: min - ' . min($static) . '; max - ' . max($static) . '; avg - ' . array_sum($static) / 100 . "<br />\n";
echo 'Dinamic: min - ' . min($dinamic) . '; max - ' . max($dinamic) . '; avg - ' . array_sum($dinamic) / 100 . "<br />\n";

Результаты стали значительно ровнее и ближе:

Static:
min: 0.011332988739; max: 0.0122909545898; avg: 0.0115933132172

Dinamic:
min: 0.0104849338531; max: 0.0119299888611; avg: 0.0108146929741

В принципе, в этот момент я уже был уверен, что использование статичных методов замедляет приложение. Но, для очистки совести, решил запустить тест посложнее:

<?php

class ClassStaticTwo {

	public static function operation() {
		return mt_rand(1, 99999);
	}

}

class ClassStatic {

	protected static $result = 0;

	public static function test($num) {
		self::$result += $num;
	}

}

class ClassDinamicTwo {

	public function operation() {
		return mt_rand(1, 99999);
	}

}

class ClassDinamic {

	protected $result = 0;

	public function test($num) {
		$this -> result += $num;
	}

}

$static = array();
$dinamic = array();

for ($count = 0; $count < 100; $count++) {
	$start = microtime(TRUE);
	for ($i = 0; $i <= 1000; $i++)
		ClassStatic::test(ClassStaticTwo::operation());
	$static[$count] = microtime(TRUE) - $start;

	$start = microtime(TRUE);
	$obj = new ClassDinamic();
	$obj2 = new ClassDinamicTwo();
	for ($i = 0; $i <= 1000; $i++)
		$obj -> test($obj2 -> operation());
	$dinamic[$count] = microtime(TRUE) - $start;
}

echo 'Static: min - ' . min($static) . '; max - ' . max($static) . '; avg - ' . array_sum($static) / 100 . "<br />\n";
echo 'Dinamic: min - ' . min($dinamic) . '; max - ' . max($dinamic) . '; avg - ' . array_sum($dinamic) / 100 . "<br />\n";

Результаты сильно не изменились, не смотря на добавление дополнительных классов:

Static:
min: 0.0130889415741; max: 0.0148260593414; avg: 0.0134398984909

Dinamic:
min: 0.0118761062622; max: 0.0135319232941; avg: 0.0122007513046

На этом я тесты закончил, т.к. подтвердил свое мнение. Если можете что-то предложить — говорите, протестим.

P.S.: часть тестов проводил днем на работе, часть сейчас дома. Уже не помню, какие именно были тесты на работе, но результаты СИЛЬНО отличались. Пару раз получилось так, что статичные методы были БЫСТРЕЕ обычных! Дома, к сожалению, мне это повторить не удалось (((
P.S.S.: тестировал на sony vaio z + win7x32 + denwer.

  • http://medar.me medar

    php под виндой — это тот еще оригинал, хорошо бы протестировать это под линухом.

    • Аноним

      Не вижу ничего предосудительного в этом, если честно. Последний тест разместил тут: http://aktuba.ru/projects/classes.php

  • Mr kto

    Нет никакого смысла тестировать скорость выполнения, важны только конкретные сценарии использования. Где что подходит больше.
    Не знаю, что за фреймворк, это скорее похоже на библиотеку какую-то.
    А так, обновят версию php и всё станет наоборот, и что? Если это узкое место приложения — тогда можно и подредактировать скрипты.

    • Аноним

      При чем тут сценарии? Я где-то сказал, что необходимо везде использовать обычные объекты?
      «А что, если…» — вот главный враг, преждевременная оптимизация. А знать, что замедляет выполнение приложения важно и необходимо (для тех же демонов или cron-скриптов). Вот простой пример:

      for ($i = 0; $i <= count($arr); $i++); ?

      Если вынести count из for скорость работы цикла увеличится почти на 70%. Или это тоже не надо знать? )