PHP Memesona menghiasi tanda kutip


Adapun optimasi mikro PHP, dengan mengganti tanda kutip ganda dengan tanda kutip tunggal, begitu banyak salinan yang rusak sehingga cukup bermasalah untuk membuat aliran baru. Tapi saya akan coba.

Dalam artikel ini hanya akan ada satu tolok ukur, di mana itu akan menjadi tanpa itu, dan penekanan utama adalah pada menganalisis bagaimana itu diatur di dalam.

Penafian


  1. Segala sesuatu yang diuraikan di bawah ini, untuk sebagian besar, menghemat nanodetik, dan dalam praktiknya tidak akan melakukan apa-apa selain kehilangan waktu yang hilang pada optimasi mikro tersebut. Ini terutama berlaku untuk "optimisasi" waktu kompilasi.
  2. Saya akan memotong kode dan output secara maksimal, hanya menyisakan esensi.
  3. Saat menulis artikel, saya menggunakan PHP 7.2

Diperlukan Pendahuluan


Sebuah string dalam tanda kutip ganda pada tahap kompilasi diproses sedikit berbeda dari string dalam tanda kutip tunggal.

Kutipan tunggal akan diuraikan seperti ini:

statement -> expr -> scalar -> dereferencable_scalar -> T_CONSTANT_ENCAPSED_STRING 

Gandakan jadi:

 statement -> expr -> scalar -> '"' encaps_list '"' ->        ,  ,     

Dalam artikel tentang optimasi mikro PHP, sering kali ada saran untuk tidak menggunakan cetak , karena lebih lambat daripada gema . Mari kita lihat bagaimana mereka diurutkan.

Parsing echo :

 statement -> T_ECHO echo_expr_list -> echo_expr_list ->  echo_expr -> expr 

Parsing cetak :

 statement -> expr -> T_PRINT expr -> expr ( ) 

Yaitu secara umum, ya, gema terdeteksi langkah sebelumnya dan langkah ini, harus dicatat, cukup sulit.

Agar tidak menonjolkan perhatian sekali lagi selama artikel, kami akan ingat bahwa pada tahap kompilasi, tanda kutip ganda kehilangan satu, dan cetak kehilangan gema . Juga, jangan lupa bahwa dalam kasus terburuk, ini tentang nanodetik.

Nah, agar tidak bangun dua kali. Berikut adalah fungsi-fungsi berbeda yang menyusun cetak dan gema :

 1 - void zend_compile_print(znode *result, zend_ast *ast) /* {{{ */ 1 + void zend_compile_echo(zend_ast *ast) /* {{{ */ 2 2 { 3 3 zend_op *opline; 4 4 zend_ast *expr_ast = ast->child[0]; 5 5 6 6 znode expr_node; 7 7 zend_compile_expr(&expr_node, expr_ast); 8 8 9 9 opline = zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); 10 - opline->extended_value = 1; 11 - 12 - result->op_type = IS_CONST; 13 - ZVAL_LONG(&result->u.constant, 1); 10 + opline->extended_value = 0; 14 11 } 

Yah, Anda mengerti - mereka identik dalam fungsi, tetapi cetak juga menghasilkan konstanta sama dengan 1. Saya pikir pada topik ini dengan cetak Anda dapat menutup dan melupakannya selamanya.

Garis sederhana, tanpa embel-embel


Strings echo 'Some string'; dan echo "Some string"; akan dibagi hampir identik menjadi 2 (tolak P2) token.

 T_ECHO: echo T_ENCAPSED_AND_WHITESPACE/T_CONSTANT_ENCAPSED_STRING: "Some string" 

Selain itu, untuk tanda kutip tunggal akan selalu ada T_CONSTANT_ENCAPSED_STRING, dan untuk tanda kutip ganda, jika diinginkan. Jika ada spasi di baris, maka T_ENCAPSED_AND_WHITESPACE.

Opcodes akan mudah dipermalukan dan benar-benar identik:

 line #* EIO op fetch ext return operands ----------------------------------------------------------- 4 0 E > ECHO 'Some string' 


Kesimpulan


Jika Anda ingin menyimpan beberapa siklus prosesor pada tahap kompilasi, maka, untuk string konstan, gunakan tanda kutip tunggal.

Garis dinamis


Ada 4 opsi.

 echo "Hello $name! Have a nice day!"; echo 'Hello '.$name.'! Have a nice day!'; echo 'Hello ', $name, '! Have a nice day!'; printf ('Hello %s! Have a nice day!', $name); 

Untuk opsi pertama:

 T_ECHO: echo T_ENCAPSED_AND_WHITESPACE: Hello T_VARIABLE: $name T_ENCAPSED_AND_WHITESPACE: ! Have a nice day! 

Untuk yang kedua (untuk yang ketiga juga, hanya sebagai ganti periode akan ada koma):

 T_ECHO: echo T_CONSTANT_ENCAPSED_STRING: 'Hello ' string: . T_VARIABLE: $name string: . T_CONSTANT_ENCAPSED_STRING: '! Have a nice day!' 

Untuk yang keempat:

 T_STRING: printf T_CONSTANT_ENCAPSED_STRING: 'Hello %s! Have a nice day!' string: , T_VARIABLE: $name 

Tetapi dengan opcodes semuanya akan jauh lebih menghibur.

Yang pertama:

 echo "Hello $name! Have a nice day!"; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 ROPE_INIT 3 ~3 'Hello+' 2 ROPE_ADD 1 ~3 ~3, !0 3 ROPE_END 2 ~2 ~3, '%21+Have+a+nice+day%21' 4 ECHO ~2 

Kedua:

 echo 'Hello '.$name.'! Have a nice day!'; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 CONCAT ~2 'Hello+', !0 2 CONCAT ~3 ~2, '%21+Have+a+nice+day%21' 3 ECHO ~3 

Ketiga:

 echo 'Hello ', $name, '! Have a nice day!'; line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 ECHO 'Hello+' 2 ECHO !0 3 ECHO '%21+Have+a+nice+day%21' 

Keempat:

 printf ('Hello %s! Have a nice day!', $name); line #* EIO op fetch ext return operands ----------------------------------------------------------- 3 0 E > ASSIGN !0, 'Vasya' 4 1 INIT_FCALL 'printf' 2 SEND_VAL 'Hello+%25s%21+Have+a+nice+day%21' 3 SEND_VAR !0 4 DO_ICALL 

Akal sehat memberi tahu kita bahwa opsi dengan `printf` akan hilang dalam kecepatan pada tiga yang pertama (terutama karena pada akhirnya masih ada ECHO yang sama), jadi kita akan meninggalkannya untuk tugas-tugas di mana pemformatan diperlukan dan kita tidak akan mengingat lebih banyak dalam artikel ini.

Tampaknya pilihan ketiga adalah yang tercepat - untuk mencetak tiga baris berturut-turut tanpa penggabungan, ROPE aneh dan penciptaan variabel tambahan. Tapi tidak sesederhana itu. Fungsi cetak dalam PHP tentu saja bukan Rocket Science, tetapi tidak berarti f-C-shny dangkal . Siapa yang peduli - bola terurai dimulai dengan php_output_write dalam file utama / output.c .

CONCAT. Semuanya sederhana di sini - kami mengonversi, jika perlu, argumen menjadi string dan membuat zend_string baru menggunakan memcpy cepat. Satu-satunya negatif adalah bahwa dengan rangkaian panjang rangkaian untuk setiap operasi, baris baru akan dibuat dengan menggeser byte yang sama dari satu tempat ke tempat lain.

Tetapi dengan ROPE_INIT, ROPE_ADD dan ROPE_END semuanya jauh lebih menarik. Ikuti tangan:

  1. ROPE_INIT (ext = 3, return = ~ 3, operand = 'Hello +')
    Kami mengalokasikan "tali" dari tiga slot (ext), meletakkan string 'Hello +' (operan) di slot 0 dan mengembalikan variabel sementara ~ 3 (kembali) berisi "tali".
  2. ROPE_ADD (ext = 1, return = ~ 3, operan = ~ 3 ,! 0)
    Kami menempatkan di slot 1 (ext) dari "tali" ~ 3 (operan) string 'Vasya', yang diperoleh dari variabel! 0 (operan) dan mengembalikan "tali" ~ 3 (kembali).
  3. ROPE_END (ext = 2, return = ~ 2, operan = ~ 3, '% 21 + Semoga + bagus + hari% 21')
    Kami menempatkan baris '% 21 + Memiliki + bagus + hari% 21' (operan) di slot 2 (ext), setelah itu kami membuat zend_string dari ukuran yang diperlukan dan menyalin semua "tali" slot ke dalamnya pada gilirannya dengan memcpy yang sama.

Perlu dicatat secara terpisah bahwa dalam kasus konstanta dan variabel sementara, tautan ke data akan ditempatkan di slot, dan tidak akan ada penyalinan yang tidak perlu.

Menurut saya, cukup elegan. :)

Mari patokan. Sebagai sumber data, kami mengambil file zend_vm_execute.h (IMHO ini benar) untuk 71 ribu baris dan mencetaknya dalam 100 cara untuk 100 lintasan, menjatuhkan nilai minimum dan maksimum (setiap pengukuran dimulai 10 kali, memilih opsi yang paling umum):

 <?php $file = explode("\n", file_get_contents("C:\projects\C\php-src\Zend\zend_vm_execute.h")); $out = []; for ($c = 0; $c < 100; $c++) { $start = microtime(true); ob_start(); $i = 0; foreach ($file as $line) { $i++; // echo 'line: ', $i, 'text: ', $line; // echo 'line: ' . $i . 'text: ' . $line; // echo "line: $i text: $line"; // printf('line: %d text: %s', $i, $line); } ob_end_clean(); $out[] = (microtime(true) - $start); } $min = min($out); $max = max($out); echo (array_sum($out) - $min - $max) / 98; 

Apa yang kita ukurWaktu rata-rata dalam detik
"Tali"0,0129
Beberapa ECHO0,0135
Rangkaian0,0158
printf , untuk kelengkapan0,0245

Kesimpulan


  1. Untuk string dengan substitusi sederhana, tiba-tiba, tanda kutip ganda lebih optimal daripada tanda kutip tunggal dengan penggabungan. Dan semakin panjang garis yang digunakan, semakin besar keuntungannya.
  2. Argumen dipisahkan oleh koma ... Ada banyak nuansa. Dengan pengukuran, ini lebih cepat daripada penggabungan dan lebih lambat dari "tali", tetapi ada terlalu banyak "variabel" yang terkait dengan input / output.

Kesimpulan


Sulit bagi saya untuk memikirkan situasi di mana kebutuhan untuk optimasi mikro seperti itu mungkin muncul. Ketika memilih pendekatan ini atau itu, lebih masuk akal untuk dipandu oleh prinsip-prinsip lain - misalnya, keterbacaan kode atau gaya pengkodean yang diadopsi oleh perusahaan Anda.

Bagi saya pribadi, saya tidak suka pendekatan gabungan karena tampilan vyrviglazny, meskipun dalam beberapa kasus itu dapat dibenarkan.

PS Jika analisis semacam ini menarik - beri tahu saya - ada banyak lagi yang jauh dari selalu tidak ambigu dan jelas: array objek VS, foreach VS sementara VS untuk, pilihan Anda ... :)

Sedikit penjelasan dari membaca komentar


Sintaks HEREDOC dan "string kompleks" (di mana variabel berada di dalam kurung di dalam) adalah string yang dikutip ganda dan dikompilasi dengan cara yang persis sama.

Campuran PHP dengan HTML seperti ini:
 <?php $name = 'Vasya';?>Hello <?=$name?>! Have a nice day! 

Ini hanya 3 gema berturut-turut.

Source: https://habr.com/ru/post/id447416/


All Articles