Apa yang salah dengan artikel populer yang mengatakan bahwa foo lebih cepat daripada bar?

Catatan Penerjemah: Saya juga berpikir bahwa waktu untuk artikel adalah "Apa yang lebih cepat - kutipan ganda atau tunggal?" Butuh 10 tahun yang lalu. Tapi di sini artikel serupa ("Trik kinerja apa yang sebenarnya berhasil") baru-baru ini mengumpulkan peringkat yang relatif tinggi pada Reddit dan bahkan masuk ke intisari PHP di Habré. Oleh karena itu, saya memutuskan untuk menerjemahkan artikel itu dengan analisis kritis tentang ini dan "tes" serupa.


Ada banyak artikel (dan bahkan seluruh situs) yang ditujukan untuk meluncurkan berbagai tes membandingkan kinerja berbagai konstruksi sintaksis dan menyatakan berdasarkan ini bahwa yang satu lebih cepat daripada yang lain.


Masalah utama


Tes semacam itu salah karena berbagai alasan, mulai dari mengajukan pertanyaan hingga kesalahan implementasi. Tetapi yang paling penting - tes semacam itu tidak ada artinya dan pada saat yang sama berbahaya.


  • Mereka tidak ada artinya karena mereka tidak memiliki nilai praktis. Tidak ada proyek nyata yang pernah dipercepat menggunakan metode yang disediakan dalam artikel tersebut. Hanya karena bukan perbedaan dalam hal sintaksis untuk kinerja, tetapi pengolahan data.
  • Mereka berbahaya karena menyebabkan munculnya takhayul yang paling liar dan - bahkan lebih buruk - mendorong pembaca yang tidak menaruh curiga untuk menulis kode yang buruk, berpikir bahwa mereka "mengoptimalkan" itu.

Itu sudah cukup untuk menutup pertanyaan. Tetapi bahkan jika Anda menerima aturan permainan dan berpura-pura bahwa "tes" ini setidaknya memiliki beberapa perasaan, ternyata hasilnya berkurang hanya untuk menunjukkan kurangnya pengetahuan penguji dan kurangnya pengalaman apa pun.


Tunggal versus ganda


Ambil kutipan terkenal, "tunggal versus ganda". Tentu saja, tidak ada kutipan yang lebih cepat. Pertama, ada yang namanya cache opcode , yang menyimpan hasil parsing skrip PHP dalam cache. Dalam hal ini, kode PHP disimpan dalam format opcode, di mana literal string yang sama disimpan sebagai entitas yang benar-benar identik, terlepas dari kutipan mana yang digunakan dalam skrip PHP. Yang berarti bahkan tidak ada perbedaan teoretis dalam kinerja.


Tetapi bahkan jika kita tidak menggunakan opcode cache (walaupun kita seharusnya, jika tugas kita adalah benar-benar meningkatkan kinerja), kita akan menemukan bahwa perbedaan dalam kode parsing sangat kecil (beberapa transisi kondisional yang membandingkan karakter byte tunggal, beberapa instruksi prosesor) yang akan benar-benar tidak terdeteksi. Ini berarti bahwa setiap hasil yang diperoleh hanya akan menunjukkan masalah di lingkungan pengujian. Ada artikel yang sangat rinci, Membantah Mitos Kinerja Kutipan Tunggal dari pengembang inti PHP Nikita Popov, yang mengurai masalah ini secara rinci. Namun demikian, seorang penguji yang energetik muncul hampir setiap bulan untuk mengungkapkan kepada masyarakat suatu "perbedaan" imajiner dalam kinerja.


Inkonsistensi logis


Beberapa tes pada umumnya tidak berarti, hanya dari sudut pandang mengajukan pertanyaan: Misalnya, tes berjudul "Apakah melempar benar-benar operasi yang sangat mahal?" ini pada dasarnya pertanyaan "Apakah benar memproses kesalahan akan lebih mahal daripada tidak memproses?". Apakah kamu serius? Tentu saja, menambahkan beberapa fungsionalitas dasar ke kode akan membuatnya "lebih lambat". Tetapi ini tidak berarti bahwa fungsionalitas baru tidak perlu ditambahkan sama sekali, dengan dalih yang konyol. Jika Anda berbicara seperti itu, maka program tercepat adalah yang tidak melakukan apa-apa! Program harus bermanfaat dan bekerja tanpa kesalahan sejak awal. Dan hanya setelah ini tercapai, dan hanya jika itu bekerja lambat, itu perlu dioptimalkan. Tetapi jika pertanyaan itu sendiri tidak masuk akal, lalu mengapa repot-repot menguji kinerja? Lucu sekali bahwa penguji tidak dapat menerapkan dengan benar bahkan pengujian yang tidak masuk akal ini, yang akan ditampilkan di bagian selanjutnya.


Atau contoh lain, tes berjudul "Apakah $row[id] benar-benar lebih lambat dari $row['id'] ?" ini pada dasarnya pertanyaan "Kode mana yang lebih cepat - kode yang berfungsi dengan kesalahan, atau tanpa?" (karena menulis id tanpa tanda kutip dalam hal ini adalah kesalahan tingkat E_NOTICE , dan penulisan seperti itu akan ditinggalkan dalam versi PHP yang akan datang). WTF? Apa gunanya mengukur kinerja kode kesalahan secara umum? Kesalahan harus diperbaiki hanya karena itu adalah kesalahan, dan bukan karena itu akan membuat kode berjalan lebih lambat. Lucu sekali bahwa penguji tidak dapat menerapkan dengan benar bahkan pengujian yang tidak masuk akal ini, yang akan diperlihatkan di bagian selanjutnya.


Kualitas tes


Dan lagi - bahkan tes yang tidak berguna tanpa sadar harus konsisten, konsisten - yaitu, mengukur nilai yang sebanding. Tetapi, sebagai aturan, tes tersebut dilakukan dengan tumit kiri, dan sebagai hasilnya, hasil yang diperoleh tidak berarti dan tidak relevan dengan tugas tersebut.


Sebagai contoh, penguji bodoh kami melakukan pengukuran "penggunaan operator try..catch berlebihan". Tetapi dalam tes saat ini, ia mengukur tidak hanya try catch , tetapi juga throw , melemparkan pengecualian pada setiap iterasi dari loop. Tetapi tes semacam itu tidak benar, karena dalam kehidupan nyata kesalahan tidak terjadi pada setiap eksekusi skrip.


Tentu saja, tes tidak boleh dilakukan pada versi beta PHP dan tidak boleh membandingkan solusi utama dengan yang eksperimental. Dan jika penguji berusaha untuk membandingkan "kecepatan parsing json dan xml", maka ia tidak boleh menggunakan fungsi eksperimental dalam tes.


Beberapa tes hanya menunjukkan kesalahpahaman total oleh penguji tugas yang ditetapkan olehnya. Contoh serupa dari artikel yang baru-baru ini diterbitkan telah disebutkan di atas: penulis tes mencoba mencari tahu apakah kode yang menyebabkan kesalahan ("Penggunaan konstanta tidak terdefinisi") akan lebih lambat daripada kode tanpa kesalahan (yang menggunakan string literal yang benar secara sintaksis), tetapi gagal bahkan dengan tes yang jelas tidak berarti ini, membandingkan kinerja angka yang dikutip dengan kinerja angka yang ditulis tanpa tanda kutip. Tentu saja, Anda dapat menulis angka tanpa tanda kutip dalam PHP (tidak seperti string), dan sebagai hasilnya, penulis menguji fungsi yang sama sekali berbeda, menerima hasil yang salah.


Ada masalah lain yang perlu dipertimbangkan, seperti lingkungan pengujian. Ada ekstensi ke PHP seperti XDebug yang dapat memiliki dampak yang sangat besar pada hasil tes. Atau cache opcode yang telah disebutkan, yang harus dimasukkan selama tes kinerja sehingga hasil tes setidaknya masuk akal.


Bagaimana pengujian dilakukan juga penting. Karena proses PHP mati sepenuhnya setelah setiap permintaan, masuk akal untuk menguji kinerja seluruh siklus hidup, mulai dari membuat koneksi ke server web dan berakhir dengan menutup koneksi ini. Ada utilitas seperti benchmark Apache atau Pengepungan yang memungkinkan Anda melakukan ini.


Peningkatan kinerja nyata


Semua ini baik, tetapi kesimpulan apa yang harus diambil pembaca dari artikel ini? Apa tes kinerja yang tidak berguna menurut definisi? Tentu saja tidak. Tapi yang terpenting adalah alasan mereka harus memulai. Menguji dari awal adalah buang-buang waktu. Harus selalu ada alasan khusus untuk menjalankan tes kinerja. Dan alasan ini disebut "profiling . " Ketika aplikasi Anda mulai berjalan lambat, Anda perlu melakukan profil, yang berarti mengukur kecepatan berbagai bagian kode untuk menemukan yang paling lambat. Setelah situs semacam itu ditemukan, kita harus menentukan penyebabnya. Paling sering, ini jauh lebih besar dari yang dibutuhkan, jumlah data yang diproses, atau permintaan ke sumber data eksternal. Untuk kasus pertama, optimasi akan terdiri dalam mengurangi jumlah data yang diproses, dan untuk kasus kedua, caching hasil kueri.


Misalnya, dalam hal kinerja, tidak ada bedanya apakah kita menggunakan loop yang ditentukan secara eksplisit atau fungsi PHP bawaan untuk memproses array (yang pada dasarnya hanya gula sintaksis). Yang benar-benar penting adalah jumlah data yang kami kirimkan untuk diproses. Jika terlalu besar, kita harus memotongnya, atau memindahkan pemrosesan di tempat lain (ke database). Ini akan memberi kita dorongan kinerja besar yang akan nyata . Sementara perbedaan antara metode memanggil loop untuk pemrosesan data tidak mungkin terlihat sama sekali.


Hanya setelah melakukan peningkatan kinerja wajib tersebut, atau jika kami tidak dapat memotong jumlah data yang diproses, kami dapat memulai pengujian kinerja. Tetapi sekali lagi, tes seperti itu tidak harus dilakukan dari awal. Untuk mulai membandingkan kinerja loop eksplisit dan fungsi inline, kita harus yakin bahwa loop adalah penyebab masalah, bukan isinya (spoiler: tentu saja, ini isinya).


Contoh terbaru dari praktik saya: dalam kode ada kueri menggunakan Doctrine Query Builder, yang seharusnya mengambil beberapa ribu parameter. Query itu sendiri cukup cepat, tetapi Doctrine membutuhkan waktu cukup lama untuk mencerna beberapa ribu parameter. Akibatnya, kueri itu ditulis ulang dalam SQL murni, dan parameter ditransfer ke metode execute () dari perpustakaan PDO, yang mengatasi dengan begitu banyak parameter hampir secara instan.


Apakah ini berarti saya tidak akan pernah menggunakan Doctrine Query Builder? Tentu saja tidak. Ini sempurna untuk 99% tugas, dan saya akan terus menggunakannya untuk semua pertanyaan. Dan hanya dalam kasus luar biasa layak menggunakan metode yang kurang nyaman, tetapi lebih produktif.


Permintaan dan parameter untuk seleksi ini dibangun dalam satu lingkaran. Jika saya punya ide bodoh untuk berurusan dengan bagaimana siklus itu disebut, maka saya hanya akan kehilangan waktu tanpa hasil positif. Dan ini adalah inti dari semua optimasi kinerja: untuk mengoptimalkan hanya kode yang berjalan lambat dalam kasus khusus Anda. Dan bukan kode yang dianggap lambat sejak lama, di galaksi yang jauh, jauh, atau kode yang terpikir oleh seseorang untuk memanggil lambat berdasarkan tes yang tidak berarti.

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


All Articles