Мне стало любопытно и проверил ссылку Адама Бэкстрома на Tech Your Universe . В этой статье описывается одна из причин, по которой нужно использовать require вместо require_once. Однако их претензии не выдержали моего анализа. Мне было бы интересно посмотреть, где я мог неправильно проанализировать решение. Я использовал php 5.2.0 для сравнения.
Я начал с создания 100 заголовочных файлов, которые использовали require_once для включения другого заголовочного файла. Каждый из этих файлов выглядел примерно так:
<?php
// /home/fbarnes/phpperf/hdr0.php
require_once "../phpperf/common_hdr.php";
?>
Я создал их, используя быстрый взлом bash:
for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
echo "<?php
// $i" > $i
cat helper.php >> $i;
done
Таким образом, я мог бы легко переключаться между использованием require_once и require при включении заголовочных файлов. Затем я создал app.php для загрузки ста файлов. Это выглядело как:
<?php
// Load all of the php hdrs that were created previously
for($i=0; $i < 100; $i++)
{
require_once "/home/fbarnes/phpperf/hdr$i.php";
}
// Read the /proc file system to get some simple stats
$pid = getmypid();
$fp = fopen("/proc/$pid/stat", "r");
$line = fread($fp, 2048);
$array = split(" ", $line);
// write out the statistics; on RedHat 4.5 w/ kernel 2.6.9
// 14 is user jiffies; 15 is system jiffies
$cntr = 0;
foreach($array as $elem)
{
$cntr++;
echo "stat[$cntr]: $elem\n";
}
fclose($fp);
?>
Я сравнил заголовки require_once с заголовками require, которые использовали файл заголовка, похожий на:
<?php
// /home/fbarnes/phpperf/h/hdr0.php
if(!defined('CommonHdr'))
{
require "../phpperf/common_hdr.php";
define('CommonHdr', 1);
}
?>
Я не обнаружил большой разницы, когда выполнял это с требованием и требованием. На самом деле мои первоначальные тесты предполагали, что require_once был немного быстрее, но я не обязательно в это верю. Я повторил эксперимент с 10000 входными файлами. Здесь я увидел постоянную разницу. Я запускал тест несколько раз, результаты близки, но при использовании require_once в среднем используется 30,8 пользовательских и 72,6 системных запросов; использование require использует в среднем 39,4 пользовательских и 72,0 системных. Таким образом, кажется, что загрузка немного ниже с использованием require_once. Однако настенные часы немного увеличены. Для выполнения 10 000 вызовов require_once в среднем требуется 10,15 секунды, а для 10 000 вызовов требуется в среднем 9,84 секунды.
Следующий шаг - изучить эти различия. Я использовал strace для анализа выполняемых системных вызовов.
Перед открытием файла из require_once выполняются следующие системные вызовы:
time(NULL) = 1223772434
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
time(NULL) = 1223772434
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
Это отличается от require:
time(NULL) = 1223772905
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
time(NULL) = 1223772905
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3
Tech Ваша вселенная подразумевает, что require_once должен делать больше вызовов lstat64. Однако они оба делают одинаковое количество вызовов lstat64. Возможно, разница в том, что я не использую APC для оптимизации кода выше. Тем не менее, следующее, что я сделал, это сравнил вывод strace для всех прогонов:
[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
190709 strace_1000r.out
210707 strace_1000ro.out
401416 total
Фактически при использовании require_once на каждый заголовочный файл приходится примерно еще два системных вызова. Единственное отличие состоит в том, что require_once имеет дополнительный вызов функции time ():
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20009
strace_1000ro.out:30008
Другой системный вызов - getcwd ():
[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out
strace_1000r.out:5
strace_1000ro.out:10004
Это вызвано тем, что я решил использовать относительный путь, указанный в файлах hdrXXX. Если я сделаю это абсолютной ссылкой, то единственное отличие - это вызов дополнительного времени (NULL) в коде:
[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
190705 strace_1000r.out
200705 strace_1000ro.out
391410 total
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20008
strace_1000ro.out:30008
Кажется, это означает, что вы можете уменьшить количество системных вызовов, используя абсолютные пути, а не относительные пути. Единственное отличие от этого - это вызовы времени (NULL), которые, по-видимому, используются для инструментирования кода для сравнения того, что быстрее.
Еще одно замечание: в пакете оптимизации APC есть опция «apc.include_once_override», которая утверждает, что она уменьшает количество системных вызовов, выполняемых вызовами require_once и include_once (см. PHP-документы ). .
Извините за длинный пост. Мне стало любопытно.