Apakah saya mengoptimalkan secara prematur?

9

Saya saat ini pada tahap desain arsitektur berbasis komponen di C ++.

Desain saya saat ini mencakup penggunaan fitur-fitur seperti:

  • std::vectors std::shared_ptrs untuk memegang komponen
  • std::dynamic_pointer_cast
  • std::unordered_map<std::string,[yada]>

Komponen akan mewakili data dan logika berbagai item yang diperlukan dalam perangkat lunak seperti game, seperti Grafik, Fisika, AI, Audio, dll.

Saya telah membaca di semua tempat bahwa kesalahan cache sulit pada kinerja, jadi saya menjalankan beberapa tes, yang membuat saya percaya bahwa, memang, itu dapat memperlambat aplikasi.

Saya belum dapat menguji fitur bahasa yang disebutkan di atas, tetapi dikatakan di banyak tempat bahwa ini cenderung menghabiskan banyak biaya dan harus dihindari jika mungkin.

Karena saya pada tahap desain arsitektur, dan ini akan dimasukkan dalam inti desain, haruskah saya mencoba mencari cara untuk menghindarinya sekarang, karena akan sangat sulit untuk mengubahnya nanti jika ada kinerja masalah?

Atau saya hanya terjebak melakukan optimasi prematur?

Vaillancourt
sumber
3
Saya akan sangat enggan untuk memilih desain yang membuatnya sangat sulit untuk diubah nanti, terlepas dari masalah kinerja. Hindari itu jika Anda bisa. Ada banyak desain yang fleksibel dan cepat.
candied_orange
1
Tanpa mengetahui detailnya, jawaban untuk pertanyaan ini hampir selalu berupa "YA !!".
Mawg mengatakan mengembalikan Monica
2
@ Mawg "... Namun kita tidak boleh melewatkan peluang kita dalam 3% kritis itu." Karena ini adalah inti dari desain, bagaimana saya bisa tahu apakah saya sedang mengerjakan 3% ini?
Vaillancourt
1
Poin excelelnt, Alexandre (+1), dan, ya, saya tahu bahwa setengah kutipan terakhir, yang hampir tidak pernah disebutkan :-) Tapi, untuk kembali ke komentar saya sebelumnya (yang tercermin dalam jawaban yang diuraikan) , the answer to this question is almost always a resounding "YES !!". Saya masih merasa bahwa itu lebih baik untuk membuatnya bekerja pertama & mengoptimalkan nanti, tetapi YMMV, semua orang memiliki pendapatnya, yang semuanya valid, dan hanya OP yang benar-benar dapat menjawab pertanyaannya sendiri - subyektif.
Mawg mengatakan mengembalikan Monica
1
@AlexandreVaillancourt Tetap membaca kertas Knuth (PDF, kutipan berasal dari sisi kanan halaman berlabel 268, halaman 8 dalam pembaca PDF). "... dia akan bijaksana untuk melihat dengan hati-hati pada kode kritis; tetapi hanya setelah kode itu diidentifikasi. Seringkali kesalahan membuat penilaian apriori tentang bagian mana dari suatu program yang benar-benar kritis, karena pengalaman universal dari programmer yang telah menggunakan alat pengukuran telah menebak bahwa intuisi mereka gagal. " (tekankan)
8bittree

Jawaban:

26

Tanpa membaca apa pun kecuali judulnya: Ya.

Setelah membaca teks: Ya. Meskipun adalah benar bahwa peta dan berbagi pointer dll tidak melakukan cache-bijaksana dengan baik, Anda pasti akan menemukan bahwa apa yang ingin Anda menggunakannya untuk - sejauh yang saya mengerti - tidak hambatan dan tidak akan diadakan di atau menggunakan cache secara efisien terlepas dari struktur data.

Tulis perangkat lunak untuk menghindari kesalahan paling bodoh, lalu uji, lalu temukan kemacetan, lalu optimalkan!

Fwiw: https://xkcd.com/1691/

steffen
sumber
3
Sepakat. Buat itu berfungsi dengan benar dulu, karena tidak masalah seberapa cepat ia gagal bekerja. Dan selalu ingat bahwa optimasi yang paling efektif tidak melibatkan mengubah kode, mereka melibatkan menemukan algoritma yang berbeda dan lebih efisien.
Todd Knarr
10
Saya ingin menunjukkan bahwa baris pertama tidak benar karena pengoptimalan selalu prematur, tetapi karena pengoptimalan tidak hanya prematur jika Anda tahu Anda membutuhkannya, dalam hal ini Anda tidak akan menanyakannya. Jadi, baris pertama hanya benar karena fakta bahwa Anda mengajukan pertanyaan tentang apakah optimasi adalah prematur atau tidak berarti Anda tidak yakin bahwa Anda memerlukan optimasi, yang menurut definisi membuatnya prematur. Fiuh.
Jörg W Mittag
@ JörgWMittag: setuju.
steffen
3

Saya tidak terbiasa dengan C ++, tetapi secara umum tergantung.

Anda tidak perlu mengoptimalkan secara prematur algoritme terisolasi di mana Anda dapat dengan mudah mengoptimalkannya ketika sampai pada hal itu.

Namun Anda perlu mendapatkan desain keseluruhan aplikasi untuk mencapai indikator kinerja utama yang diinginkan.

Misalnya jika Anda perlu mendesain aplikasi untuk melayani jutaan permintaan per detik, Anda perlu memikirkan skalabilitas aplikasi saat mendesainnya, daripada membuat aplikasi berfungsi.

Pelican Terbang Rendah
sumber
3

Jika Anda harus bertanya, maka ya. Optimalisasi prematur berarti optimisasi sebelum Anda yakin ada masalah kinerja yang signifikan.

JacquesB
sumber
1

ECS? Saya benar-benar akan menyarankan bahwa mungkin tidak prematur jika demikian untuk menaruh banyak pemikiran ke sisi berorientasi data dari desain dan benchmark repetisi yang berbeda karena dapat berdampak pada desain antarmuka Anda , dan yang terakhir sangat mahal untuk berubah di akhir permainan. ECS juga hanya menuntut banyak pekerjaan dan pemikiran di muka dan saya pikir layak menggunakan sebagian dari waktu itu untuk memastikan itu tidak akan memberi Anda kesedihan kinerja tingkat desain lebih jauh di bawah garis mengingat bagaimana itu akan menjadi jantung dari hati Anda seluruh mesin freaking. Bagian ini memelototi saya:

unordered_map<string,[yada]>

Bahkan dengan optimasi string kecil, Anda memiliki wadah berukuran variabel (string) di dalam wadah berukuran variabel lain (unordered_maps). Bahkan, optimasi string kecil sebenarnya bisa sama berbahayanya dengan membantu dalam kasus ini jika tabel Anda sangat jarang, karena optimasi string kecil akan menyiratkan bahwa setiap indeks tabel hash yang tidak digunakan masih akan menggunakan lebih banyak memori untuk optimasi SS ( sizeof(string)akan menjadi jauh lebih besar) ke titik di mana total memori overhead tabel hash Anda mungkin lebih mahal daripada apa pun yang Anda simpan di dalamnya, terutama jika itu adalah komponen sederhana seperti komponen posisi, di samping menimbulkan lebih banyak cache yang hilang dengan langkah besar untuk mendapatkan dari satu entri di tabel hash ke yang berikutnya.

Saya mengasumsikan string adalah semacam kunci, seperti ID komponen. Jika demikian, ini sudah membuat segalanya lebih murah secara dramatis:

unordered_map<int,[yada]>

... jika Anda menginginkan manfaat memiliki nama yang mudah digunakan yang dapat digunakan skrip, misalnya, string yang diinternir dapat memberi Anda yang terbaik dari kedua dunia di sini.

Yang mengatakan, jika Anda bisa memetakan string ke kisaran yang cukup rendah dari indeks yang digunakan secara padat, maka Anda mungkin bisa melakukan ini:

vector<[yada]> // the index and key become one and the same

Alasan saya tidak menganggap ini terlalu dini adalah karena, sekali lagi, ini dapat berdampak pada desain antarmuka Anda. Maksud dari DOD seharusnya bukan untuk mencoba untuk datang dengan representasi data yang paling efisien yang dapat dibayangkan dalam sekali jalan IMO (yang umumnya harus dicapai secara iteratif sesuai kebutuhan), tetapi untuk memikirkannya cukup untuk merancang antarmuka di atas untuk bekerja dengan itu data yang membuat Anda cukup ruang bernapas untuk profil dan mengoptimalkan tanpa perubahan desain cascading.

Sebagai contoh naif, perangkat lunak pemrosesan video yang memasangkan semua kodenya dengan ini:

// Abstract pixel that could be concretely represented by
// RGB, BGR, RGBA, BGRA, 1-bit channels, 8-bit channels, 
// 16-bit channels, 32-bit channels, grayscale, monochrome, 
// etc. pixels.
class IPixel
{
public:
    virtual ~IPixel() {}
    ...
};

Tidak akan jauh tanpa berpotensi menulis ulang epik, karena gagasan mengabstraksi pada tingkat piksel tunggal sudah sangat tidak efisien ( vptritu sendiri akan sering menghabiskan lebih banyak memori daripada seluruh piksel) dibandingkan dengan mengabstraksi pada tingkat gambar (yang akan sering mewakili jutaan piksel). Jadi taruh pemikiran yang cukup dalam representasi data Anda terlebih dahulu sehingga Anda tidak harus menghadapi skenario mimpi buruk seperti itu, dan idealnya tidak lebih, tapi di sini saya pikir ada baiknya memikirkan hal-hal ini di muka karena Anda tidak ingin membangun sebuah mesin rumit di sekitar ECS Anda dan temukan bahwa ECS itu sendiri adalah penghambat dalam cara yang mengharuskan Anda mengubah hal-hal pada tingkat desain.

Mengenai kesalahan cache ECS, menurut saya pengembang sering berusaha terlalu keras untuk membuat cache ECS mereka ramah. Itu mulai menghasilkan sedikit bang untuk uang untuk mencoba mengakses semua komponen Anda dengan cara yang berdekatan, dan sering akan menyiratkan menyalin dan mengacak data di semua tempat. Biasanya cukup baik untuk, katakanlah, hanya indeks komponen sortir radix sebelum mengaksesnya sehingga Anda mengaksesnya dengan cara di mana Anda setidaknya tidak memuat wilayah memori ke dalam garis cache, hanya untuk mengusirnya, dan kemudian memuatnya, lalu memuat semuanya lagi dalam loop yang sama hanya untuk mengakses bagian yang berbeda dari garis cache yang sama. Dan ECS tidak harus memberikan efisiensi luar biasa di seluruh papan. Ini tidak seperti manfaat sistem input dari itu sebanyak fisika atau sistem rendering, jadi saya sarankan bertujuan untuk "baik" efisiensi secara menyeluruh dan "luar biasa" hanya di tempat-tempat di mana Anda benar-benar membutuhkannya. Yang mengatakan, penggunaanunordered_mapdan di stringsini cukup mudah untuk dihindari.


sumber