Apakah kode latensi rendah terkadang harus "jelek"?


21

(Ini terutama ditujukan bagi mereka yang memiliki pengetahuan khusus tentang sistem latensi rendah, untuk menghindari orang yang hanya menjawab dengan pendapat tidak berdasar).

Apakah Anda merasa ada pertukaran antara menulis kode berorientasi objek "bagus" dan menulis kode latensi rendah yang sangat cepat? Misalnya, menghindari fungsi virtual dalam C ++ / overhead polimorfisme dll- penulisan ulang kode yang terlihat buruk, tetapi apakah sangat cepat, dll?

Itu masuk akal - siapa yang peduli jika itu terlihat jelek (asalkan dapat dipertahankan) - jika Anda membutuhkan kecepatan, Anda perlu kecepatan?

Saya akan tertarik untuk mendengar dari orang-orang yang telah bekerja di bidang tersebut.


1
@ user997112: Alasan penutupannya cukup jelas. Ia mengatakan: "Kami mengharapkan jawaban didukung oleh fakta, referensi, atau keahlian khusus, tetapi pertanyaan ini kemungkinan akan mengundang debat, argumen, pemungutan suara, atau diskusi panjang. Tidak berarti bahwa jawaban itu benar, tetapi itulah penutupnya. alasan dipilih oleh ketiga pemilih dekat
Robert Harvey

Secara anekdot, saya akan mengatakan bahwa alasan pertanyaan ini menarik suara yang dekat adalah bahwa itu mungkin dianggap sebagai kata-kata kasar terselubung (meskipun saya tidak berpikir begitu).
Robert Harvey

8
Saya akan mencuat: Saya memberikan suara ketiga untuk menutup sebagai "tidak konstruktif" karena saya pikir si penanya cukup banyak menjawab pertanyaannya sendiri. Kode "cantik" yang tidak berjalan cukup cepat untuk melakukan pekerjaan gagal memenuhi persyaratan latensi. Kode "jelek" yang berjalan cukup cepat dapat dibuat lebih mudah dikelola melalui dokumentasi yang baik. Bagaimana Anda mengukur kecantikan atau kejelekan adalah topik untuk pertanyaan lain.
Blrfl

1
Kode sumber untuk LMAX's Disruptor tidak terlalu jelek. Ada beberapa bagian 'neraka dengan model keamanan Java' (kelas tidak aman) dan beberapa modifikasi khusus perangkat keras (variabel cache-line cache) tetapi IMO sangat mudah dibaca.
James

5
@ Carson63000, user1598390 dan siapa pun yang tertarik: Jika pertanyaan berakhir dengan tertutup, jangan ragu untuk bertanya tentang penutupan di situs Meta kami , ada gunanya membahas penutupan di komentar, terutama penutupan yang belum terjadi . Juga, perlu diingat bahwa setiap pertanyaan tertutup dapat dibuka kembali, ini bukan akhir dari dunia. Kecuali tentu saja jika bangsa Maya benar, dalam hal ini senang mengetahui kalian semua!
yannis

Jawaban:


31

Apakah Anda merasa ada trade-off antara menulis kode berorientasi objek "bagus" dan menulis kode latensi sangat rendah?

Iya nih.

Itu sebabnya ada ungkapan "optimasi prematur". Itu ada untuk memaksa pengembang untuk mengukur kinerja mereka, dan hanya mengoptimalkan kode yang akan membuat perbedaan dalam kinerja, sementara merancang arsitektur aplikasi mereka sejak awal sehingga tidak jatuh di bawah beban berat.

Dengan begitu, semaksimal mungkin, Anda bisa menjaga kode Anda yang cantik, dirancang dengan baik, berorientasi objek, dan hanya mengoptimalkan dengan kode jelek bagian-bagian kecil yang penting.


15
"Jadikan berhasil, lalu buat cepat". Jawaban ini cukup banyak mencakup semua yang saya pikir katakan ketika saya membaca pertanyaan.
Carson63000

13
Saya akan menambahkan "Measure, don't guess"
Martijn Verburg

1
Saya pikir menempatkan beberapa ke dalam penghindaran pekerjaan dasar saat Anda pergi bermanfaat selama itu tidak datang dengan mengorbankan keterbacaan. Menjaga segala sesuatunya singkat, dapat dibaca dan melakukan hanya melakukan hal-hal yang jelas perlu mereka lakukan mengarah pada banyak kemenangan jangka panjang tidak langsung seperti pengembang lain mengetahui apa yang harus dilakukan dengan kode Anda sehingga mereka tidak menduplikasi usaha atau membuat asumsi buruk tentang cara kerjanya.
Erik Reppen

1
Pada "optimasi prematur" - itu masih berlaku bahkan jika kode yang dioptimalkan akan sama "baik" dengan kode yang tidak dioptimalkan. Intinya adalah untuk tidak membuang waktu dengan tujuan untuk kecepatan / apa pun yang tidak perlu Anda capai. Sebenarnya optimasi tidak selalu tentang kecepatan, dan bisa dibilang ada yang namanya optimasi yang tidak perlu untuk "kecantikan". Kode Anda tidak perlu menjadi karya seni yang hebat agar dapat dibaca dan dipelihara.
Steve314

Saya kedua @ Steve314. Saya adalah pemimpin kinerja pada suatu produk dan sering menemukan kode yang sangat rumit yang asalnya saya dapat melacak kembali ke beberapa jenis optimasi kinerja. Menyederhanakan kode itu sering menunjukkan peningkatan kinerja yang signifikan. Salah satu contohnya berubah menjadi peningkatan kinerja 5x ketika saya menyederhanakannya (pengurangan bersih ribuan baris kode). Jelas, tidak ada yang meluangkan waktu untuk benar-benar mengukur dan hanya melakukan optimasi prematur dari apa yang mereka pikir mungkin kode lambat .
Brandon

5

Ya, contoh yang saya berikan bukan C ++ vs. Java tetapi Assembly vs COBOL seperti yang saya tahu.

Kedua bahasa sangat cepat, tetapi, bahkan COBOL ketika dikompilasi memiliki lebih banyak instruksi yang ditempatkan ke set instruksi yang tidak perlu harus ada di sana vs menulis instruksi itu sendiri di Majelis.

Gagasan yang sama dapat diterapkan secara langsung pada pertanyaan Anda tentang penulisan "kode yang tampak jelek" vs. menggunakan pewarisan / polimorfisme dalam C ++. Saya percaya perlu untuk menulis kode yang terlihat jelek, jika pengguna akhir membutuhkan kerangka waktu transaksi kedua, maka tugas kita sebagai programmer untuk memberi mereka bahwa tidak peduli bagaimana itu terjadi.

Karena itu, penggunaan komentar secara bebas akan meningkatkan fungsionalitas & kemudahan programmer, tidak peduli seberapa buruk kodenya.


3

Ya, ada trade-off. Maksud saya, kode yang lebih cepat dan lebih buruk tidak perlu lebih baik - manfaat kuantitatif dari "kode cepat" perlu dipertimbangkan terhadap kompleksitas pemeliharaan perubahan kode yang diperlukan untuk mencapai kecepatan itu.

Pertukarannya berasal dari biaya bisnis. Kode yang lebih kompleks membutuhkan programmer yang lebih terampil (dan programmer dengan keahlian yang lebih terfokus, seperti yang memiliki arsitektur CPU dan pengetahuan desain), membutuhkan lebih banyak waktu untuk membaca dan memahami kode dan untuk memperbaiki bug. Biaya bisnis untuk mengembangkan dan memelihara kode tersebut dapat berkisar antara 10x - 100x dibandingkan dengan kode yang ditulis secara normal.

Biaya pemeliharaan ini dapat dibenarkan di beberapa industri , di mana pelanggan bersedia membayar premi yang sangat tinggi untuk perangkat lunak yang sangat cepat.

Beberapa optimasi kecepatan menghasilkan ROI lebih baik daripada yang lain. Yaitu, beberapa teknik optimasi dapat diterapkan dengan dampak yang lebih rendah pada pemeliharaan kode (mempertahankan struktur tingkat yang lebih tinggi dan keterbacaan tingkat yang lebih rendah) dibandingkan dengan kode yang ditulis secara normal.

Dengan demikian, pemilik bisnis harus:

  • Lihatlah biaya dan manfaatnya,
  • Lakukan pengukuran dan perhitungan
    • Mintalah programmer mengukur kecepatan program
    • Mintalah programmer memperkirakan waktu pengembangan yang dibutuhkan untuk optimasi
    • Buat perkiraan sendiri tentang peningkatan pendapatan dari perangkat lunak yang lebih cepat
    • Memiliki arsitek perangkat lunak atau manajer QA mengukur secara kualitatif kelemahan dari berkurangnya intuitif dan keterbacaan kode sumber
  • Dan memprioritaskan buah rendah dari optimasi perangkat lunak.

Pertukaran ini sangat spesifik untuk keadaan tertentu.

Ini tidak dapat diputuskan secara optimal tanpa partisipasi manajer dan pemilik produk.

Ini sangat spesifik untuk platform. Misalnya, CPU desktop dan seluler memiliki pertimbangan berbeda. Aplikasi server dan klien juga memiliki pertimbangan berbeda.


Ya, umumnya benar bahwa kode yang lebih cepat terlihat berbeda dari kode yang biasanya ditulis. Kode apa pun yang berbeda akan membutuhkan lebih banyak waktu untuk membaca. Apakah itu menyiratkan keburukan ada di mata yang melihatnya.

Teknik yang saya miliki beberapa paparan adalah: (tanpa mencoba mengklaim tingkat keahlian) optimasi vektor pendek (SIMD), paralelisme tugas berbutir halus, pra-alokasi memori dan penggunaan kembali objek.

SIMD biasanya memiliki dampak parah pada keterbacaan tingkat rendah, meskipun biasanya tidak memerlukan perubahan struktural tingkat tinggi (asalkan API dirancang dengan mempertimbangkan pencegahan kemacetan).

Beberapa algoritma dapat ditransformasikan ke dalam SIMD dengan mudah (secara memalukan- vektorizable). Beberapa algoritma memerlukan lebih banyak pengaturan komputasi untuk menggunakan SIMD. Dalam kasus-kasus ekstrem seperti paralelisme SIMD wavefront, algoritma yang sepenuhnya baru (dan implementasi yang dapat dipatenkan) harus ditulis untuk memanfaatkan.

Paralisasi tugas berbutir halus membutuhkan penataan ulang algoritma ke dalam grafik aliran data, dan berulang kali menerapkan dekomposisi fungsional (komputasi) ke algoritma sampai tidak ada keuntungan margin lebih lanjut yang dapat diperoleh. Tahap dekomposisi biasanya dirantai dengan gaya kelanjutan, sebuah konsep yang dipinjam dari pemrograman fungsional.

Dengan dekomposisi fungsional (komputasi), algoritma yang seharusnya dapat ditulis secara normal dalam urutan linier dan jelas secara konseptual (baris kode yang dapat dieksekusi dalam urutan yang sama seperti yang dituliskan) harus dipecah menjadi beberapa fragmen, dan didistribusikan ke dalam beberapa fungsi atau kelas. (Lihat objektifikasi algoritma, di bawah.) Perubahan ini akan sangat menghalangi sesama programmer yang tidak terbiasa dengan proses desain dekomposisi yang memunculkan kode semacam itu.

Untuk membuat kode tersebut dapat dipertahankan, penulis kode tersebut harus menulis dokumentasi rumit dari algoritma - jauh melampaui jenis komentar kode atau diagram UML yang dilakukan untuk kode yang ditulis secara normal. Ini mirip dengan cara para peneliti menulis makalah akademis mereka.


Tidak, kode cepat tidak harus bertentangan dengan orientasi objek.

Dengan kata lain, dimungkinkan untuk mengimplementasikan perangkat lunak yang sangat cepat yang masih berorientasi objek. Namun, menuju ujung bawah implementasi itu (pada tingkat mur dan baut di mana mayoritas komputasi terjadi), desain objek dapat menyimpang secara signifikan dari desain yang diperoleh dari desain berorientasi objek (OOD). Desain tingkat rendah diarahkan untuk algoritma-objektifikasi.

Beberapa manfaat pemrograman berorientasi objek (OOP), seperti enkapsulasi, polimorfisme, dan komposisi, masih dapat dipetik dari algoritma-objektifikasi tingkat rendah. Ini adalah alasan utama untuk menggunakan OOP di tingkat ini.

Sebagian besar manfaat desain berorientasi objek (OOD) hilang. Yang paling penting, tidak ada intuitif dalam desain tingkat rendah. Seorang sesama programmer tidak dapat belajar bagaimana bekerja dengan kode tingkat lebih rendah tanpa terlebih dahulu memahami sepenuhnya bagaimana algoritma telah ditransformasikan dan didekomposisi di tempat pertama, dan pemahaman ini tidak dapat diperoleh dari kode yang dihasilkan.


2

Ya kadang-kadang kode harus "jelek" untuk membuatnya bekerja dalam waktu yang diperlukan, semua kode tidak harus jelek sekalipun. Kinerja harus diuji dan diprofilkan sebelumnya untuk menemukan bit kode yang perlu "jelek" dan bagian-bagian itu harus dicatat dengan komentar sehingga pengembang masa depan tahu apa yang sengaja jelek dan apa yang hanya kemalasan. Jika seseorang menulis banyak kode yang dirancang dengan buruk dan mengklaim alasan kinerja, buat mereka membuktikannya.

Kecepatan sama pentingnya dengan persyaratan lain dari suatu program, memberikan koreksi yang salah untuk rudal yang dipandu setara dengan memberikan koreksi yang tepat setelah dampak. Maintainability selalu menjadi perhatian sekunder untuk kode kerja.


2

Beberapa penelitian saya telah melihat ekstrak menunjukkan bahwa kode mudah dibaca bersih sering lebih cepat daripada kode sulit dibaca lebih kompleks. Sebagian, ini karena cara pengoptimal dirancang. Mereka cenderung jauh lebih baik dalam mengoptimalkan variabel menjadi register, daripada melakukan hal yang sama dengan hasil penghitungan perantara. Urutan panjang penugasan menggunakan operator tunggal yang mengarah ke hasil akhir dapat dioptimalkan lebih baik daripada persamaan rumit yang panjang. Pengoptimal yang lebih baru mungkin telah mengurangi perbedaan antara kode bersih dan rumit, tetapi saya ragu mereka telah menghilangkannya.

Optimalisasi lain seperti loop membuka gulungan dapat ditambahkan dengan cara yang bersih saat diperlukan.

Setiap optimasi yang ditambahkan untuk meningkatkan kinerja harus disertai dengan komentar yang sesuai. Ini harus mencakup pernyataan bahwa itu ditambahkan sebagai optimasi, lebih disukai dengan ukuran kinerja sebelum dan sesudah.

Saya menemukan aturan 80/20 berlaku untuk kode yang saya optimalkan. Sebagai aturan praktis saya tidak mengoptimalkan apa pun yang tidak mengambil setidaknya 80% dari waktu. Saya kemudian bertujuan untuk (dan biasanya mencapai) peningkatan kinerja 10 kali lipat. Ini meningkatkan kinerja sekitar 4 kali lipat. Sebagian besar optimasi yang saya terapkan belum membuat kode secara signifikan kurang "indah". Jarak tempuh Anda mungkin beragam.


2

Jika dengan jelek, maksud Anda sulit untuk membaca / memahami pada tingkat di mana pengembang lain akan menggunakannya kembali atau perlu memahaminya, maka saya akan mengatakan, elegan, kode yang mudah dibaca akan hampir selalu akhirnya membuat Anda bersih. keuntungan kinerja dalam jangka panjang dalam aplikasi yang harus Anda pertahankan.

Kalau tidak, kadang-kadang ada cukup banyak kemenangan kinerja untuk membuatnya layak menempatkan jelek di kotak yang indah dengan antarmuka pembunuh di dalamnya tetapi dalam pengalaman saya, ini adalah dilema yang sangat langka.

Pikirkan tentang penghindaran pekerjaan dasar saat Anda pergi. Simpan trik misterius ketika masalah kinerja muncul dengan sendirinya. Dan jika Anda benar-benar harus menulis sesuatu yang hanya bisa dipahami seseorang melalui keakraban dengan pengoptimalan spesifik, lakukan apa yang dapat Anda lakukan untuk setidaknya membuat orang yang jelek mudah dipahami dari penggunaan kembali sudut pandang kode Anda. Kode yang berkinerja sangat buruk jarang melakukannya karena para pengembang berpikir terlalu keras tentang apa yang akan diwariskan oleh orang berikutnya, tetapi jika perubahan yang sering terjadi adalah satu-satunya yang konstan dari suatu aplikasi (kebanyakan aplikasi web dalam pengalaman saya), kode kaku / tidak fleksibel yang sulit untuk memodifikasi secara praktis meminta kekacauan panik untuk mulai muncul di seluruh basis kode Anda. Bersih dan ramping lebih baik untuk kinerja dalam jangka panjang.


Saya ingin menyarankan dua perubahan: (1) Ada tempat-tempat di mana kecepatan diperlukan. Di tempat-tempat itu, saya pikir lebih bermanfaat untuk membuat antarmuka mudah dipahami, daripada membuat implementasi mudah dimengerti, karena yang terakhir mungkin jauh lebih sulit. (2) "Kode yang berkinerja buruk jarang melakukannya ...", yang saya ingin ulangi sebagai "Penekanan kuat pada keanggunan dan kesederhanaan kode jarang menjadi penyebab kinerja yang menyedihkan. Yang pertama bahkan lebih penting jika sering perubahan diantisipasi, ... "
rwong

Implementasi adalah pilihan kata yang buruk dalam percakapan OOPish. Saya maksudkan dalam hal kemudahan digunakan kembali dan diedit. # 2, saya baru saja menambahkan kalimat untuk menetapkan bahwa 2 pada dasarnya adalah poin yang saya buat.
Erik Reppen

1

Kompleks dan jelek bukan hal yang sama. Kode yang memiliki banyak kasus khusus, yang dioptimalkan untuk mengeluarkan setiap tetes kinerja terakhir, dan yang terlihat pada awalnya seperti jalinan koneksi dan dependensi mungkin sebenarnya direkayasa dengan sangat hati-hati dan cukup indah setelah Anda memahaminya. Memang, jika kinerja (apakah diukur dalam hal latensi atau sesuatu yang lain) cukup penting untuk membenarkan kode yang sangat kompleks, maka kode tersebut harus dirancang dengan baik. Jika tidak, maka Anda tidak dapat memastikan bahwa semua kompleksitas itu benar-benar lebih baik daripada solusi yang lebih sederhana.

Kode jelek, bagi saya, adalah kode yang ceroboh, kurang dipertimbangkan, dan / atau tidak perlu rumit. Saya tidak berpikir Anda ingin semua fitur dalam kode yang harus dilakukan.


1

Apakah Anda merasa ada pertukaran antara menulis kode berorientasi objek "bagus" dan menulis kode latensi rendah yang sangat cepat? Misalnya, menghindari fungsi virtual dalam C ++ / overhead polimorfisme dll- penulisan ulang kode yang terlihat buruk, tetapi apakah sangat cepat, dll?

Saya bekerja di bidang yang sedikit lebih fokus pada throughput daripada latensi, tetapi sangat kritis terhadap kinerja, dan saya akan mengatakan "agak" .

Namun masalahnya adalah bahwa begitu banyak orang yang salah memahami kinerja mereka. Pemula sering mendapatkan semua yang salah, dan seluruh model konseptual mereka "biaya komputasi" perlu pengerjaan ulang, dengan hanya kompleksitas algoritme yang menjadi satu-satunya hal yang dapat mereka perbaiki. Perantara mendapatkan banyak hal yang salah. Para ahli salah.

Mengukur dengan alat yang akurat yang dapat memberikan metrik seperti cache yang hilang dan salah duga cabang adalah hal yang membuat semua orang dari semua tingkat keahlian di bidang ini dalam kendali.

Mengukur juga menunjukkan apa yang tidak optimal . Para ahli sering menghabiskan lebih sedikit waktu untuk mengoptimalkan daripada pemula, karena mereka mengoptimalkan hotspot yang benar diukur dan tidak mencoba untuk mengoptimalkan tusukan liar dalam gelap berdasarkan firasat tentang apa yang bisa lambat (yang, dalam bentuk ekstrim, dapat menggoda seseorang untuk mengoptimalkan mikro hanya tentang setiap baris lain dalam basis kode).

Merancang untuk Kinerja

Selain itu, kunci untuk mendesain kinerja berasal dari bagian desain , seperti dalam desain antarmuka. Salah satu masalah dengan pengalaman kurang adalah bahwa cenderung ada perubahan awal pada metrik implementasi absolut, seperti biaya panggilan fungsi tidak langsung dalam beberapa konteks umum, seolah-olah biaya (yang lebih baik dipahami dalam arti langsung dari titik pengoptimal) pandangan daripada sudut pandang percabangan) adalah alasan untuk menghindarinya di seluruh basis kode.

Biaya relatif . Meskipun ada biaya untuk pemanggilan fungsi tidak langsung, misalnya, semua biaya relatif. Jika Anda membayar biaya satu kali untuk memanggil fungsi yang melewati jutaan elemen, mengkhawatirkan biaya ini seperti menghabiskan berjam-jam menawar uang untuk membeli produk miliar dolar, hanya untuk menyimpulkan tidak membeli produk itu karena satu sen terlalu mahal.

Desain Antarmuka Lebih Kasar

Aspek desain antarmuka kinerja sering berusaha sebelumnya untuk mendorong biaya-biaya ini ke tingkat yang lebih kasar. Alih-alih membayar biaya abstraksi runtime untuk satu partikel, misalnya, kita mungkin mendorong biaya itu ke tingkat sistem partikel / emitor, secara efektif menjadikan partikel ke dalam detail implementasi dan / atau hanya data mentah dari pengumpulan partikel ini.

Jadi desain berorientasi objek tidak harus tidak kompatibel dengan mendesain untuk kinerja (apakah latensi atau throughput), tetapi mungkin ada godaan dalam bahasa yang berfokus padanya untuk memodelkan objek granular yang semakin kecil, dan di sana pengoptimal terbaru tidak dapat membantu. Itu tidak dapat melakukan hal-hal seperti menyatukan kelas yang mewakili satu titik dengan cara yang menghasilkan representasi SoA yang efisien untuk pola akses memori perangkat lunak. Kumpulan poin dengan desain antarmuka yang dimodelkan pada tingkat kekasaran menawarkan peluang itu, dan memungkinkan iterasi ke arah solusi yang lebih dan lebih optimal sesuai kebutuhan. Desain seperti ini dirancang untuk memori massal *.

* Catat fokus pada memori di sini dan bukan data , karena bekerja di area yang sangat kritis untuk waktu yang lama akan cenderung mengubah pandangan Anda tentang tipe data dan struktur data dan melihat bagaimana mereka terhubung ke memori. Pohon pencarian biner tidak lagi menjadi semata-mata tentang kompleksitas logaritmik dalam kasus-kasus seperti potongan memori yang mungkin berbeda dan tidak ramah cache untuk simpul pohon kecuali dibantu oleh pengalokasi tetap. Tampilan tidak mengabaikan kompleksitas algoritmik, tetapi melihatnya tidak lagi terlepas dari tata letak memori. Kita juga mulai melihat iterasi pekerjaan sebagai lebih banyak tentang iterasi akses memori. *

Banyak desain yang sangat penting untuk kinerja sebenarnya sangat kompatibel dengan konsep desain antarmuka tingkat tinggi yang mudah dipahami dan digunakan manusia. Perbedaannya adalah bahwa "level tinggi" dalam konteks ini adalah tentang agregasi massal memori, sebuah antarmuka yang dimodelkan untuk koleksi data yang berpotensi besar, dan dengan implementasi di bawah kap yang mungkin levelnya cukup rendah. Analogi visual mungkin adalah mobil yang benar-benar nyaman dan mudah dikendarai dan dipegang serta sangat aman saat berjalan dengan kecepatan suara, tetapi jika Anda membuka penutupnya, ada sedikit setan api yang bernapas di dalam.

Dengan desain yang lebih kasar juga cenderung menjadi cara yang lebih mudah untuk memberikan pola penguncian yang lebih efisien dan mengeksploitasi paralelisme dalam kode (multithreading adalah subjek lengkap yang saya akan lewati di sini).

Memory Pool

Aspek kritis dari pemrograman latensi rendah mungkin akan menjadi kontrol yang sangat eksplisit atas memori untuk meningkatkan lokalitas referensi serta hanya kecepatan umum mengalokasikan dan membatalkan alokasi memori. Memori pengalokasi pengalokasi khusus sebenarnya menggaungkan jenis pola pikir desain yang sama seperti yang kami gambarkan. Ini dirancang untuk massal ; itu dirancang pada tingkat kasar. Ini mengalokasikan memori dalam blok besar dan menyatukan memori yang sudah dialokasikan dalam potongan kecil.

Idenya persis sama dengan mendorong hal-hal yang mahal (mengalokasikan potongan memori terhadap pengalokasi tujuan umum, misalnya) ke tingkat yang lebih kasar dan lebih kasar. Kumpulan memori dirancang untuk menangani memori secara massal .

Jenis Sistem Pisahkan Memori

Salah satu kesulitan dengan desain berorientasi objek granular dalam bahasa apa pun adalah bahwa ia sering ingin memperkenalkan banyak tipe dan struktur data yang didefinisikan pengguna. Jenis-jenis itu kemudian dapat dialokasikan dalam potongan kecil jika dialokasikan secara dinamis.

Contoh umum dalam C ++ adalah untuk kasus-kasus di mana polimorfisme diperlukan, di mana godaan alami adalah untuk mengalokasikan setiap instance dari subclass terhadap pengalokasi memori tujuan umum.

Ini akhirnya memecah-mecah tata letak memori yang mungkin bersebelahan menjadi sedikit demi sedikit bit-bit yang tersebar di seluruh rentang pengalamatan yang diterjemahkan menjadi lebih banyak kesalahan halaman dan cache misses.

Bidang-bidang yang menuntut respons laten terendah, bebas gagap, deterministik mungkin adalah satu-satunya tempat di mana hotspot tidak selalu berubah menjadi hambatan tunggal, di mana inefisiensi kecil sebenarnya dapat benar-benar semacam "terakumulasi" (sesuatu yang banyak orang bayangkan terjadi secara tidak benar dengan profiler untuk memastikannya, tetapi di bidang latensi yang digerakkan, sebenarnya bisa ada beberapa kasus yang jarang terjadi di mana akumulasi ketidakefisienan kecil). Dan banyak alasan paling umum untuk akumulasi seperti ini bisa jadi ini: alokasi yang berlebihan dari potongan memori kecil di semua tempat.

Dalam bahasa seperti Java, akan sangat membantu untuk menggunakan lebih banyak array tipe data lama polos bila memungkinkan untuk area bottlenecky (area yang diproses dalam loop ketat) seperti array int(tetapi masih di belakang antarmuka tingkat tinggi yang besar) alih-alih, katakanlah , sebuah objek yang ArrayListditentukan pengguna Integer. Ini menghindari pemisahan memori yang biasanya menyertai yang terakhir. Dalam C ++, kita tidak harus menurunkan struktur sebanyak jika pola alokasi memori kita efisien, karena tipe yang ditentukan pengguna dapat dialokasikan secara berdekatan di sana dan bahkan dalam konteks wadah generik.

Memori Sekering Kembali Bersama

Solusi di sini adalah menjangkau pengalokasi khusus untuk tipe data yang homogen, dan mungkin bahkan lintas tipe data yang homogen. Ketika tipe data kecil dan struktur data diratakan menjadi bit dan byte dalam memori, mereka mengambil sifat yang homogen (meskipun dengan beberapa persyaratan keberpihakan yang berbeda). Ketika kita tidak melihat mereka dari pola pikir sentris-memori, jenis sistem bahasa pemrograman "ingin" untuk membagi / memisahkan wilayah memori yang berpotensi bersebelahan menjadi beberapa potongan kecil yang tersebar.

Tumpukan menggunakan fokus sentris-memori ini untuk menghindari hal ini dan berpotensi menyimpan kombinasi campuran yang mungkin dari instance tipe yang ditentukan pengguna di dalamnya. Memanfaatkan tumpukan lebih banyak adalah ide bagus bila memungkinkan karena bagian atasnya hampir selalu duduk di baris cache, tetapi kita juga dapat merancang pengalokasi memori yang meniru beberapa karakteristik ini tanpa pola LIFO, menggabungkan memori melintasi tipe data yang berbeda menjadi berdekatan. potongan bahkan untuk alokasi memori dan pola alokasi yang lebih kompleks.

Perangkat keras modern dirancang untuk mencapai puncaknya ketika memproses blok memori yang berdekatan (berulang kali mengakses jalur cache yang sama, halaman yang sama, misalnya). Kata kunci ada persentuhan, karena ini hanya menguntungkan jika ada data yang menarik di sekitarnya. Jadi banyak kunci (namun juga kesulitan) untuk kinerja adalah memadukan potongan-potongan memori yang terpisah kembali bersama-sama menjadi blok-blok yang berdekatan yang diakses secara keseluruhan (semua data di sekitarnya menjadi relevan) sebelum penggusuran. Sistem tipe kaya terutama tipe yang ditentukan pengguna dalam bahasa pemrograman dapat menjadi kendala terbesar di sini, tetapi kami selalu dapat menjangkau dan menyelesaikan masalah melalui pengalokasi khusus dan / atau desain bulkier bila sesuai.

Jelek

"Jelek" sulit dikatakan. Ini adalah metrik subjektif, dan seseorang yang bekerja di bidang yang sangat kritis terhadap kinerja akan mulai mengubah gagasan mereka tentang "keindahan" menjadi yang lebih berorientasi pada data dan berfokus pada antarmuka yang memproses berbagai hal secara massal.

Berbahaya

"Berbahaya" mungkin lebih mudah. Secara umum, kinerja cenderung ingin mencapai kode tingkat yang lebih rendah. Mengimplementasikan pengalokasi memori, misalnya, tidak mungkin tanpa mencapai di bawah tipe data dan bekerja pada level berbahaya bit dan byte mentah. Sebagai hasilnya, ini dapat membantu meningkatkan fokus pada prosedur pengujian yang cermat dalam subsistem yang sangat penting ini, meningkatkan ketelitian pengujian dengan tingkat optimisasi yang diterapkan.

Keindahan

Namun semua ini akan berada pada level detail implementasi. Baik dalam skala besar veteran maupun pola pikir kritis-kinerja, "keindahan" cenderung beralih ke desain antarmuka daripada detail implementasi. Ini menjadi prioritas yang secara eksponensial lebih tinggi untuk mencari antarmuka yang "indah", dapat digunakan, aman, efisien daripada implementasi karena kerusakan kopling dan kaskade yang dapat terjadi dalam menghadapi perubahan desain antarmuka. Implementasi dapat ditukar kapan saja. Kami biasanya beralih ke kinerja sesuai kebutuhan, dan sebagaimana ditunjukkan oleh pengukuran. Kunci dengan desain antarmuka adalah untuk memodelkan pada tingkat yang cukup kasar untuk meninggalkan ruang untuk iterasi seperti itu tanpa merusak seluruh sistem.

Bahkan, saya akan menyarankan bahwa fokus veteran pada pengembangan kinerja-kritis sering cenderung menempatkan fokus utama pada keselamatan, pengujian, perawatan, hanya murid SE secara umum, karena basis kode skala besar yang memiliki sejumlah kinerja subsistem-kritis (sistem partikel, algoritma pemrosesan gambar, pemrosesan video, umpan balik audio, raytracer, mesin mesh, dll) perlu memperhatikan teknik perangkat lunak untuk menghindari tenggelam dalam mimpi buruk pemeliharaan. Bukan kebetulan bahwa seringkali produk yang paling efisien di luar sana juga dapat memiliki jumlah bug paling sedikit.

TL; DR

Pokoknya, itulah pendapat saya tentang masalah ini, mulai dari prioritas di bidang yang benar-benar kritis terhadap kinerja, apa yang dapat mengurangi latensi dan menyebabkan inefisiensi kecil menumpuk, dan apa yang sebenarnya merupakan "keindahan" (ketika melihat hal-hal yang paling produktif).


0

Tidak berbeda, tapi inilah yang saya lakukan:

  1. Tulis itu bersih dan terawat.

  2. Lakukan diagnosa kinerja , dan perbaiki masalah yang ia beri tahu, bukan yang Anda duga. Dijamin, mereka akan berbeda dari yang Anda harapkan.

Anda dapat melakukan perbaikan ini dengan cara yang masih jelas dan dapat dipertahankan, tetapi, Anda harus menambahkan komentar sehingga orang yang melihat kode akan tahu mengapa Anda melakukannya dengan cara itu. Jika tidak, mereka akan membatalkannya.

Jadi, apakah ada tradeoff? Saya kira tidak begitu.


0

Anda dapat menulis kode jelek yang sangat cepat dan Anda juga dapat menulis kode cantik secepat kode jelek Anda. Kemacetan tidak akan terletak pada keindahan / organisasi / struktur kode Anda tetapi dalam teknik yang Anda pilih. Misalnya, apakah Anda menggunakan soket yang tidak menghalangi? Apakah Anda menggunakan desain single-threaded? Apakah Anda menggunakan antrian bebas kunci untuk komunikasi antar utas? Apakah Anda menghasilkan sampah untuk GC? Apakah Anda melakukan operasi pemblokiran I / O di utas kritis? Seperti yang Anda lihat, ini tidak ada hubungannya dengan kecantikan.


0

Apa yang penting bagi pengguna akhir?

  • Performa
  • Fitur / Fungsi
  • Desain

Kasus 1: Kode buruk yang dioptimalkan

  • Perawatan sulit
  • Sulit dibaca jika sebagai proyek open-source

Kasus 2: Kode baik yang tidak dioptimalkan

  • Perawatan mudah
  • Pengalaman pengguna yang buruk

Larutan?

Mudah, optimalkan potongan kode penting kinerja

misalnya:

Sebuah program yang terdiri dari 5 Metode , 3 di antaranya adalah untuk manajemen data, 1 untuk membaca disk, yang lain untuk penulisan disk

Ketiga metode manajemen data ini menggunakan dua metode I / O dan bergantung padanya

Kami akan mengoptimalkan metode I / O.

Alasan: Metode I / O lebih kecil kemungkinannya untuk diubah, atau mereka memengaruhi desain aplikasi, dan semuanya, semua yang ada dalam program itu bergantung pada mereka, dan dengan demikian mereka nampak kritis terhadap kinerja, kami akan menggunakan kode apa pun untuk mengoptimalkannya. .

Ini berarti kami mendapatkan kode yang baik dan desain program yang dapat dikelola sambil menjaganya tetap cepat dengan mengoptimalkan bagian kode tertentu

Saya berpikir..

Saya pikir kode yang buruk membuat sulit bagi manusia untuk memoles-mengoptimalkan dan kesalahan kecil mungkin membuatnya lebih buruk, jadi kode yang baik untuk pemula / pemula akan lebih baik jika ditulis dengan baik kode jelek itu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.