Pertanyaan Mengapa tanggal () berfungsi dua kali lebih cepat jika kita menetapkan zona waktu dari kode?


Pernahkah Anda memperhatikan hal itu date() fungsi berfungsi 2x lebih cepat daripada biasanya jika Anda menetapkan zona waktu aktual di dalam skrip Anda sebelumnya date() panggilan? Saya sangat ingin tahu tentang ini.

Lihatlah kode sederhana ini:

<?php

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

?>

Itu hanya panggilan date() fungsi menggunakan for loop 100.000 kali. Hasil yang saya dapatkan selalu ada 1,6 detik (Windows, PHP 5.3.5) tapi ...

Jika saya menetapkan zona waktu yang sama lagi menambahkan satu baris tidak masuk akal sebelum memulai:

date_default_timezone_set(date_default_timezone_get());

Saya mendapat waktu di bawah 800ms; ~ 2x lebih cepat (server yang sama).

Saya mencari-cari penjelasan yang masuk akal untuk perilaku ini tetapi tidak berhasil. Dari sudut saya, baris tambahan ini tidak berguna tetapi PHP tidak setuju dengan saya.

Saya telah mencoba tes ini pada dua server linux (versi PHP yang berbeda) dan mendapat hasil yang berbeda tetapi secara proporsional ~ 6: 1.

catatan: date.timezone properti di php.ini telah diatur dengan benar (Eropa / Paris).

Saya mencari pertanyaan terkait di sini dan tidak menemukan hal yang serupa. Saya juga sudah memeriksa manual untuk date_default_time_zone () fungsi @ php.net dan menemukan bahwa aku bukan hanya orang yang memperhatikan ini, tapi masih tidak mengerti mengapa itu terjadi?

Siapa saja?


32
2018-04-05 18:39


asal


Jawaban:


Pembaruan untuk PHP 5.4:

Seperti yang didokumentasikan dalam deskripsi date_default_timezone_get, mulai dari PHP 5.4.0 algoritma untuk menebak zona waktu dari informasi sistem telah dihapus dari kode (Berbeda dengan sumber PHP 5.3) sehingga perilaku ini tidak ada lagi.

Menjalankan tes waktu pada server dev saya untuk melihatnya dalam tindakan, saya mendapat:

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

Jawaban asli:

Saya baru saja melihat ke sumber PHP. Secara khusus, semua kode yang relevan masuk /ext/date/php_date.c.

Saya mulai dengan asumsi bahwa jika Anda tidak menyediakan zona waktu date, date_default_timezone_get dipanggil untuk mendapatkannya. Inilah fungsi itu:

PHP_FUNCTION(date_default_timezone_get)
{
    timelib_tzinfo *default_tz;

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

Oke, jadi apa get_timezone_info terlihat seperti? Ini:

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;
}

Bagaimana dengan guess_timezone? Sini ini:

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";
}

Oke, jadi bagaimana itu berinteraksi dengan date_default_timezone_set? Mari lihat fungsi itu:

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;
}

Singkat cerita: jika Anda menelepon date_default_timezone_set sekali, lalu guess_timezone mengambil jalan cepat membaca dari timezone variabel (kondisional pertama dipenuhi, dan ia segera kembali). Jika tidak, dibutuhkan waktu untuk menentukan zona waktu default, yang tidak di-cache (saya kira untuk kesederhanaan), dan jika Anda melakukannya dalam satu lingkaran, penundaan akan mulai muncul.


41
2018-04-05 18:56



Saya membayangkan itu harus menentukan zona waktu untuk dirinya sendiri setiap kali itu dipanggil kecuali secara eksplisit ditentukan, yang menambah runtime fungsi.

Tapi benarkah itu penting? Berapa banyak skrip yang kemungkinan Anda buat pada tanggal panggilan tersebut () 100.000 kali per berlari?


2
2018-04-05 18:50