Насколько значимы накладные расходы на вызов функции PHP?
Я относительно новичок в PHP и медленно изучаю особенности, характерные для языка. Одна вещь, которую я получаю много, заключается в том, что я (так мне говорят) использую слишком много вызовов функций, и меня обычно просят делать что-то, чтобы обойти их. Вот два примера:
// Change this:
} catch (Exception $e) {
print "It seems that error " . $e->getCode() . " occured";
log("Error: " . $e->getCode());
}
// To this:
} catch (Exception $e) {
$code = $e->getCode();
print "It seems that error " . $code . " occured";
log("Error: " . $code);
}
Пример 2
// Change this:
$customer->setProducts($products);
// To this:
if (!empty($products)) {
$customer->setProducts($products);
}
в первом примере я нахожу, что назначение $e->getCode()
to $code
объявления небольшие когнитивные накладные расходы; "Что такое " $code"? Это код исключения." Поскольку второй пример добавляет цикломатическую сложность. В обоих примерах я нахожу оптимизацию за счет читаемости и ремонтопригодности.
стоит ли увеличение производительности или это микро-оптимизация?
я должен отметить, что мы застряли с PHP 5.2 прямо сейчас.
я сделал некоторые очень грубые стендовые тесты и считаю, что производительность вызова функции зависит от порядка 10% до 70% в зависимости от характера моего стенда тест. Я признаю, что это важно. Но до того, как этот блок catch был поражен, был вызов базы данных и конечной точки HTTP. До $products
на $customer
был сложный вид, который произошел с $products
массив. в конце концов, оправдывает ли эта оптимизация стоимость усложнения чтения и обслуживания кода? или, хотя эти примеры являются упрощениями, кто-нибудь находит 2-й примеры так же легко или легче читать, чем первый (am Я-сосиска)?
может кто-нибудь привести какие-либо хорошие статьи или исследования по этому поводу?
Edit:
пример стендового испытания:
<?php
class Foo {
private $list;
public function setList($list) {
$this->list = $list;
}
}
$foo1 = new Foo();
for($i = 0; $i < 1000000; $i++) {
$a = array();
if (!empty($a))
$foo1->setList($a);
}
?>
запустите этот файл с помощью . На одной конкретной машине это занимает в среднем 0.60 секунд после нескольких запусков. Комментируя if (!empty($a))
заставляет его занимать в среднем 3,00 секунды для запуска.
уточнение: это примеры. 1-й пример демонстрирует ужасная обработка исключений и возможное сухое нарушение за счет простого, не специфичного для домена примера.
4 ответов
каноническая реализация PHP очень медленная, потому что ее легко реализовать, и приложения, на которые нацелен PHP, не требуют необработанной производительности, такой как быстрые вызовы функций.
вы можете рассмотреть другие реализации PHP.
Если вы пишете приложения, которые вы должны писать в PHP (данные дампа из БД в браузер по сети), то накладные расходы на вызов функции не являются значительными. Конечно, не выходите из своего пути, чтобы дублировать код, потому что вы боялись, что использование функции будет слишком накладным.
накладные расходы на вызов функции PHP точно 15.5355%.
:) просто подливаю масло в огонь.
серьезно, вот пара отличных ссылок на эту тему:
возможно ли иметь слишком много функций в приложении PHP?
функции против повторяющегося кода
поддержка кода по сравнению со скоростью обсуждения по этим ссылкам адресуют (возможно, более важный) вопрос, подразумеваемый OP, но просто добавить небольшие данные, которые также могут быть уместны и, надеюсь, полезны для людей, которые столкнутся с этой темой в будущем, вот результаты запуска приведенного ниже кода на Macbook Pro 2011 года (с очень маленьким дисковым пространством и слишком большим количеством запущенных программ).
Как отмечалось в другом месте, важным соображением при принятии решения о вызове функции или вводе кода "в строку" является то, сколько раз функция будет вызываться из определенного блока кода. Чем больше раз будет вызываться функция, тем более стоит рассмотреть возможность выполнения работы в режиме онлайн.
Результаты (время в секундах)
Метод Функции Вызова / Метод In-Line / Разница / Процент Отличается
1000 итераций (4 работает)
0.0039088726043701 | 0.0031478404998779 | 0.00076103210449219 | 19.4694
0.0038208961486816 | 0.0025999546051025 | 0.0012209415435791 | 31.9543
0.0030159950256348 | 0.0029480457305908 | 6.7949295043945 E-5 2.2530
0.0031449794769287 | 0.0031390190124512 | 5.9604644775391 E-6 / 0,1895
1,000,000 итераций (4 трассы)
3.1843111515045 | 2.6896121501923 | 0.49469900131226 | 15.5355
3.131945848465 | 2.7114839553833 | 0.42046189308167 | 13.4249
3.0256152153015 | 2.7648048400879 | 0.26081037521362 | 8.6201
3.1251409053802 | 2.7397727966309 | 0.38536810874939 | 12.3312
function postgres_friendly_number($dirtyString) {
$cleanString = str_ireplace("(", "-", $dirtyString);
$badChars = array("$", ",", ")");
$cleanString = str_ireplace($badChars, "", $cleanString);
return $cleanString;
}
//main
$badNumberString = '-0,832.61';
$iterations = 1000000;
$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
$goodNumberString = postgres_friendly_number($badNumberString);
}
$endTime = microtime(true);
$firstTime = ($endTime - $startTime);
$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
$goodNumberString = str_ireplace("(", "-", $badNumberString);
$badChars = array("$", ",", ")");
$goodNumberString = str_ireplace($badChars, "", $goodNumberString);
}
$endTime = microtime(true);
$secondTime = ($endTime - $startTime);
$timeDifference = $firstTime - $secondTime;
$percentDifference = (( $timeDifference / $firstTime ) * 100);
никто еще не обсуждал, как оборудование сервера связано с накладными расходами вызова функций.
при вызове функции все регистры ЦП содержат данные, относящиеся к текущей точке выполнения. Все регистры процессора должны быть сохранены в памяти (как правило, стек процесса) или нет никакой надежды когда-либо вернуться к этой точке выполнения и возобновить выполнение. При возврате из функции все регистры процессора должны быть восстановлены из памяти (обычно process ' stack).
Итак, можно увидеть, как строка вложенных вызовов функций может добавить накладные расходы к процессу. Регистры процессора должны быть сохранены снова и снова в стеке и восстановлены снова и снова, чтобы вернуться от функций.
Это действительно источник накладных расходов на вызовы функций. И если аргументы функции передаются, они должны быть дублированы, прежде чем функция может быть вызвана. Поэтому передача огромных массивов в качестве аргументов функции является плохой дизайн.
проведены исследования по объектно-ориентированному PHP на накладные расходы использования геттеров / сеттеров. Удаление всех геттеров / сеттеров сокращает время выполнения примерно на 50%. И это просто связано с накладными расходами на вызов функции.
Я вас путаю терминологией. Как правило, накладные расходы на вызов функции означает накладные расходы, связанные с вызовом возврата из функции. Вместо обработки inline. Это не общая стоимость вызова функции. Его просто стоимость подготовки аргументов и возвращаемых значений и выполнения ветви.
проблема в том, что PHP и другие слабо типизированные языки сценариев действительно плохо определяют, имеют ли функции побочные эффекты. Поэтому вместо того чтобы хранить результаты функции в качестве temp, они будут делать несколько вызовов. Если функция делает что-то сложное, это будет очень неэффективно.
Итак, суть такова: раз функцию и сохранить результат! Не вызывайте одну и ту же функцию несколько раз с одними и теми же аргументами. (без веской причины)--1-->