Подтвердить что ты не робот

Почему date() работает в два раза быстрее, если установить часовой пояс из кода?

Вы заметили, что функция date() работает 2x быстрее, чем обычно, если вы установили фактический часовой пояс в своем script перед любым вызовом date()? Мне очень интересно об этом.

Посмотрите на этот простой фрагмент кода:

<?php

  $start = microtime(true);
  for ($i = 0; $i < 100000; $i++) date('Y-m-d H:i:s');
  echo (microtime(true) - $start);

?>

Он просто вызывает функцию date(), используя цикл for 100 000 раз. Результат Ive получил всегда 1,6 секунды (Windows, PHP 5.3.5), но...

Если я устанавливаю тот же часовой пояс, добавляя одну абсурдную строку перед запуском:

date_default_timezone_set(date_default_timezone_get());

Я получаю время ниже 800 мс; ~ 2x быстрее (тот же сервер).

Я искал вокруг, чтобы найти разумное объяснение этого поведения, но не имел никакого успеха. С моей точки зрения эта дополнительная строка бесполезна, но PHP не согласен со мной.

Я пробовал этот тест на двух серверах linux (разные версии PHP) и получил разные результирующие времена, но в пропорции ~ 6: 1.

Примечание. Правильно установлено свойство date.timezone в php.ini (Европа/Париж).

Я искал связанные вопросы здесь и не нашел ничего подобного. Я также проверил руководство для date_default_time_zone() function @php.net и обнаружил, что я не только тот, кто это заметил, но все же не может понять, почему это происходит?

Кто-нибудь?

4b9b3361

Ответ 1

Обновление для PHP 5.4:

Как описано в описании date_default_timezone_get, начиная с PHP 5.4.0 алгоритм, чтобы угадать часовой пояс из системной информации был удален из кода (в отличие от источника PHP 5.3), поэтому этого поведения больше не существует.

Запуск теста времени на моем dev-сервере, чтобы увидеть его в действии, я получил:

  • PHP 5.3.11: ~ 720 мс
  • PHP 5.4.3: ~ 470ms

Оригинальный ответ:

Я только что изучил источник PHP. В частности, весь соответствующий код находится в /ext/date/php_date.c.

Я начал с предположения, что если вы не предоставляете часовой пояс для date, вызывается date_default_timezone_get для его получения. Здесь эта функция:

PHP_FUNCTION(date_default_timezone_get)
{
    timelib_tzinfo *default_tz;

    default_tz = get_timezone_info(TSRMLS_C);
    RETVAL_STRING(default_tz->name, 1);
}

ОК, так что выглядит get_timezone_info? Это:

PHPAPI timelib_tzinfo *get_timezone_info(TSRMLS_D)
{
    char *tz;
    timelib_tzinfo *tzi;

    tz = guess_timezone(DATE_TIMEZONEDB TSRMLS_CC);
    tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB TSRMLS_CC);
    if (! tzi) {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Timezone database is corrupt - this should *never* happen!");
    }
    return tzi;
}

Как насчет guess_timezone? Здесь это:

static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC)
{
    char *env;

    /* Checking configure timezone */
    if (DATEG(timezone) && (strlen(DATEG(timezone)) > 0)) {
        return DATEG(timezone);
    }
    /* Check environment variable */
    env = getenv("TZ");
    if (env && *env && timelib_timezone_id_is_valid(env, tzdb)) {
        return env;
    }
    /* Check config setting for default timezone */
    /*  ..... code omitted ....... */
#if HAVE_TM_ZONE
    /* Try to guess timezone from system information */
    /*  ..... code omitted ....... */
#endif
#ifdef PHP_WIN32
    /*  ..... code omitted ....... */
#elif defined(NETWARE)
    /*  ..... code omitted ....... */
#endif
    /* Fallback to UTC */
    php_error_docref(NULL TSRMLS_CC, E_WARNING, DATE_TZ_ERRMSG "We had to select 'UTC' because your platform doesn't provide functionality for the guessing algorithm");
    return "UTC";
}

ОК, так как это взаимодействует с date_default_timezone_set?. Посмотрите эту функцию:

PHP_FUNCTION(date_default_timezone_set)
{
    char *zone;
    int   zone_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &zone, &zone_len) == FAILURE) {
        RETURN_FALSE;
    }
    if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Timezone ID '%s' is invalid", zone);
        RETURN_FALSE;
    }
    if (DATEG(timezone)) {
        efree(DATEG(timezone));
        DATEG(timezone) = NULL;
    }
    DATEG(timezone) = estrndup(zone, zone_len);
    RETURN_TRUE;
}

Короче говоря: если вы вызываете date_default_timezone_set один раз, то guess_timezone берет быстрый путь чтения из переменной timezone (выполняется первое условие, и оно немедленно возвращается). В противном случае требуется некоторое время для разработки часового пояса по умолчанию, который не кэшируется (я думаю, для простоты), и если вы сделаете это в цикле, задержка начнет отображаться.

Ответ 2

Я бы предположил, что он должен определять часовой пояс для себя каждый раз, когда он вызывал, если явно не указано, что добавляет к времени выполнения функции.

Но действительно ли это имеет значение? Сколько скриптов вы, скорее всего, сделаете эту дату вызова() 100 000 раз за каждый запуск?