Saatnya Untuk Membedah Program Kalian Supaya Lebih Optimal!
Lintang Erlangga - March 17th, 2021
Sebagai alumni tim robot di salah satu universitas di kota pelajar, Yogyakarta, saya pernah mengalami bahwa terpenuhinya objektif dari program yang kita buat aja gak cukup. Kondisi ini memaksa saya untuk cari tahu apakah sebuah kodingan yang menjadi dasar program tersebut ada alternatifnya? Meskipun saya belum tahu jawabannya pada saat itu, namun situasi tersebut memaksa saya untuk mengulik kodingan saya sehingga eksekusinya lebih optimal.
Daftar Isi
Waktu eksekusi yang lambat
Faktor pertama yang membuat saya pusing tujuh keliling saat itu adalah waktu eksekusi yang begitu lambat, terutama apabila kita gunakan sebagai kemampuan robot, dimana robot yang digunakan memerlukan kelincahan, yang artinya tingkat kelincahannya tentu akan bergantung dengan cepat atau lambatnya proses progarm yang kita buat.
Apalagi pas pertama masuk ke tim robot, saya yang belum begitu lancar ngoding, dan bahkan eksekusi di laptop pun runtime-nya begitu lemot, padahal kemampuan hardware untuk komputasi di robot di bawah laptop saya, terutama dari RAM-nya (fun fact: laptop saya AMD E1 Dual-Core 1.35 GHz dan RAM 6 GB, sedangkan robot menggunakan ARM Quad-Core 1,4 GHz dan RAM 2GB, tapi komputasi di laptop saya jauh lebih kenceng).
Yang jadi pertanyaan, emangnya apa aja sih yang mempenaruhi waktu eksekusi suatu program? Oke mungkin jawaban saya tidak begitu general, tapi setidaknya saya harap kalian dapat gambarannya.
From scratch!
Kalau bisa ngoding algoritma sendiri, cobalah bikin sendiri. Memang diantara banyak programmer saya yakin mereka tidak sepakat karena dianggap sebagai reinvent the wheel. Namun menurut saya beberapa library yang tersebar di internet itu terlalu banyak assertion ataupun if-else yang digunakan untuk pengecekkan kondisi, yang mana terkadang saya gak perlu itu.
Alasan pertama dari saya mengapa yakin tidak perlu itu karena saya yakin bahwa input atau argumen pada suatu modul pemrograman tidak mungkin sampai melewati kondisi tersebut. Wajar aja menurut saya jika library menyediakan pengecekkan beberapa kondisi, karena memang tujuannya adalah dibuat untuk general purpose.
Boros definisi
Selain itu, perhatikan beberapa variabel yang dianggap tidak perlu didefinisikan lagi pada suatu perulangan. Misal kita punya class sebut saja namanya, CalcInvers, yang tujuannya untuk menghitung invers matriks.
Apabila kita ingin melakukan perhitungan pada suatu perulangan seperti, for, while, ataupun do-while, kita tidak perlu mendefinisikan variabelnya lagi setiap perulangan itu terjadi. Yang perlu kita lakukan adalah mendefinisikan variabel tersebut di sebelum perulangan tersebut, bukan di dalam scope perulangannya.
Jadi alasannya begini, menurut saya faktor menurunnya waktu komputasi berada pada bagaimana, sebut saja konstruktor class CalcInvers tersebut, yang mungkin bisa jadi ada perhitungan sebelumnya. Selain itu, ketika suatu variabel didefinisikan, maka ada pengalokasian memori pada RAM, nah untuk kasus variabel yang didefinisikan di dalam perulangan, maka akan ada pengalokasian, kemudian menuju step berikutnya variabel dihapus kemudian dialokaskan lagi pada memori secara terus menerus. Kalau saya melihat kondisi itu lebih ke kasian sama komputernya.
Debugging
Buat nge-debug programnya biasanya saya menggunakan Boost.Chrono, lebih spesifiknya yaitu boost::chrono::high_resolution_clock, perlu diperhatikan bahwa kalau kalian menggunakan library pewaktuan lainnya, kita harus menggunakan system clock jangan CPU clock, karena CPU clock merupakan clock pada CPU kita yang mana besarnya berubah-ubah. Dan system clock sendirilah yang bertanggung jawab atas pewaktuan di komputer kita semua.
Perhatikan penggunaan memori
Mungkin untuk yang satu ini akan lebih condong ke bahasa pemrograman C++. Namun karena saya jarang ngoding bahasa pemrograman yang lain, jadi saya gak tahu apakah ada fitur yang serupa.
Hati-hati sama pointer
Pada C++, penggunaan memori bener-bener perlu diperhatikan, terutama ketika kalian menggunakan pointer. Mungkin diantara kalian ada yang mengatakan bahwa, ngapain kita pakai pointer kalau kita bisa buat reference variable, memang betul, namun pada kondisi tertentu dengan menggunakan pointer kita bisa menunjuk alamat yang lain kapan pun, dimana pada reference variable ketika sudah menunjuk satu alamat, maka selamanya akan tertuju pada alamat memori yang dimaksud.
Selain itu, pada pointer enaknya kita bisa assignnull pada variabel, alias bisa tanpa inisialisasi.
Nah yang perlu diperhatikan yaitu, bagaimana kita memanajemen memori ini. Jadi pointer itu ketika sudah keluar scope baris kode tertentu maka dia gak langsung dihapus atau hilang dari memori (dia berada di heap memory), kita perlu menghapusnya manual.
Tapi saat ini memang sangat kebantu banget semenjak adanya fitur unique_ptr, shared_ptr, dan lainnya. Dengan adanya wrapper tersebut kita tidak perlu mengatur kapan aja pointer-nya harus dihapus, karena akan dihapus otomatis oleh destruktor wrapper tersebut.
Gunakan inline
Untuk faktor yang kedua berkaitan dengan fungsi, gunakanlah inline pada fungsi yang mewakili tugas-tugas yang dianggap enteng tanpa perlu memerlukan barisan kode yang banyak.
Untuk memahaminya, kita perlu tahu dahulu bagaimana suatu fungsi tersebut dipanggil, jadi suatu fungsi dipanggil maka komputer kita akan menuju letak alamat dimana isi dari fungsi-fungsi tersebut disimpan, kemudian loncat lagi ke alamat-alamat argumen yang digunakan, setelah fungsi dieksekusi kemudian loncat lagi ke alamat memori terakhir dimana fungsi tersebut dipanggil. Dengan adanya fitur inline maka compiler akan meletakkan isi dari suatu fungsi pada baris kode dimana fungsi tersebut dipanggil.
Jangan dialokasikan lagi
Kebetulan kita membahas fungsi sebelumnya, jadi kalau bisa argumen yang akan kita gunakan pada suatu fungsi merupakan const-reference, terutama untuk variabel-variabel yang bukan POD (Plain Old Data, seperti int, double dsb.), kasusnya mirip seperti definisi variabel pada perulangan.
Dari pada kita mengalokasikan variabel baru, kita gunakan saja alamat dari variabel yang menjadi argumen tersebut. Nah disini penambahan const sendiri menunjukkan bahwa variabel tersebut tidak akan kita ubah selama berada di fungsi tersebut, kita hanya memanfaatkan nilai ataupun proses-prosesnya.
Debugging
Ngomong-ngomong cara nge-debug penggunaan memori program gimana sih? Kalau saya biasanya tutup semua program yang kita buka, terus kita eksekusi program yang mau kita amati, dan buka System Monitor (khusus Ubuntu).
Kemudian kita lihat pada bagian Memory and Swap History, biasanya kalau saya dulu ngejalanin programnya kurang lebih 30 menit terus diperhatikan aja apakah kenaikannya normal apa enggak (besar atau kecilnya tergantung, dan yang tahu hanya yang ngerti programmnya) dari grafik yang ada.
Mungkin ini aja berbagi pengalaman saya yang sedikit ini, semoga bisa bermanfaat buat kita semua. Jika kalian ada saran atau masukkan, atau bisa juga pertanyaan, mari kita diskusikan di kolom komentar. Terima kasih.