Заметки, Проекты → Сравниваем классы в 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.