Mengapa terlalu buruk untuk mengoptimalkan terlalu dini?

168

Setelah melihat ke dalam optimasi sedikit, saya telah menemukan (secara harfiah di mana-mana) bahwa itu tampaknya dosa yang diakui secara universal untuk mengoptimalkan permainan terlalu dini.

Saya benar-benar tidak mengerti hal ini, bukankah akan sangat sulit untuk mengubah beberapa struktur inti permainan pada akhirnya, daripada mengembangkannya pertama kali dengan kinerja dalam pikiran?

Saya mendapatkan bahwa menunggu sampai permainan selesai akan memberi tahu Anda jika Anda bahkan perlu optimisasi, tetapi sebaiknya Anda tidak melakukannya, setelah semua, itu bisa memperluas berbagai perangkat yang bisa dijalankan permainan, yang akan menambah jumlah potensi pemain.

Bisakah seseorang menjelaskan kepada saya mengapa terlalu buruk untuk mengoptimalkan terlalu dini?

mr-matt
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Josh
3
Tidak bisa lebih mudah untuk menjawab ini: "itu membuang-buang waktu". Anda mungkin juga bertanya "mengapa mencoba menghemat uang saat berbelanja?"
Fattie
27
Sebaliknya, perhatikan bahwa banyak insinyur mengatakan bahwa kalimat "jangan optimalkan terlalu cepat" sama sekali salah . Kalimatnya seharusnya, "jangan dioptimalkan sampai Anda tahu apa yang harus dioptimalkan". Jadi, cukup sederhana, jika ada tiga sistem A, B dan C, itu sama sekali tidak ada gunanya mengoptimalkan A jika, ternyata, A sama sekali tidak ada hambatan, dan C sebenarnya adalah hambatan. Dalam contoh itu, jelas, mengoptimalkan A akan menjadi buang-buang waktu. Jadi - cukup sederhana - jangan mengoptimalkan sampai Anda tahu mana dari A, BC untuk mengoptimalkan: itu saja artinya, sesederhana itu.
Fattie
2
Mengoptimalkan kelas waktu aplikasi Anda selama pengembangan sangat berbeda daripada mencoba mencukur beberapa instruksi dari satu loop yang biasanya merupakan waktu yang konstan. Fokus pada O (n) vs O (n log n) alih-alih, "menulis untuk loop dengan cara ini menghemat satu instruksi CPU".
1
@ phyrfox Sebenarnya, mengurangi waktu buka tampaknya lebih mengesankan bagi saya. Waktu muat tiga detik terlihat. Saya tidak yakin apakah 55fps hingga 60fps terlihat.
immibis

Jawaban:

237

Pembukaan:

Beberapa keberatan telah diajukan dalam komentar, dan saya pikir mereka sebagian besar berasal dari kesalahpahaman tentang apa yang kita maksudkan ketika kita mengatakan "optimasi prematur" - jadi saya ingin menambahkan sedikit klarifikasi tentang itu.

"Jangan mengoptimalkan sebelum waktunya" tidak berarti "menulis kode yang Anda tahu buruk, karena Knuth mengatakan Anda tidak diizinkan untuk membersihkannya sampai akhir"

Ini berarti "jangan mengorbankan waktu & keterbacaan untuk pengoptimalan sampai Anda tahu bagian mana dari program Anda yang benar-benar membutuhkan bantuan agar lebih cepat." Karena program tipikal menghabiskan sebagian besar waktunya dalam beberapa hambatan, berinvestasi dalam mengoptimalkan "semuanya" mungkin tidak memberi Anda dorongan kecepatan yang sama dengan memfokuskan investasi yang sama hanya pada kode yang mengalami kemacetan.

Ini berarti, ketika ragu , kita harus:

  • Lebih suka kode yang mudah ditulis, jelas dimengerti, dan mudah dimodifikasi untuk pemula

  • Periksa apakah optimasi lebih lanjut diperlukan (biasanya dengan membuat profil program yang sedang berjalan, meskipun satu komentar di bawah ini mencatat melakukan analisis matematika - satu-satunya risiko di sana adalah Anda juga perlu memeriksa apakah matematika Anda benar)

Optimalisasi prematur tidak :

  • Keputusan arsitektural untuk menyusun kode Anda dengan cara yang akan disesuaikan dengan kebutuhan Anda - memilih modul / tanggung jawab / antarmuka / sistem komunikasi yang sesuai dengan cara yang dipertimbangkan.

  • Efisiensi sederhana yang tidak membutuhkan waktu ekstra atau membuat kode Anda lebih sulit dibaca. Hal-hal seperti menggunakan pengetikan yang kuat dapat menjadi efisien dan membuat maksud Anda lebih jelas. Caching referensi alih-alih mencarinya berulang kali adalah contoh lain (selama kasing Anda tidak membutuhkan logika pembatalan-cache yang rumit - mungkin tunda menulisnya sampai Anda membuat profil dengan cara sederhana terlebih dahulu).

  • Menggunakan algoritma yang tepat untuk pekerjaan itu. A * lebih optimal dan lebih kompleks daripada mencari grafik pathfinding secara mendalam. Ini juga merupakan standar industri. Mengulangi tema, tetap menggunakan metode yang sudah terbukti benar seperti ini sebenarnya dapat membuat kode Anda lebih mudah dipahami daripada jika Anda melakukan sesuatu yang sederhana tetapi bertentangan dengan praktik terbaik yang diketahui. Jika Anda memiliki pengalaman mengalami hambatan dalam mengimplementasikan fitur game X satu arah pada proyek sebelumnya, Anda tidak perlu menekan bottleneck yang sama lagi pada proyek ini untuk mengetahui itu nyata - Anda dapat dan harus menggunakan kembali solusi yang telah bekerja sebelumnya. pertandingan.

Semua jenis optimasi itu dibenarkan dengan baik dan umumnya tidak akan diberi label "prematur" (kecuali jika Anda pergi ke lubang kelinci menerapkan pathfinding mutakhir untuk peta papan catur 8x8 Anda ...)

Jadi sekarang dengan itu dibersihkan, mengapa kita mungkin menemukan kebijakan ini berguna dalam game khusus:


Dalam gamedev khususnya, kecepatan iterasi adalah hal yang paling berharga. Kami akan sering menerapkan dan menerapkan kembali ide-ide yang jauh lebih banyak daripada yang pada akhirnya akan dikirimkan dengan permainan yang sudah jadi, mencoba "menemukan kesenangan."

Jika Anda dapat membuat prototipe mekanik secara langsung & mungkin agak naif dan melakukan pengujian pada hari berikutnya, Anda berada dalam posisi yang jauh lebih baik daripada jika Anda menghabiskan waktu seminggu membuat versi yang paling optimal terlebih dahulu. Terutama jika ternyata menyedot dan Anda akhirnya membuang fitur itu. Melakukannya dengan cara sederhana sehingga Anda dapat menguji lebih awal dapat menghemat satu ton kode optimisasi pekerjaan yang sia-sia yang tidak Anda simpan.

Kode yang tidak dioptimalkan juga umumnya lebih mudah untuk dimodifikasi dan mencoba varian yang berbeda daripada kode yang secara halus disetel untuk melakukan satu hal yang tepat secara optimal, yang cenderung rapuh dan sulit untuk dimodifikasi tanpa memecahkannya, memperkenalkan bug, atau memperlambatnya. Jadi, menjaga kode tetap sederhana dan mudah diubah sering kali bernilai inefisiensi runtime kecil di sebagian besar pengembangan (kami biasanya mengembangkan mesin di atas spesifikasi target, sehingga kami dapat menyerap overhead dan fokus untuk mendapatkan pengalaman target terlebih dahulu) hingga kami Kami telah mengunci apa yang kami butuhkan dari fitur ini dan dapat mengoptimalkan bagian-bagian yang kami tahu lambat.

Ya, refactoring bagian dari proyek terlambat dalam pengembangan untuk mengoptimalkan titik lambat bisa sulit. Namun demikian, refactoring berulang kali sepanjang pengembangan karena optimisasi yang Anda buat bulan lalu tidak kompatibel dengan arah permainan yang telah berkembang sejak saat itu, atau memperbaiki sesuatu yang ternyata tidak menjadi hambatan nyata begitu Anda mendapatkan lebih banyak fitur & konten di.

Game itu aneh dan eksperimental - sulit untuk memprediksi bagaimana proyek game dan kebutuhan teknologinya akan berkembang dan di mana kinerjanya paling ketat. Dalam praktiknya, kita sering berakhir mengkhawatirkan hal-hal yang salah - cari melalui pertanyaan kinerja di sini dan Anda akan melihat tema umum muncul dari para dev yang teralihkan oleh hal-hal di atas kertas yang sepertinya tidak menjadi masalah sama sekali.

Untuk mengambil contoh dramatis: jika game Anda terikat GPU (bukan tidak umum) maka semua waktu yang dihabiskan untuk mengoptimalkan hiper dan threading kerja CPU mungkin tidak menghasilkan manfaat nyata sama sekali. Semua jam dev itu bisa digunakan untuk mengimplementasikan & memoles fitur gameplay sebagai gantinya, untuk pengalaman pemain yang lebih baik.

Secara keseluruhan, sebagian besar waktu yang Anda habiskan untuk mengerjakan permainan tidak akan dihabiskan untuk kode yang akhirnya menjadi hambatan. Terutama ketika Anda bekerja pada mesin yang ada, hal-hal loop dalam yang sangat mahal dalam rendering dan sistem fisika sebagian besar di luar tangan Anda. Pada saat itu, tugas Anda dalam skrip gameplay pada dasarnya adalah menghindari mesin - selama Anda tidak melempar kunci pas di sana, maka Anda mungkin akan keluar dengan cukup baik untuk build pertama.

Jadi, terlepas dari sedikit kode kebersihan dan penganggaran (mis. Jangan berulang kali mencari / membuat barang jika Anda dapat dengan mudah menggunakannya kembali, pertahankan pencarian pathfinding / fisika atau pembacaan kembali GPU yang sederhana, dll.), Biasakan untuk tidak berlebihan Mengoptimalkan sebelum kita tahu di mana masalah yang sebenarnya ternyata baik untuk produktivitas - menyelamatkan kita dari membuang waktu mengoptimalkan hal-hal yang salah, dan menjaga kode kita lebih sederhana dan lebih mudah untuk mengubah secara keseluruhan.

DMGregory
sumber
38
Ada beberapa diskusi lebih lanjut tentang StackOverflow di sini , menekankan pentingnya keterbacaan dan kejelasan dalam kode, dan bahaya membuat kode lebih sulit untuk dipahami (dan lebih mudah untuk memasukkan bug ke dalam) dengan mengoptimalkannya secara prematur.
DMGregory
8
Jawaban yang bagus, saya ingin menambahkan, sebagai tanggapan terhadap " bukankah akan sangat sulit untuk mengubah beberapa struktur inti permainan pada akhirnya, daripada mengembangkannya pertama kali dengan kinerja dalam pikiran? " Secara langsung, bahwa tentu saja Anda mencoba merancang untuk efisiensi di tempat pertama. Ketika disajikan dengan dua pilihan, Anda memilih yang lebih efisien. Gagal itu, Anda menulis game semodel mungkin dengan antarmuka yang jelas. Anda kemudian dapat mengubah mekanisme di dalam setiap modul tanpa merestrukturisasi seluruh basis kode.
Baldrickk
1
@Baldrickk Saya akan mengatakan itu layak dibagikan sebagai jawaban tambahan. (Saya akan meneguhkannya!) Jawaban saya melompati pentingnya arsitektur dan perencanaan untuk menghindari menciptakan masalah besar di tempat pertama. Adapun referensi Gizmo untuk "pengembangan program secara umum," di situlah konsep optimasi prematur ini berasal. Seperti diuraikan di atas, optimasi yang salah dapat menjadi hutang teknis semudah optimasi yang hilang. Tetapi kita harus menekankan bahwa kita tidak pernah melakukan hal-hal dengan sengaja lambat, hanya lebih suka kesederhanaan & keterbacaan sampai kita dapat profil & menemukan masalah nyata
DMGregory
1
Walaupun saya mungkin paling tidak berpengalaman di sini dari Anda, saya harus mengatakan saya menemukan "akar optimasi prematur dari semua kejahatan" ini agak aneh. Dua tahun yang lalu saya mencoba melakukan sesuatu di Unity3D. Saya menulis beberapa pertanyaan LINQ, masing-masing menggunakan yang sebelumnya. Saya melihat kode saya dan mulai memiliki keraguan yang kuat. Saya pikir kompleksitas perhitungannya mengerikan. Saya mengambil selembar kertas dan menghitung bahwa memproses permintaan ini pada ukuran data yang diharapkan akan memakan waktu berjam-jam. Jadi saya menambahkan sedikit caching di sana-sini. Dan pertanyaan berjalan dengan baik.
gaazkam
3
Jadi Anda melakukan due diligence dan memverifikasi bahwa masalahnya adalah nyata sebelum mengubah kode Anda dari apa yang awalnya paling jelas dan paling intuitif untuk Anda. Itu berarti optimisasi Anda tidak prematur. ;) "Hindari optimisasi berlebihan sebelum waktunya" tidak berarti "menulis kode mahal yang tidak perlu" - hanya untuk mendukung kesederhanaan, keterbacaan, kemudahan modifikasi hingga masalah kinerja yang pasti telah diidentifikasi. Komentar tidak dimaksudkan untuk diskusi, jadi kami dapat pindah ke obrolan jika Anda ingin membicarakan hal ini lebih lanjut.
DMGregory
42

catatan: jawaban ini dimulai sebagai komentar pada jawaban DMGregory, dan karenanya tidak menduplikasi poin yang sangat baik yang dia buat.

"Apakah tidak akan terlalu sulit untuk mengubah beberapa struktur inti permainan pada akhirnya, daripada mengembangkannya pertama kali dengan kinerja dalam pikiran?"

Bagi saya ini adalah inti dari pertanyaan.

Saat membuat desain asli Anda, Anda harus mencoba merancang untuk efisiensi - di tingkat atas. Ini kurang optimasi, dan lebih banyak tentang struktur.

Contoh:
Anda perlu membuat sistem untuk menyeberangi sungai. Desain yang jelas adalah jembatan atau feri, jadi mana yang Anda pilih?
Jawabannya tentu saja tergantung pada ukuran persimpangan dan volume lalu lintas. Ini bukan pengoptimalan, melainkan memulai dengan desain yang cocok untuk masalah Anda.

Ketika disajikan dengan pilihan desain, Anda memilih yang paling cocok dengan apa yang ingin Anda lakukan.

Jadi, katakanlah volume lalu lintas kami cukup rendah, jadi kami memutuskan untuk membangun dua terminal dan membeli dengan feri untuk menangani lalu lintas. Implementasi sederhana yang bagus
Sayangnya, begitu kita menjalankan dan menjalankannya, kami menemukan bahwa ia melihat lebih banyak lalu lintas dari yang diharapkan. Kita perlu mengoptimalkan feri! (karena berfungsi, dan membangun jembatan sekarang bukan rencana yang baik)

Pilihan:

  • Beli feri kedua (pemrosesan paralel)
  • Tambahkan dek mobil lain ke feri (kompresi lalu lintas)
  • Tingkatkan mesin feri untuk membuatnya lebih cepat (algoritma pemrosesan ulang tertulis)

Di sinilah Anda harus berusaha untuk membuat desain asli Anda semodel mungkin.
Semua hal di atas adalah kemungkinan optimasi, dan Anda bahkan dapat melakukan ketiganya.
Tetapi bagaimana Anda membuat perubahan ini tanpa perubahan struktural besar?

Jika Anda memiliki desain modular dengan antarmuka yang jelas, maka harus mudah untuk menerapkan perubahan ini.
Jika kode Anda tidak digabungkan secara ketat, maka perubahan pada modul tidak mempengaruhi struktur di sekitarnya.

Mari kita lihat menambahkan feri tambahan.
Program 'buruk' dapat dibangun di sekitar gagasan sebuah feri tunggal, dan biarkan negara bagian dermaga dan negara bagian feri dan posisinya semua disatukan dan berbagi keadaan. Ini akan sulit dimodifikasi untuk memungkinkan feri tambahan ditambahkan ke sistem.
Desain yang lebih baik adalah memiliki dermaga dan feri sebagai entitas yang terpisah. Tidak ada kopling ketat di antara mereka, tetapi mereka memiliki antarmuka, di mana feri bisa tiba, menurunkan penumpang, naik yang baru, dan pergi. Dock dan feri hanya berbagi antarmuka ini, dan ini membuatnya mudah untuk membuat perubahan pada sistem, dalam hal ini dengan menambahkan feri kedua. Dermaga tidak peduli tentang apa sebenarnya feri, yang perlu diperhatikan adalah bahwa sesuatu (apa saja) menggunakan antarmuka.

tl; dr:

  • Coba desain untuk efisiensi sejak awal.
  • Ketika disajikan dengan dua pilihan, Anda memilih yang lebih efisien.
  • Tulis kode Anda semodel mungkin dengan antarmuka yang jelas.

Anda kemudian dapat mengubah mekanisme dalam setiap modul tanpa merestrukturisasi seluruh basis kode saat Anda perlu mengoptimalkan.

Baldrickk
sumber
16
Pada awalnya Anda juga dapat menerapkan feri Anda sebagai IRiverCrossing, dan ketika menjadi jelas bahwa volume lalu lintas terlalu besar untuk mengatur feri, mengimplementasikan jembatan sebagai IRiverCrossingdan menjatuhkannya. Triknya tentu saja adalah mengurangi API eksternal dari Feri dan Bridge ke fungsi umum sehingga mereka dapat diwakili oleh antarmuka yang sama.
Mr.Mindor
2
Anda bahkan mungkin memiliki tes otomatis yang ditulis untuk antarmuka modular ini, sehingga Anda dapat melakukan refactor dengan cepat tanpa perlu khawatir merusak apa pun di luar modul yang Anda sentuh.
user3067860
2
Contoh yang sangat bagus tentang mengapa OOP sangat penting hari ini.
Jaime Gallego
1
Anda menemukan alasan yang tepat. Mantra Donald Knuth hanya benar ketika kecepatan bukan salah satu persyaratan inti, dan fokus pada pengoptimalan akan membahayakan desain utama. Sayangnya, dalam game, kecepatan sering kali merupakan inti, dan karenanya harus diperhitungkan dalam desain sejak dini. Kekhawatiran OP (dan jawaban Anda) sepenuhnya dibenarkan.
Peter A. Schneider
3
@AlexandreVaillancourt alih-alih menjawab judul pertanyaan, saya mencoba menjawab apa yang menurut saya bagian kunci dari pertanyaan yang sebenarnya dijawab; yaitu, "mengapa saya tidak harus mengoptimalkan lebih awal jika mengoptimalkan akan lebih banyak pekerjaan nanti karena desain saya sudah diperbaiki" (diparafrasekan) . Jawaban ini juga dimulai sebagai komentar pada jawaban DMGregory, dan merupakan tambahan untuk itu, dan tidak ada gunanya menduplikasi jawabannya.
Baldrickk
24

"Jangan mengoptimalkan awal" tidak berarti "memilih cara terburuk untuk melakukan sesuatu". Anda masih perlu mempertimbangkan implikasi kinerja (kecuali Anda hanya membuat prototipe). Intinya bukan untuk melumpuhkan hal-hal lain yang lebih penting pada saat itu dalam pengembangan - seperti fleksibilitas, keandalan dll. Pilih sederhana, optimisasi aman - pilih hal-hal yang Anda batasi, dan hal-hal yang Anda jaga tetap bebas; melacak biaya. Haruskah Anda menggunakan pengetikan kuat? Sebagian besar permainan berhasil dan bekerja dengan baik; berapa biaya yang harus Anda keluarkan untuk itu jika Anda menemukan kegunaan yang menarik dari fleksibilitas untuk gamemplay?

Jauh lebih sulit untuk memodifikasi kode yang dioptimalkan, terutama kode "pintar". Itu selalu merupakan pilihan yang membuat beberapa hal lebih baik, dan yang lain lebih buruk (misalnya, Anda mungkin memperdagangkan waktu CPU untuk penggunaan memori). Ketika membuat pilihan itu, Anda harus menyadari semua implikasinya - mereka mungkin menjadi bencana, tetapi mereka juga bisa membantu.

Misalnya, Komandan Keen, Wolfenstein dan Doom masing-masing dibangun di atas mesin rendering yang dioptimalkan. Masing-masing memiliki "trik" mereka yang memungkinkan permainan untuk ada di tempat pertama (masing-masing juga memiliki optimasi lebih lanjut dari waktu ke waktu, tetapi itu tidak penting di sini). Itu baik-baik saja . Tidak apa-apa untuk sangat mengoptimalkan inti dari permainan, pemikiran yang memungkinkan permainan; terutama jika Anda menjelajahi wilayah baru di mana fitur yang dioptimalkan khusus ini memungkinkan Anda untuk mempertimbangkan desain game yang tidak banyak dieksplorasi. Batasan yang diperkenalkan oleh optimasi dapat memberikan Anda gameplay yang menarik juga (mis. Batasan jumlah unit dalam game RTS mungkin telah dimulai sebagai cara untuk meningkatkan kinerja, tetapi mereka memiliki efek gameplay juga).

Tetapi harap dicatat bahwa dalam masing-masing contoh ini, permainan tidak akan ada tanpa optimasi. Mereka tidak memulai dengan mesin "sepenuhnya dioptimalkan" - mereka mulai dengan kebutuhan telanjang, dan berjalan naik. Mereka mengembangkan teknologi baru, dan menggunakannya untuk membuat game yang menyenangkan. Dan trik mesin dibatasi sebagai bagian kecil dari basis kode mungkin - optimasi yang lebih berat hanya diperkenalkan ketika gameplay sebagian besar dilakukan, atau di mana itu memungkinkan fitur baru yang menarik muncul.

Sekarang pertimbangkan sebuah game yang mungkin ingin Anda buat. Apakah memang ada keajaiban teknologi yang membuat atau menghancurkan game itu? Mungkin Anda membayangkan game dunia terbuka di dunia tanpa batas. Apakah itu benar - benar bagian utama dari permainan? Apakah game tidak akan berfungsi tanpanya? Mungkin Anda sedang memikirkan permainan di mana medan dapat dideformasi tanpa batas, dengan geologi realistis dan semacamnya; dapatkah Anda membuatnya bekerja dengan cakupan yang lebih kecil? Apakah ini akan bekerja dalam 2D ​​daripada 3D? Dapatkan sesuatu yang menyenangkan sesegera mungkin - bahkan jika optimasi mungkin mengharuskan Anda untuk mengolah ulang sejumlah besar kode Anda yang ada, mungkin itu layak dilakukan; dan Anda bahkan mungkin menyadari bahwa membuat sesuatu yang lebih besar tidak benar-benar membuat permainan menjadi lebih baik.

Sebagai contoh permainan baru-baru ini dengan banyak optimisasi, saya akan menunjuk ke Factorio. Salah satu bagian penting dari gim ini adalah ikat pinggang - ada ribuan di antaranya, dan mereka membawa banyak potongan material di sekitar pabrik Anda. Apakah permainan dimulai dengan mesin sabuk yang sangat dioptimalkan? Tidak! Bahkan, desain sabuk asli hampir mustahil untuk dioptimalkan - itu semacam melakukan simulasi fisik dari barang-barang di sabuk, yang menciptakan beberapa hal menarik yang bisa Anda lakukan (ini adalah cara Anda mendapatkan gameplay "emergent" - gameplay yang mengejutkan desainer), tetapi berarti Anda harus mensimulasikan setiap item pada ikat pinggang. Dengan ribuan sabuk, Anda mendapatkan puluhan ribu item yang disimulasikan secara fisik - bahkan hanya dengan melepasnya dan membiarkan sabuknya bekerja, Anda dapat menghemat waktu CPU terkait sebesar 95-99%, bahkan tanpa mempertimbangkan hal-hal seperti memori lokalitas. Tapi itu hanya berguna untuk melakukan itu ketika Anda benar-benar mencapai batas-batas itu.

Hampir semua yang ada hubungannya dengan ikat pinggang harus dibuat ulang untuk memungkinkan ikat pinggang dioptimalkan. Dan sabuk harus dioptimalkan, karena Anda membutuhkan banyak sabuk untuk pabrik besar, dan pabrik besar adalah salah satu daya tarik permainan. Lagi pula, jika Anda tidak dapat memiliki pabrik besar, mengapa memiliki dunia tanpa batas? Lucu Anda harus bertanya - versi awal tidak :) Permainan ini ulang dan dibentuk ulang berkali-kali untuk mendapatkan di mana mereka sekarang - termasuk remake 100% ground-up ketika mereka menyadari Jawa bukan cara untuk mencari game seperti ini dan beralih ke C ++. Dan itu bekerja sangat baik untuk Factorio (meskipun itu masih merupakan hal yang baik itu tidak dioptimalkan sejak awal - terutama karena ini adalah proyek hobi, yang mungkin gagal jika tidak karena kurangnya minat).

Tapi masalahnya adalah, ada yangbanyak hal yang dapat Anda lakukan dengan pabrik dengan cakupan terbatas - dan banyak game telah menunjukkan hal itu. Batas bisa lebih memberdayakan untuk bersenang-senang daripada kebebasan; Apakah Spacechem akan lebih menyenangkan jika "peta" tidak terbatas? Jika Anda mulai dengan "sabuk" yang sangat dioptimalkan, Anda akan dipaksa untuk pergi ke sana; dan Anda tidak dapat menjelajahi arah desain lainnya (seperti melihat hal-hal menarik apa yang dapat Anda lakukan dengan sabuk konveyor simulasi-fisika). Anda membatasi ruang desain potensial Anda. Mungkin tidak terlihat seperti itu karena Anda tidak melihat banyak permainan yang belum selesai, tetapi bagian yang sulit adalah mendapatkan kesenangan dengan benar - untuk setiap permainan menyenangkan yang Anda lihat, mungkin ada ratusan yang tidak bisa sampai di sana dan dibatalkan (atau lebih buruk, dirilis sebagai kekacauan mengerikan). Jika optimisasi membantu Anda melakukan itu - silakan. Jika tidak ... kemungkinan prematur. Jika Anda berpikir beberapa mekanik permainan bekerja dengan baik, tetapi perlu optimisasi untuk benar-benar bersinar - silakan. Jika Anda tidak memiliki mekanik yang menarik,jangan mengoptimalkannya . Temukan kesenangan terlebih dahulu - Anda akan menemukan sebagian besar optimisasi tidak membantu dengan itu, dan seringkali bersifat detriminal.

Akhirnya, Anda memiliki gim yang asyik dan menyenangkan. Apakah masuk akal untuk mengoptimalkan sekarang ? Ha! Masih tidak sejelas yang Anda kira. Apakah ada sesuatu yang menyenangkanAnda bisa melakukannya? Jangan lupa waktu Anda masih terbatas. Semuanya membutuhkan upaya, dan Anda ingin memfokuskan upaya itu di tempat yang paling berarti. Ya, meskipun Anda membuat game "gratis", atau "open source". Tonton bagaimana permainan dimainkan; perhatikan di mana kinerja menjadi hambatan. Apakah mengoptimalkan tempat-tempat itu membuat lebih menyenangkan (seperti membangun pabrik yang semakin besar, semakin kusut)? Apakah ini memungkinkan Anda untuk menarik lebih banyak pemain (mis. Dengan komputer yang lebih lemah, atau pada platform yang berbeda)? Anda selalu perlu memprioritaskan - mencari upaya untuk menghasilkan rasio. Anda mungkin akan menemukan banyak buah yang mudah digantung hanya dari memainkan game Anda dan menonton orang lain memainkan game. Tetapi perhatikan bagian penting - untuk sampai ke sana, Anda membutuhkan permainan . Fokuslah pada hal itu.

Sebagai ceri di atas, pertimbangkan bahwa optimasi tidak pernah berakhir. Ini bukan tugas dengan tanda centang kecil yang Anda selesaikan dan beralih ke tugas lain. Selalu ada "satu lagi optimasi" yang dapat Anda lakukan, dan sebagian besar pengembangan apa pun adalah memahami prioritas. Anda tidak melakukan optimasi demi optimisasi - Anda melakukannya untuk mencapai tujuan tertentu (mis. "200 unit di layar sekaligus pada 333 MHz Pentium" adalah tujuan yang hebat). Jangan kehilangan jejak tujuan terminal hanya karena Anda terlalu fokus pada tujuan menengah yang bahkan mungkin bukan prasyarat untuk tujuan terminal lagi.

Luaan
sumber
Saya percaya pengembang faktorio sekarang mengoptimalkan sabuk lagi sehingga mereka hanya perlu mensimulasikan item secara berkala, dan di lain waktu itu hanya menginterpolasi posisi mereka ketika Anda melihatnya. Tapi mereka hanya melakukan itu sekarang ketika seluruh permainan secara efektif berbasis di sekitar sabuk (dan robot) untuk waktu yang lama dan tidak sampai orang-orang mulai memperhatikan dan mengeluh tentang kinerja.
immibis
2
@immibis Persis. Membuatnya berkinerja lebih baik sebelum orang benar-benar mencapai batas akan menjadi pemborosan sumber daya, yang lebih baik dihabiskan di tempat lain. Bahkan dengan optimisasi terbaru, Anda kemungkinan besar menghadapi masalah hanya untuk megafactories jauh di luar cakupan gameplay normal (meluncurkan roket). Tapi itu memungkinkan pengaturan gameplay Marathon baru, yang membutuhkan logistik yang jauh lebih berat. Dan lagi, mereka mengidentifikasi kemacetan dengan mendapatkan statistik dari orang-orang yang benar-benar memainkan permainan - sekitar setengah dari kemacetan adalah kejutan besar bagi mereka.
Luaan
@ Fat Jadi Anda mengerti pertanyaannya, tetapi bukan jawabannya? Apakah Anda hanya mencoba mengatakan bahwa jawaban yang lebih sederhana itu mungkin (mis. "Ekonomi")? Saya tidak melihat bagaimana komentar Anda seharusnya bersifat membangun.
Luaan
2
@Fattie Jika Anda berpikir jawabannya adalah "Optimalkan hanya sekali Anda tahu apa yang menjadi hambatan", posting sebagai jawaban. Anda sudah menghabiskan lebih banyak kata daripada yang dibutuhkan untuk jawaban yang baik, jadi silakan saja. Optimalisasi seperti apa yang tidak membuat segalanya lebih baik dan lebih buruk? Saya pasti belum pernah melihatnya. Hal mendasar yang saya terus tegaskan adalah selalu ada trade-off - waktu yang dapat dihabiskan lebih baik di tempat lain, kompleksitas yang membatasi fleksibilitas Anda ... Contoh-contoh menunjukkan bahwa "awal" bukanlah istilah absolut; beberapa optimisasi diperlukan sejak hari pertama, sementara yang lain bersifat detriminal.
Luaan
1
@Fattie "Optimalkan hanya sekali Anda tahu apa hambatannya" adalah jawaban untuk "Kapan saya harus mengoptimalkan?" dan bukan jawaban untuk "Mengapa terlalu buruk untuk mengoptimalkan terlalu dini?".
immibis
10

Banyak jawaban tampaknya banyak berfokus pada aspek kinerja "optimasi" sementara saya sendiri suka melihat optimasi dan seluruh cobaan mengoptimalkan terlalu dini pada tingkat yang lebih abstrak.

Humor saya ketika saya mencoba menguraikan perspektif saya dengan bantuan polyomino.

Misalkan kita memiliki beberapa batasan tetap yang ditetapkan oleh framework atau engine yang sedang kita kerjakan.

masukkan deskripsi gambar di sini

Kami kemudian melanjutkan untuk membuat layer / modul pertama dari permainan kami seperti itu.

masukkan deskripsi gambar di sini

Selanjutnya, kita membangun lapisan / modul kedua.

masukkan deskripsi gambar di sini

Pada titik ini, kita mungkin memperhatikan bahwa ada beberapa ruang yang tersedia antara kedua modul dan kita mungkin tergoda untuk mengoptimalkannya untuk memanfaatkan sepenuhnya batas yang diberikan kepada kita.

masukkan deskripsi gambar di sini

Sempurna, sekarang aplikasi sepenuhnya memanfaatkan sumber daya yang tersedia untuk kita, aplikasi lebih baik, bagus kan?

Kami melanjutkan untuk membangun lapisan / modul ketiga dari aplikasi kami dan tiba-tiba kami menyadari (mungkin bahkan yang tidak dapat kami perkirakan selama perencanaan awal) bahwa lapisan / modul ke-3 tidak berfungsi untuk kami.

Kami mencari alternatif, kami menemukan satu, dan pada akhirnya, ini juga mengharuskan kami untuk mengubah modul ke-2 dari aplikasi kami karena tidak sesuai dengan modul ke-3 yang baru kami pilih. (Untungnya ini agak kompatibel dengan modul 1 kami sehingga kami tidak perlu menulis ulang semuanya dari awal.)

Jadi kami menggabungkan semuanya ...

masukkan deskripsi gambar di sini

Hmm, bisakah kamu melihat apa yang terjadi?

Dengan mengoptimalkan terlalu dini, kami sekarang benar-benar membuat efisiensi menjadi lebih buruk karena apa yang kami optimalkan bukanlah apa yang akhirnya kami lakukan.

Dan jika kita ingin menambahkan beberapa modul tambahan atau informasi tambahan sesudahnya, kita mungkin tidak lagi memiliki kapasitas untuk melakukannya pada saat ini.

masukkan deskripsi gambar di sini

Dan menyesuaikan tingkat terendah dari sistem kami tidak lagi layak karena telah terkubur di bawah semua lapisan lainnya.

Namun, jika kami memilih untuk menunggu dengan keinginan kami untuk segera mengoptimalkannya, kami akan berakhir dengan sesuatu seperti ini:

masukkan deskripsi gambar di sini

Dan sekarang jika kita melakukan optimasi pada titik ini kita mendapatkan sesuatu yang memuaskan untuk dilihat.

masukkan deskripsi gambar di sini

Semoga setidaknya beberapa dari Anda bersenang-senang membaca ini seperti saya bersenang-senang membuat ini :) dan jika Anda sekarang merasa seperti Anda memiliki pemahaman yang lebih baik tentang subjek - semua lebih baik.

Tokek langit-langit
sumber
3
Saya menghapus iklan. Kami tidak peduli bagaimana Anda membuat gambar; satu-satunya bagian penting adalah asumsi bahwa Anda berhak mempostingnya.
Gnemlock
2
@Gnemlock cukup adil, sementara niat saya tidak ditujukan untuk iklan, saya juga bisa melihat bagaimana hal itu dapat dengan mudah disalahartikan seperti itu, dan saya akan setuju bahwa itu tidak menambahkan apa pun pada jawaban sehingga tidak memiliki tempat di dalamnya.
Ceiling Gecko
2
Saya pikir metafora visual ini sangat efektif. Pendekatan hebat untuk menjawab pertanyaan! :)
DMGregory
8

Uang.

Turun ke itu. Uang. Karena waktu adalah uang *, semakin banyak waktu yang Anda habiskan untuk kegiatan yang tidak dijamin menghasilkan lebih banyak uang (yaitu Anda tidak dapat menganggap kegiatan ini sebagai investasi ), semakin banyak uang yang Anda sia-siakan, dan semakin sedikit uang yang Anda hasilkan dengan permainan.

Berikut adalah beberapa lebih potensial efek samping mengoptimalkan terlalu dini, dan alasan mengapa Anda harus menghindarinya:

  • Rekan kerja Anda membenci Anda karena Anda bermain di kotak pasir dengan mainan sementara mereka bekerja keras untuk mendapatkan fitur.
  • Penundaan itu membuat manajemen tidak senang, dan membuat tim melewatkan tanggal pengiriman, yang menyebabkan game dirilis pada musim semi alih-alih sebelum musim liburan, yang pada gilirannya menyebabkan game tidak cukup menjual. Dan karena ini, kantor pusat memutuskan untuk menutup studio, secara efektif membuat Anda menganggur. Dan tidak ada pekerjaan berarti tidak ada uang. (Dan karena tidak ada yang menyukai Anda karena Anda menghabiskan terlalu banyak waktu melakukan optimasi awal, tidak ada yang ingin menulis ulasan positif tentang pekerjaan Anda untuk Anda di LinkedIn, yang akan membuat Anda kehabisan uang untuk waktu yang lebih lama.)

* Jawaban lain telah menyoroti bagian 'waktu' dengan cukup baik


Sebagai catatan tambahan, secara umum , pengalaman membantu menentukan apa yang dan apa yang bukan optimasi prematur dan apa yang memiliki nilai untuk bisnis dan apa yang tidak.

Misalnya, jika Anda telah bekerja pada game A, dan menyadari pada akhir proyek bahwa fitur XYZ sangat berat pada loop game Anda, dan Anda akhirnya mulai bekerja pada game B yang memiliki fitur yang sama persis, memutuskan untuk menulis ulang fitur dan mengoptimalkannya dari awal bukanlah optimasi yang terlalu dini, karena Anda tahu itu akan menjadi hambatan jika tidak ada yang dilakukan.

Alexandre Vaillancourt
sumber
1
@JBentley itu adalah poin yang valid. Namun, ada jawaban konsekuensial untuk "mengapa" sebagai implikasi langsung dari pengalaman yang dikumpulkan. Jadi, 1) mengoptimalkan lebih awal dapat menyebabkan terbuangnya waktu pada bagian-bagian kode yang tidak memengaruhi runtime rata-rata (berdasarkan pengalaman, Anda harus tahu konstruk kode mana yang merupakan kandidat untuk optimalisasi praktik terbaik) 2) mengoptimalkan awal dapat membuat kode lebih sulit untuk mempertahankan karena pembatasan desain yang diberlakukan atas beberapa sistem. Dengan demikian, Anda mungkin mendapatkan terlalu sedikit sebagai imbalan untuk mempertahankan siklus / kode rumit lebih lama untuk dipertahankan.
teodron
3
@JBentley Pertimbangkan ini sebagai tambahan untuk jawaban DMGregory.
Alexandre Vaillancourt
1
Ini tampaknya menjadi satu-satunya jawaban yang berharga di sini. Pertanyaannya adalah tautologi. Anda mungkin juga bertanya, "Jadi mengapa, sebenarnya, apakah Anda ingin game menghasilkan lebih banyak daripada lebih sedikit uang di toko aplikasi?" Bagaimana Anda bisa menjawabnya?
Fattie
@Fattie Ada baiknya memiliki perspektif seperti ini dalam jawaban. Tetapi mengatakan itu satu-satunya jawaban yang berharga adalah mempersempit ruang lingkup pertanyaan secara tidak masuk akal. Misalnya, Anda berasumsi, bahwa satu-satunya game yang dibuat adalah di dalam organisasi nirlaba (jelas tidak benar). Anda juga mengasumsikan bahwa jelas bagi OP bahwa mengoptimalkan lebih awal menyebabkan lebih banyak waktu yang dihabiskan (dan karenanya lebih banyak uang jika kita berbicara tentang bisnis). Sebenarnya pertanyaan OP menunjukkan bahwa dia tidak yakin tentang ini.
JBentley
6

Optimalisasi, menurut definisi, adalah proses meningkatkan efisiensi suatu solusi sampai pada titik di mana ia kehilangan kemanjurannya. Proses ini kemudian menyiratkan pengurangan ruang solusi.

Pada tahap awal pengembangan perangkat lunak masih mungkin ada "persyaratan tersembunyi": jika Anda mengurangi terlalu banyak ruang solusi Anda, Anda mungkin berakhir dalam situasi di mana Anda tidak dapat memenuhi "persyaratan tersembunyi" ketika muncul pada tahap pengembangan selanjutnya, memaksa Anda untuk memodifikasi arsitektur yang menambah ketidakstabilan dengan cara ini dan sekelompok perilaku yang mungkin tidak terpikirkan.

Idenya adalah untuk membuat seluruh solusi bekerja dan hanya saat itu, ketika semua persyaratan diperbaiki dan diimplementasikan, kencangkan kode. Anda akan melihat kemudian, bahwa banyak optimasi yang akan Anda implementasikan dengan ringan sekaligus saat pengkodean, sekarang tidak lagi cocok karena interaksi dengan persyaratan yang terlambat.

Ruang nyata dari solusi selalu lebih besar dari yang kita harapkan di awal karena kita tidak dapat memiliki pengetahuan yang sempurna tentang sistem yang sangat kompleks.

Buat itu bekerja dulu. Kemudian kencangkan tali.

Earendel
sumber
2
Saya pikir "kemanjuran" bukanlah kata yang Anda inginkan - itu berarti "kekuatan untuk menghasilkan efek", jadi dalam arti tertentu, pengoptimalan adalah tentang peningkatan kemanjuran. Apakah Anda akan menggunakan beberapa versi khasiat tertentu? (Bisakah Anda menautkannya?) Sebaliknya, apakah Anda maksudkan sesuatu seperti fleksibilitas?
doppelgreener
2
@doppelgreener Berarti Anda membuat solusi lebih efisien hingga berhenti menghasilkan efek yang Anda inginkan ...
immibis
1
"Buat itu bekerja dulu. Lalu kencangkan tali." - Itu harus bernilai sebanyak sisa jawaban digabungkan. Dan tidak mengatakan hal-hal lain tidak baik.
ebyrob
1
@ebyrob: ada pepatah lain: "Jadikan berhasil, perbaiki
ninjalj
@ninjalj Itu bagus juga. Tentu saja, bos saya selalu memberi tahu saya: "Jangan terburu-buru, perbaiki." Jadi saya tidak tahu apakah yang Anda katakan sama universal dengan prinsipal yang diminta poster asli lebih detail.
ebyrob
2

Singkatnya, sangat sering mengoptimalkan awal menjadi usaha yang sia-sia jika Anda ingin mengubah sesuatu nanti, dan ternyata Anda telah mengoptimalkan struktur kode yang mudah diubah menjadi tingkat yang jauh lebih rendah, dan sekarang Anda perlu mengubahnya kembali ke tingkat yang lebih tinggi. pendekatan level, dan optimalkan hasilnya sekali lagi.

Ini adalah kesalahan umum bagi pengembang pemula yang suka fokus untuk mendapatkan kesenangan dari "telah melakukan pekerjaan yang bermanfaat", seperti optimasi, tidak memikirkan apakah ini saat yang tepat untuk melakukannya. Saat Anda mendapatkan pengalaman memprogram proyek-proyek besar seperti game, Anda akan belajar kapan diperlukan untuk mengoptimalkan basis kode yang ada, dan ketika itu terlalu cepat. Jangan takut melakukan kesalahan ini, Anda hanya akan mendapat manfaat dengan belajar darinya.

Secara umum, hanya optimalkan jika Anda benar-benar tidak dapat bekerja dengan pengembangan yang dibangun saat ini. Seperti jika Anda membuat dan menghapus jutaan objek 60 kali per detik.

Jadi, saya akan mengatakan itu baik, dalam hal pengalaman belajar, untuk mengoptimalkan awal beberapa kali: hal

pengguna1306322
sumber
2

Optimalisasi berfokus pada membuat komputer bekerja dengan baik pada kode sementara pengembangan membutuhkan membuat programmer bekerja dengan baik pada kode.

Optimalisasi tidak memunculkan wawasan. Itu membuat komputer bekerja lebih sedikit dan programmer lebih banyak.

Tentu saja, ada kategori yang berbeda, seperti dengan mendesain mobil. Tidak ada yang salah dengan mengoptimalkan mesin sebelum Anda membuat sasis pertama, tetapi mengoptimalkan sasis sebelum Anda tidak tahu bentuk mesin mungkin berakhir dengan buang-buang waktu. Modularitas dan spesifikasi bisa mendapatkan sejumlah tempat yang layak untuk pekerjaan optimasi bahkan sebelum produk secara keseluruhan dirakit.

pengguna101307
sumber
Ini akan ditingkatkan dengan memimpin dengan sesuatu selain mendefinisikan optimasi, dan dengan berbicara tentang situasi praktis dalam pengembangan game daripada mencari analogi tentang mobil.
doppelgreener
Jawaban sempurna untuk pertanyaan tautologis.
Fattie
2

Saya sangat tidak setuju dengan semua pernyataan itu bahwa kode tidak boleh dioptimalkan pada tahap awal. Itu tergantung pada tahap apa Anda. Jika ini adalah prototipe MVP dan Anda hanya mencoba untuk melihat permainan yang membutuhkan waktu 1 hingga 2 minggu, Anda siap untuk menulis ulang semua yang sudah Anda tulis. Ya, pengoptimalan tidak masalah. Tetapi jika Anda sudah mengerjakan game yang Anda tahu akan dirilis, itu harus dioptimalkan kode selama ini. Kisah-kisah yang orang katakan tentang fitur-fitur baru dan hal-hal yang dapat ditambahkan adalah kesalahan konsep. Ini arsitektur kode yang dirancang dengan buruk daripada kode yang dioptimalkan. Anda tidak perlu menulis ulang apa pun untuk menambahkan elemen baru jika Anda memiliki desain kode yang bagus.

Misalnya, jika Anda memiliki algoritma pathfinding A *, bukankah lebih baik untuk menuliskannya dengan cara yang paling optimal yang Anda bisa? Daripada kemudian mengubah setengah dari kode yang Anda miliki karena Anda harus membuat beberapa perubahan dalam algoritma karena sekarang perlu panggilan metode lain dan panggilan balik? Dan jika Anda sudah memiliki kode yang dioptimalkan sejak awal, itu akan menghemat banyak waktu. Karena Anda dapat menggambar sendiri hubungan antara objek dan bagaimana mereka berinteraksi, daripada membabi buta membuat banyak skrip baru dan membuat semua koneksi segera - ini mengarah pada kode sphagetti yang tidak jelas.

Hal terakhir yang ingin saya tambahkan adalah nanti, bahkan jika Anda tidak ingin membuat game lagi, Anda memiliki algoritma A * yang dioptimalkan yang dapat Anda gunakan untuk gim berikutnya. Dapat digunakan kembali. Misalnya, banyak game memiliki inventaris, penelusuran jalan, pembuatan prosedural, interaksi antara NPC, sistem pertarungan, AI, Interaksi antarmuka, manajemen input. Sekarang Anda harus bertanya pada diri sendiri - "Berapa kali saya menulis ulang elemen-elemen itu dari awal?" Mereka adalah 70-80% dari game. Apakah Anda benar-benar perlu menulis ulang sepanjang waktu? Dan bagaimana jika mereka tidak dioptimalkan?

Candid Moon _Max_
sumber
5
Jika kode A * harus mulai dioptimalkan, bagaimana dioptimalkan "dioptimalkan"? Apakah Anda melakukan handcode hal-hal dalam perakitan sebelum melakukan benchmark? Saya pikir umumnya pekerjaan non-sepele pada optimasi mikro harus dibiarkan sampai benchmark telah dilakukan. Kompilator pengoptimal mungkin melakukan pekerjaan yang lebih baik daripada Anda di optimasi mikro, dan jauh lebih murah.
gmatht
@ Gmatht Yah A * bisa berbeda seperti yang saya katakan itu dapat memiliki desain yang buruk dan tidak berfungsi dengan baik. Tetapi dapat multithreaded, berdasarkan tumpukan Fibonacci, memiliki beberapa prediksi, dibagi menjadi beberapa bagian. Jika kita berbicara tentang pathfinding, kita dapat menambahkan Flow Field untuk dicampur dengan A *. Juga cara smoothing, multi height. Mungkin A * bukan contoh terbaik, tetapi seperti yang saya katakan, optimasi tidak ada hubungannya dengan benar-benar mengubah kode, pengembang mengubah kode karena itu dirancang dengan buruk, misalnya dia tidak mengikuti SOLID sebagai 1 alasan. Jika Anda telah menulis A * ini dengan baik, Anda tidak perlu menulisnya lagi.
Candid Moon _Max_
@gmatht optimisasi mikro sebenarnya bukan masalah. Saya pikir OP berarti lebih banyak cara komputasi AI atau shader yang terbaik dan paling efisien atau apa pun.
Candid Moon _Max_
Jika Anda tahu cara menulis hal ini secara efisien, saya tidak melihat masalah melakukannya. Itu akan membutuhkan jumlah waktu yang sama atau bahkan lebih sedikit. Itu lebih tergantung pada pengalaman pengembang. Sebagai seorang programmer, jika saya tahu solusi yang lebih baik saya tidak akan menulis yang jelek. Jika saya tahu cara menulis tumpukan Fibonacci, saya tidak akan menggunakan Daftar dan pengurutan untuk mendapatkan nilai min atau maks. Tentu saja akan membutuhkan lebih banyak waktu dan upaya untuk menulisnya, daripada menggunakan List.Sort (); , tetapi bagaimana jika saya sudah memiliki yang dapat digunakan kembali?
Candid Moon _Max_
2
@CandidMoon Saya tidak setuju dengan "Ini akan memakan waktu yang sama atau bahkan lebih sedikit." untuk menulis kode yang efisien. Mungkin butuh waktu yang sama untuk menulis kode yang tidak jelas tidak efisien, tetapi ketika gagasan Anda tentang "efisiensi" berarti mempertimbangkan pertentangan cache, menambahkan multithreading, menggunakan intrinsik khusus CPU yang tersedia hanya pada beberapa CPU, alihkan algoritma untuk N besar karena O (N log N) vs O (N) - hal semacam itu sulit dan rawan kesalahan dan membutuhkan lebih banyak waktu daripada implementasi sederhana. Anda hanya melakukannya ketika Anda tahu itu akan mempengaruhi hasil akhir secara material.
Michael Anderson