Author Archives: admin

Kisah Quick Fix Aplikasi Web PHP 5

Belum lama ini saya diminta tolong memperbaiki aplikasi lama dalam PHP 5. Aplikasi ini sudah lama dan masih dipakai sampai development aplikasi baru selesai. Aplikasi ditulis dalam PHP dengan framework Code Igniter dengan database MySQL. Deskripsi masalahnya begini: di aplikasi ini setiap hari ada satu halaman yang semakin lambat sampai suatu hari error, tidak bisa diakses lagi.

Pesan popup itu muncul dari komponen datatables.net di browser. Setelah diselidiki: penyebabnya adalah error di sisi server, tepatnya lagi ternyata out of memory di sisi server.

Allowed memory size of 134217728 bytes exhausted (tried to allocate 7077931 bytes) /var/www/html/application/XXX.php

Aplikasi ini meload data dari database menjadi satu file JSON berisi beberapa belas ribu baris. Dari hasil membaca dokumentasi komponen datatables ini, seharusnya mudah membuat paging dengan pemrosesan di sisi server cukup dengan menambah OFFSET dan LIMIT pada query SQL. Tapi ternyata tidak mudah di aplikasi ini.

Kira-kira aplikasinya begini:

$q = "select puluhan_kolom from table where belasan_filter;";
$data=array()
foreach ($rows as $row){
    //isi loop for ini ratusan baris 
    //ada beberapa puluh query berdasarkan nilai $row
    //dalam kondisi tertentu data baru ditambahkan ke $data
}
echo json_encode($data);


Jadi untuk tahu berapa jumlah row-nya perlu menjalankan banyak query, bukan hasil count dari query utama. Andaikan tahu jumlah row-nya pun, tetap akan sulit untuk pergi ke halaman-N karena harus memfilter lagi dari awal. Dari berbagai kondisi di dalam loopnya sepertinya banyak hal ad-hoc ditambahkan, atau mungkin juga designnya salah dari awal.

Lalu setelah saya pikirkan lagi: Jika bisa paging pun, fitur lain harus diubah. Datatables menyediakan fitur filtering dan search built in. Jika kita meload data per page, search dan filter ini harus diimplementasikan ulang dengan kode baru di sisi server.

Jadi apa yang bisa saya lakukan? permintaan tolong ini di hari kerja, jadi saya cuma bisa melakukan ini:

  • menaikkan memory limit PHP
  • menambah ukuran cache mysql
  • menginstall PHP opcode cache (APC)

Beberapa hal lain yang tidak saya lakukan:

  • menginstall PHP 7, karena distribusi Linuxnya terlalu tua. Ribet jika harus menginstall manual
  • mengubah kode program karena terlalu rumit

Programnya bisa berjalan lagi walau seringkali butuh beberapa menit mendownload JSON sekitar 5.5 MB. Dengan koneksi di indonesia, jika bisa stabil 100 kilobyte/second maka butuh 55 detik untuk mengirim datanya. Itu di luar waktu query, karena transfer data baru bisa dimulai setelah semua data siap. Dari cerita yang saya dapat: Koneksi 100 kilobyte/second (kira-kira 1 Mbit/s) sebenarnya tidak sulit dicapai tapi ketika sambil sharing screen di meeting Zoom, kecepatannya bisa sangat tidak stabil. Belum lagi jika dua orang mengakses halaman yang sama untuk didiskusikan.

Mungkin ada yang heran: memory limit kan 128 MB dan ukuran JSON 5.5MB itu sebenarnya sangat kecil, kenapa bisa out of memory? Struktur internal PHP (terutama PHP versi sebelum 7) sangat tidak efisien, ukuran memori yang dipakai bisa belasan kali lipat dari tipe data dasar di bahasa lain (pernah saya tuliskan di artikel tahun 2009). File JSON itu hanya bentuk akhir saja, array yang menyimpan data sebelum jadi JSON memakai memori besar dan setiap query juga memakan memori.

Quick Fix

Di akhir pekan, saya ubah kodenya seminimal mungkin agar cukup cepat. Pertama yang terlihat jelas adalah mengkompres data. Saya sudah menguji bahwa data JSON hasil aplikasi jika dikompres ukurannya menjadi kurang dari 700kb. Dari kecepatan download saja: data 700 kb bisa didownload dalam 7 detik jika koneksinya 100 kilobyte/second vs 55 detik data aslinya.

Di PHP, kompresi bisa dilakukan dengan:

    ob_start("ob_gzhandler", 16*1024);
    //outputkan data seperti biasa dengan echo
    @ob_end_flush();

Dengan modifikasi kecil ini hasilnya: data bisa diakses kurang dari 7 detik setelah dihasilkan. Masih ada masalah lain: harus menunggu dulu sampai semua query selesai baru bisa dikompresi. Langkah berikutnya adalah mengoutputkan JSON per elemen array. Jadi loopnya diganti kira-kira menjadi seperti ini:

ob_start("ob_gzhandler", 16*1024);
echo '{"data":[';
$first = false;
foreach (/*row*/) {
        if ($data_ok) {
              if ($first)   $first = false; else echo ",";
              echo json_encode($current_data);
        }
}
echo "]}";
@ob_end_flush();

Dengan ini sambil melakukan query, data sudah bisa mulai dikirim ke browser. Aplikasi juga menjadi lebih cepat karena tidak menyimpan data di memori, dan tidak perlu menghasilkan string ukuran besar dengan JSON.

Dengan dua quickfix tersebut plus yang sudah dilakukan sebelumnya (menambah limit memori, memakai cache APC, dan menaikkan limit cache mysql), aplikasi bisa diakses kurang dari 10 detik. Menurut saya ini masih lambat, seharusnya dengan design yang baik, aplikasinya bisa instan menampilkan halaman pertama, dan setiap halaman berikutnya bisa meminta request baru. Tentunya akan lebih banyak kode yang perlu ditulis baik di sisi client maupun server.

Pelajaran

Sebagai catatan: posting ini bukan kritik terhadap PHP. Dalam bahasa manapun kesalahan seperti ini bisa terjadi. Tapi faktor kerakusan memori PHP dan lambatnya PHP versi lama membuat bugnya lebih cepat muncul.

Jika Anda adalah seorang client yang meminta dibuatkan aplikasi: perhatikan ukuran data. Selain menguji bahwa aplikasi sudah berjalan, ujilah juga aplikasi dengan jumlah data yang besarnya sesuai dengan pemakaian. Misalnya ingin membuat aplikasi untuk sekolah offline, jumlah murid mungkin bisa ratusan, atau ribuan sampai beberapa tahun ke depan. Dalam kasus ini jumlah murid tapi tidak akan sampai level jutaan.

Ini juga cerita secara umum: membuat aplikasi sederhana memang tidak sulit, tapi membuat aplikasi yang scalable akan butuh waktu lebih lama dan/atau kode yang lebih banyak. Cerita ini hanya sekedar scaling dari ratusan data (sangat cepat ketika didemokan) sampai ke belasan ribu data (aplikasi sangat lambat lalu mati), belum sampai ke level jutaan atau ratusan juta data yang butuh perencanaan yang lebih matang dan implementasi yang lebih sulit.

Mencoba Bahasa Dart dan SDK Flutter

Posting ini akan membahas pengalaman singkat saya memakai bahasa pemrograman Dart dan Software Development Kit (SDK) Flutter di sebuah proyek kecil. Flutter merupakan SDK pembuatan user interface (UI) yang membuat Dart jadi dikenal untuk UI Development. Ini seperti seperti Rails yang membuat Ruby jadi terkenal untuk web development (Ruby on Rails).

Dart programming language

Saya akan bahas sedikit mengenai sejarah bahasa Dart ini supaya jelas bahwa: bahasa ini bukan bahasa baru, dibuat perusahaan besar (Google), memiliki standard resmi (ECMA), dan sudah memiliki sejarah cukup panjang.

Sejarah Dart

Bahasa pemrograman Dart diumumkan Google sejak 2011 dan dirilis publik tahun 2013. Tadinya bahasa ini dikembangkan untuk web browser untuk menyaingi Java Script. Awalnya ada kabar bahwa Virtual Machine untuk Dart akan dimasukkan Chrome, tapi rencananya berubah: Dart ditranslasikan (transcompile) saja ke JavaScript, jadi tidak ada VM khusus Dart dalam browser.

Bahasa Dart ini sempat distandardkan dalam ECMA standard 408 tahun 2014, tapi tahun 2018 bahasanya diubah dengan rilis 2.0 sehingga tidak sepenuhnya kompatibel lagi dengan versi sebelumnya. Spesifikasi terbaru saat ini bisa dilihat di sini. Tapi JavaScript juga terus berubah, plus masih ada saingan bahasa lain yang ditranslasikan ke JavaScript (misalnya TypeScript, CoffeeScript, dsb) jadi Dart kurang menonjol.

Selain Dart, Google juga memiliki bahasa lain yang lebih populer: Go. Selama beberapa tahun nasib dan tujuan Dart ini menurut saya kurang jelas, sampai ketika tahun 2015 Flutter diumumkan.

Bahasa Dart

Bahasa Dart ini menurut saya cukup normal, tidak memiliki konsep yang sangat aneh. Menurut saya ini bagus karena beralih dari berbagai bahasa ke Dart tidak sulit.: “the Dart language aims to be unsurprising“. Contoh sederhana hello world seperti ini:

main()
{
       print("hello world");
}

Berbagai konstruksi standar bahasa Dart mirip dengan bahaca lain yang C-like. Misalnya:

  • komentar seperti C/C++: /*block comment*/ dan //line comment
  • sintaks if adalah: if (x) { /*aksi*/ } else { /*aksi lain*/)
  • sintaks for loop: for (int i=0; i < 10; i++) {/* aksi */}
  • sintaks while loop: while(kondisi) { /*aksi*/} atau do { /*aksi*/} while (kondisi)

Sintaks OOP-nya juga tidak terlalu jauh dari bahasa C++/Java/C#. Berbagai konsep dari bahasa modern, misalnya mixin, nullable type, null safety, named parameter, async, dsb juga ada di Dart. Ringkasannya: beralih dari bahasa lain ke Dart ini cukup mudah.

Flutter

Flutter adalah SDK yang untuk membuat aplikasi GUI di Android, iOS, Windows, Linux, Web dan Google Fuschia dari satu kode Dart. Flutter juga memungkinkan kita melakukan “live editing“, jadi begitu kode diubah dan disimpan, user interface juga langsung terupdate. Flutter ini diklaim mampu menampilkan user interface 120fps dengan mudah.

Setelah beberapa tahun sejak diumumkan, akhir tahun 2018, Flutter versi 1.0 dirilis. Tapi ternyata sampai saat ini menurut saya Flutter masih belum matang. Masih banyak breaking changes yang muncul. Ini artinya kode yang Anda tulis sekarang mungkin tidak jalan di versi berikutnya.

Kebetulan ada proyek yang saya kerjakan yang membolehkan saya menggunakan Flutter. Ini sekedar proyek Proof Of Concept, jadi nanti bisa ditulis ulang ke bahasa lain. Salah satu alasan kenapa sebuah perusahaan memilih atau menolak bahasa atau teknologi tertentu adalah: takut tidak ada yang bisa meneruskan.

Aplikasi yang saya buat sederhana, memanfaatkan kamera dan Firebase Machine Learning (Firebase ML). Firebase ML adalah bagian dari platform Firebase untuk memudahkan pemakaian machine learning di aplikasi mobile. Contohnya: kita bisa mendeteksi sebuah wajah, dan mendeteksi apakah sedang tersenyum.

Hal-hal yang sudah bagus

Bahasa Dart cukup menyenangkan dan mudah dipakai. Seperti tujuan designnya, bahasa ini dirancang agar tidak mengejutkan dan menurut saya cukup berhasil. Saya tidak punya keluhan khusus di bahasa Dart-nya sendiri.

Flutter sudah didukung oleh Visual Studio Code dengan baik. Instruksi untuk mulai menggunakan Flutter sangat jelas dan saya bisa membuat Hello World dengan cepat. Untuk target Android, Android Studio juga sudah mendukung Flutter dengan baik. Ada tool command line “flutter doctor” yang bisa membantu mendeteksi beberapa masalah dasar.

Library Dart masih sedikit

Beberapa bahasa dianggap terbaik untuk teknologi tertentu karena ketersediaan librarynya. Contoh: Python dianggap terbaik untuk Machine Learning karena sudah ada banyak library Machine Learning tersedia. Jumlah library Dart/Flutter masih sedikit dibandingkan bahasa lain. Library Dart adalah library yang terpakai untuk Dart dan Flutter, sedangkan library Flutter untuk Flutter saja.

Jika dilihat sekilas, memang sepertinya banyak library Dart/Flutter sudah ada, tapi baru terasa ketika membangun aplikasi tertentu. Kadang librarynya sudah ada, tapi fungsi tertentu tidak tersedia.

Kadang sebuah library hanya berjalan untuk versi OS tertentu saja. Plugin resmi juga sifatnya sebagian masih beta, misalnya WebView dan In App Purchase. Kadang library memiliki bug yang lama belum diperbaiki, misalnya di iOS camera memiliki memory leak sejak 2018.

Perlu dicatat bahwa untuk berbagai aplikasi sederhana, library yang tersedia sudah cukup. Aplikasi yang sekedar memakai form yang tidak terlalu kompleks, mengirim data ke web mudah dibuat dan bisa dibuat lebih cepat dibandingkan memakai teknologi lain.

Flutter Masih berkembang

Sistem plugin flutter untuk Android belum lama ini diupdate (Desember 2019), jadi beberapa plugin versi lama tidak berjalan atau memiliki warning. Untuk yang masih berjalanpun, tidak cepat updatenya ke versi terbaru. Saya juga sempat mendapati masalah dengan contoh dari github yang ternyata masih memakai versi plugin lama dan dicampur dengan plugin lain yang memakai library baru.

Semoga saja tidak ada update penting lagi dari Flutter/Dart, karena jika sering ada update maka semakin banyak plugin yang tidak berjalan. Secara umum ini adalah masalah teknologi baru: masih banyak perubahan yang membuat kode lama menjadi tidak berjalan.

Dart kurang cepat

Dibandingkan C, Dart masih puluhan kali atau ratusan kali lebih lambat lebih lambat. Contohnya jika kita ingin memproses gambar dalam Dart, hasilnya akan butuh beberapa detik. Jadi untuk pemrosesan tingkat rendah, sebaiknya memakai plugin atau kode dalam bahasa C.

Dalam kasus aplikasi kecil saya: stream data dari kamera formatnya adalah YUV, sedangkan image library untuk mengencode JPG/PNG perlu input dalam format RGB. Sudah ada yang memberikan kode ini dalam Dart, tapi memproses jutaan piksel ternyata lambat. Supaya cepat, kita harus memanggil kode dalam C.

Komentar: https://github.com/flutter/flutter/issues/26348#issuecomment-617266911

Ini kasus yang nyata dan penting. Contohnya adalah Issue ini di github yang masih aktif dari awal tahun hingga saat ini: https://github.com/flutter/flutter/issues/26348. Untuk pemrosesan di sisi server yang tidak butuh interaktif, 4 detik mungkin cepat, tapi jika tiap kali mengambil foto kamera berhenti selama 4 detik, maka ini akan membuat pengguna kesal.

Foreign Function Interface (FFI) di Dart masih Beta

Jika sebuah bahasa tidak memiliki library atau fitur untuk melakukan sesuatu, maka biasanya kita perlu memakai library dari bahasa lain. Ada banyak cara untuk melakukan ini, misalnya dengan plugin, tapi cara lain adalah dengan FFI (foreign function interface). Intinya kita ingin menulis dalam bahasa tertentu (biasanya C) lalu dipanggil langsung dengan Dart. FFI ini juga kadang dipakai jika Dart terlalu lambat melakukan hal tertentu.

Masalahnya fitur FFI di Dart masih beta, dan tidak bisa dipakai di Dart rilis stabil terbaru. Dalam kasus github yang saya sebutkan sebelumnya: proses yang lambat (butuh beberapa detik dalam dart) bisa selesai dalam hitungan miliseconds di C. Selain itu masih ada kemungkinan berubah:

As of Dart 2.7, dart:ffi is in beta, and breaking API changes might still happen. If you’re developing a Flutter app, you can get access to dart:ffi by using the Flutter dev channel, as described in the Flutter dart:ffi page.

https://dart.dev/guides/libraries/c-interop

Digabungkan dengan beberapa hal di atas, ini jadi masalah yang cukup serius:

  • Ingin memakai plugin tertentu: ternyata belum ada atau belum diupdate
  • Ingin diimplementasikan dalam Dart, tapi ternyata tidak cukup cepat
  • Ingin memakai kode C: tapi FFI tidak bisa jika tidak memakai versi beta

Library kurang cepat diupdate

Saya berharap library Firebase dari Google untuk Flutter cepat diupdate karena Firebase ini adalah produk Google dan Flutter juga produk Google, tapi ternyata tidak demikian. Flutter ini masih dinomorduakan oleh Google dibandingkan yang lain.

Di awal Juni 2020 Ada update Firebase untuk memisahkan bagian AI offline dari yang online di Firebase, tapi sampai saat ini (akhir Juni) belum juga library flutter di update setelah hampir sebulan.

Penutup

Saya berharap Google lebih memperhatikan Flutter lagi. Untuk sistem operasi Google Fuschia, Google akan perlu bahasa yang cocok untuk development aplikasi GUI. Bahasa Go kurang cocok karena sifatnya yang harus dikompilasi (walau kompilasi cukup cepat), sedangkan Google selalu memliki masalah dengan lisensi Java dengan Oracle (dan Kotlin masih sangat erat dengan Java).

Untuk pengembangan cross platform, masih banyak teknologi cross platform lain yang lebih matang (seperti misalnya React Native atau bahkan Cordova). Tapi Flutter ini menurut saya sangat menarik dan developmentnya cukup fun. Jika perkembangannya lancar, mungkin ini bisa jadi bahasa pilihan untuk pembuatan aplikasi GUI.

Assembler di browser berbasis Keystone dengan WebAssembly

Sebenarnya saat ini saya sedang sibuk dengan banyak hal, tapi karena sedang berduka, saya ingin melakukan sesuatu untuk mengalihkan pikiran. Bapak saya suka membongkar mobil untuk mengalihkan pikiran, sedangkan saya lebih suka memprogram sesuatu yang tidak berhubungan dengan pekerjaan. Hasilnya: hari ini saya mengkompilasi framework Keystone dengan target WebAssembly (Wasm) lalu memberi interface HTML dengan Preact (alternatif React yang ukurannya jauh lebih kecil).

Assembler ini bisa diakses online di https://asm.x32.dev dan sourcenya saya berikan di https://github.com/yohanes/online-assembler . Aplikasinya bisa diakses dengan semua browser modern (Safari, Chrome, Firefix, Edge), termasuk juga mobile browser (sudah ditest di iOS 13 dan Android 10).

WebAssembly (Wasam)

Supaya tidak bingung dengan penjelasan assembler dan webassembly, akan saya jelaskan apa itu WebAssembly (biasa disingkat dengan Wasm). WebAssembly adalah format instruksi biner untuk virtual machine (bisa dibayangkan seperti bytecode Java) . Saat ini target utamanya adalah web browser (saat ini sudah disupport di Firefox, Google Chrome, Safari dan Edge). Di masa depan web assembly ini dihrapkan akan bisa dipakai juga di aplikasi desktop maupun server.

Sekarang ini sudah ada compiler supaya kita bisa mengkompilasi bahasa C, C++, Rust, dsb ke kode WebAssembly. Setelah dikompilasi, kodenya bisa dijalankan di browser. Sayangnya API web browser tidak bisa diakses oleh kode web assembly, jadi diperlukan jembatan berupa kode JavaScript.

Keystone

Keystone merupakan library assembler untuk berbagai arsitektur. Diberikan teks assembly, kita bisa memakai library ini untuk mengubahnya menjadi bahasa mesin. Keystone ini sebenarnya memakai bagian dari libary LLVM, tapi diekstrak bagian assemblernya saja. LLVM sendiri merupakan proyek infrastruktur compiler yang memiliki banyak fitur (parser, optimizer, assembler, linker, debugger, dsb) tapi terlalu kompleks dan terlalu besar jika hanya ingin fitur assemblernya saja.

Untuk keperluan development program dalam assembly, Keystone ini belum terlalu layak pakai karena banyak bugnya dan tidak memiliki fitur macro yang ada di banyak assembler modern. Untuk development, sebaiknya pakai saja assembler dari berbagai compiler yang ada. Keystone ini lebih terpakai untuk riset security, patching program, dan berbagai fungsi kecil lain yang overkill jika memakai assembler lengkap dari sebuah compiler. Contoh pemakaian Keystone adalah untuk menyelesaikan soal CTF semacam yang saya selesaikan dua tahun yang lalu.

Jadi supaya jelas: assembly di WebAssembly tidak berhubungan dengan assembler di library Keystone (saat ini keystone belum mendukung web assembly).

EMSDK

Dengan menggunakan emscripten SDK (emsdk), sekarang kita bisa mengkompilasi banyak library dan program yang ditulis dalam bahasa C/C++ dengan target Web Assembly dengan relatif mudah. Jika program atau library memakai configure, kita bisa menjalankan emconfigure ./configure dan jika memakai cmake, kita bisa menjalankan emcmake cmake. Program emconfigure dan emcmake merupakan wrapper, agar ketika configure/cmake dijalankan, maka compiler dan library yang akan dipakai adalah untuk target WASM.

Tentunya tidak semua program dan library bisa dikompilasi dengan mudah. Contoh kode yang tidak bisa dicompile langsung: kode yang butuh library lain, kode yang butuh akses hardware, kode yang butuh akses layanan sistem operasi tertentu, kode yang memakai kode assembly dalam Intel x86 atau ARM, dan masih banyak lagi kasus khusus lain. Dalam kasus seperti ini, kita perlu melakukan porting manual.

Selain mengkompilasi file C menjadi WASM, emsdk juga menghasilkan file Javascript yang gunanya menjadi jembatan antara Javascript dan WASM. Jembatan ini dua arah: kode C yang memanggil fungsi library standar seperti printf akan memanggil kode JavaScript (misalnya kita arahkan menjadi memanggil console.log), dan kita bisa memanggil kode C dari Javascript.

Optimasi ukuran

Saat ini kode yang dihasilkan dari EMSDK masih belum efisien. Library Keystone ketika dicompile dengan target Web Assembly, hasilnya menjadi file 9.7 megabyte. Saat ini ada proyek lain bernama binaryen yang merupakan infrastruktur compiler untuk WebAssembly. Salah satu toolnya adalah wasm-opt yang dapat digunakan untuk mengoptimasi bytecode WASM. Tool yang lain adalah disassembler Web Assembly (disassembler ini saya pakai untuk menyelesaikan soal CTF).

Setelah dioptimasi, library Keystone yang berukuran 9.7 megabyte berkurang menjadi 5.7 megabyte saja. Ketika didistribusikan, file ini masih akan dikompresi dengan gzip dan hasilnya menjadi 1.5 megabyte. Ukurannya memang tidak kecil, tapi kebanyakan foto dan video ukurannya juga sudah ratusan kilobyte.

User Interface

Bagian berikutnya yang perlu dilakukan adalah membuat user interface yang bagus untuk library yang kita compile tersebut. Untuk program yang memakai Simple DirectMedia Layer (SDL) dan OpenGL, kadang kita bisa langsung menjalankannya karena emsdk sudah menyediakan wrapper untuk API HTML Canvas dan WebGL. Sedangkan untuk aplikasi lain, kita perlu membuat interface HTML dan menggunakan JavaScript untuk memanggil kode C.

Alasan saya memporting Keystone agar bisa memakai assembler keystone terbaru dari browser. Sudah ada yang membuat keystone.js tapi sudah lama tidak diupdate.

Tadinya saya hanya ingin memakai HTML + Javascript murni saja untuk user interfacenya. Tapi setelah saya pertimbangkan lagi, memakai library Javascript akan membuat kodenya jadi lebih terstruktur. Tadinya saya akan memakai React, tapi aplikasi kecil dalam React butuh ratusan kilobyte (terutama karena bagian ReactDOM). Jadi saya memakai Preact yang memakai API yang (hampir) sama dengan React tapi ukurannya hanya 9 kilobyte saja (bahkan hanya 4 kilobyte setelah dikompres).

Development JavaScript Modern biasanya dilakukan dengan menggunakan build system, tapi sebenarnya tidak wajib. Karena program saya ini sangat kecil, saya putuskan untuk tidak memakai build system. Semua kode langsung masuk ke file HTML dan JS. Karena saya memakai WASM, browser harus cukup modern, jadi saya bisa memakai berbagai fitur HTML/JS terbaru tanpa butuh berbagai library polyfill.

Penutup

Salah satu cara belajar teknologi Web Assembly adalah dengan memporting berbagai library dan program ke browser dan membuat atau menyesuaikan user interface agar berjalan di browser. Bahasa C/C++ sudah ada sejak lama, dan sudah banyak hal menarik yang dibuat dalam C, berbagai kode yang sudah ada ini bisa dibawa ke browser dan dieksekusi langsung tanpa butuh server (server hanya sekedar untuk menyimpan file saja, tidak untuk eksekusinya).

Semoga artikel singkat ini berguna untuk memulai eksplorasi Web Assembly, dan semoga assembler onlinenya juga bisa terpakai di masa depan, baik untuk saya sendiri maupun orang lain.

Bahasa Pemrograman BASIC

Setelah membahas Forth dan juga Lua, kali ini saya akan membahas bahasa BASIC (Beginners’ All-purpose Symbolic Instruction Code). Bahasa BASIC ini merupakan bahasa yang sudah ada lama sekali (sejak 1964), tapi ada beberapa hal yang baru-baru ini terjadi yang membuat saya ingin menuliskan tentang bahasa BASIC:

Tiga hal pertama tersebut menunjukkan bahwa:

  • Minat terhadap bahasa lama (BASIC dan COBOL) ternyata masih cukup besar
  • Bahasa-bahasa lama ini masih terpakai (tapi dalam artikel ini saya hanya membahas BASIC)

Setelah membaca mengenai SmileBASIC 4, saya segera membeli softwarenya dari Nintendo Shop. Harga softwarenya 25 USD (masih lebih murah dari kebanyakan game di Nintendo Shop), plus harus membeli slot upload seharga 5 USD jika kita ingin mengupload program kita ke server SmileBASIC.

Setelah mencoba-coba lagi Smile BASIC, ternyata memang bahasa BASIC ini masih bisa membuat saya tersenyum Saya masih bisa membuat program dalam bahasa ini dengan cepat, dan bisa membuat animasi sederhana untuk Joshua dalam waktu sangat sebentar (dalam hitungan menit). Saya juga teringat dulu masa-masa saya belajar sendiri bahasa BASIC. Jadi saya ingin bercerita lebih jauh mengenai bahasa BASIC ini.

Animasi huruf yang dibuat hanya dalam beberapa menit

Di masa kejayaannya, BASIC ada di mana-mana:

  • Ada novel petualangan yang di dalamnya ada kode bahasa BASIC (pernah saya tulis di sini)
  • Berbagai majalah memberikan listing program bahasa BASIC
  • Berbagai buku teks mahasiswa juga diberi kode dalam bahasa BASIC untuk bisa langsung dicoba

Banyak orang menghindari bahasa BASIC karena sifatnya yang tidak terstruktur. Ini memang benar untuk BASIC generasi pertama. Ilmuan komputer Djikstra sering dikutip karena mengkritik BASIC, begini katanya:

“It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration”,

Edsger W. Dijkstra – “How do we tell truths that might hurt” . Selected Writings on Computing: A Personal Perspective

Tapi sebenarnya di tulisan lengkapnya, Djikstra tidak cuma mengkritik BASIC, tapi juga bahasa lain seperti FORTRAN dan COBOL yang sampai saat ini masih dipakai.

Setelah kritiknya itu, sebenarnya bahasa BASIC masih berkembang dan generasi berikutnya sebenarnya sudah cocok menjadi bahasa pemula dan bisa digunakan untuk mengajarkan berbagai konsep pemrograman yang baik.

Beberapa Generasi

Bahasa generasi pertama memakai nomor baris, konsep fungsi dan subrutinnya terbatas, dan sulit membuat struktur data kompleks. Contoh keterbatasan adalah: fungsi hanya sekedar formula, tidak bisa membuat fungsi rekursif. Subrutin memakai nomor baris (tidak memakai nama) dan tidak memiliki variabel lokal. Beberapa implementasi bahkan tidak memiliki DEF untuk membuat fungsi. Contoh versi BASIC ini adalah yang ada pada ROM PC XT, dan ROM berbagai hardware komputer rumah tahun 80an.

Seperti apa sih program BASIC ini? ini adalah program sangat sederhana yang tidak berhenti menampilkan “HELLO WORLD”. Programnya mudah dimengerti: cetak tulisan hello world, dan berikutnya GOTO 10, artinya ulangi lagi baris 10, dst.

10 PRINT "HELLO WORLD"
20 GOTO 10

BASIC sangat populer sehingga pernah dibuat dua standard (ISO/ANSI) tentang bahasa BASIC (yang core dan yang full). Kedua standardnya hanya mencakup generasi pertama yang masih menggunakan nomor baris. Bahasa BASIC juga masuk ke banyak device yang bukan PC, misalnya game console dan juga kalkulator.

20161127_220343
Saya program HP 39gs untuk menampilkan hari pasaran dari sebuah tanggal

BASIC generasi kedua (Structured Basic) tidak lagi memakai nomor baris (walau beberapa versi secara opsional membolehkan nomor baris), mendukung fungsi dan subrutin dengan variabel lokal, dan mendukung user defined type (struct/record). Contoh BASIC generasi kedua adalah Microsoft QuickBasic.

Tanpa nomor baris, program hello world bisa mirip Python 2 (Python 3 mewajibkan kurung). Apalagi BASIC ini sifatnya tidak case sensitive, jadi PRINT sama dengan print.

print "hello world"

BASIC generasi ketiga sudah seperti bahasa pemrograman lainnya yang berbasis objek, hanya saja syntax-nya meniru BASIC yang lama. Contoh BASIC generasi ketiga ini adalah Visual Basic .NET. Tapi saat ini minat bahasa BASIC generasi terbaru sudah mulai berkurang, bahkan Microsoft sudah tidak akan menambah lagi fitur di Visual Basic.NET.

Menurut saya sendiri, BASIC generasi kedua ini sudah cukup ideal untuk berbagai aplikasi kecil dan program ukuran kecil yang fun. Jika ingin memprogram berorientasi objek, sekalian saja pakai Java/C#/Python atau bahasa lain.

Sederhana

Kenapa bahasa BASIC sempat populer dibandingkan bahasa lain? menurut saya ini gabungan beberapa hal. Masalah utama adalah Keterbatasan hardware: sulit membuat compiler dengan memori terbatas di hardware awal masa komputer personal (awal 1980an). Jadi alternatifnya adalah: memprogram dalam assembly, atau dalam bahasa lain yang sederhana. BASIC menang dibandingkan bahasa lain (misalnya FORTH yang pernah saya bahas di sini), karena sangat mudah dimengerti walaupun baik BASIC maupun FORTH keduanya merupakan bahasa yang interaktif.

Cassette Basic. Pengguna PC XT dulu akan ingat ini. Memori yang bisa diakses kurang dari 64kb

Kesederhanaan Bahasa BASIC ini bisa dilihat dari jumlah statement dasar yang dimiliki. Versi awal yang dikembangkan pencipta BASIC: John G. Kemeny dan Thomas E. Kurtz (disebut Darmouth BASIC) hanya punya 15 statement (LET, PRINT, END, DATA, READ, GOTO, IF, FOR/NEXT, GOSUB, RETURN, DIM, REM, STOP, DEF). Plus ada beberapa fungsi built in seperti misalnya ABS untuk mendapatkan nilai absolute atau SIN/COS/TAN untuk trigonomoetri.

Dengan kesederhanaan ini, mengimplementasikan interpreter BASIC bisa dilakukan dengan memori kecil. Salah satu kesuksesan awal Microsoft (waktu itu namanya masih Micro Soft, bukan satu kata) adalah produk interpeter BASIC yang ditulis oleh Bill Gates dalam assembly (kodenya sudah dibuka, ada di github). Bahkan di kode assembly yang relatif kecil, Bill Gates bisa menyembunyikan Easter Egg.

Kesederhanaan bahasa BASIC juga bisa digunakan sebagai latihan ketika mengimplementasikan sebuah interpreter. Saya sendiri pernah mengimplementasikan interpreter BASIC dalam JavaScript 10 tahun yang lalu (masih berfungsi ada sampai sekarang).

Instan

Kebanyakan implementasi BASIC, bahkan dari jaman PC XT dulu, eksekusinya hampir instan. Dari mengetik kode hingga berjalan tidak dibutuhkan waktu berdetik-detik atau bermenit-menit. Selain itu hampir semua implementasi BASIC menggabungkan editor dan interpreter, jadi mengetik, menjalankan program, dan bahkan mendebug bisa dilakukan di environment yang sama.

Experience memprogram BASIC ini menurut saya masih sulit ditemui di sistem lain, kecuali jika kita mendownload IDE super lengkap dengan ukuran besar. Bahkan berbagai IDE modern dengan fitur lengkap sering sulit dikonfigurasi.

Meskipun cycle untuk mengedit dan menjalankan program bisa cepat, tapi program yang ditulis dalam BASIC relatif lambat. Untungnya sejak awal sebagian besar implementasi BASIC bisa memanggil kode mesin.

Kode dalam bahasa BASIC umumnya dijalankan dalam sebuah interpreter, tapi sejak puluhan tahun yang lalu sudah ada banyak compiler Bahasa BASIC yang bisa menghasilkan native code. Jadi tidak benar jika bahasa BASIC pasti harus diinterpretasi.

Menengok implementasi BASIC Modern

Saat ini ada banyak implementasi BASIC modern, dan saya sudah mencoba cukup banyak. Saya tidak akan membahas semuanya, dan hanya yang menurut saya menarik saja.

QB64

QB64 ini open source dan meniru user interface QBasic/QuickBasic. QB64 tersedia untuk multi platform. Meskipun meniru QBasic, fitur-fitur baru yang dimiliki sangat banyak, termasuk untuk membuat aplikasi serius atau game 3D dan bahkan aplikasi Android. Sayangnya saya tidak suka implementasinya: kode ditranslasikan ke C, alalu dicompile dengan compiler C. Hasilnya: waktu kompilasinya lama, tidak bisa menjalankan program dengan instan seperti QBasic/QuickBasic. Bahkan saat ini versi terbaru dari github yang saya download gagal menjalankan aplikasi hello world.

Just BASIC

Just BASIC ini hanya tersedia di Windows, gratis tapi tidak open source. Kelebihannya dibandingkan solusi yang lain adalah: bisa digunakan untuk membuat aplikasi Windows yang memakai native control di Windows. Just BASIC memakai pendekatan interpreter, dan eksekusinya instan, ketika mengklik RUN, program akan langsung berjalan.

AppGameKit Classic

AppGameKit Classic adalah implementasi BASIC berbayar untuk membuat game. Saya mengenal AppGameKit ini dari Humble Bundle beberapa tahun lalu dan ternyata sampai saat ini masih terus diupdate. Harga penuh aplikasi AppGameKit relatif mahal, tapi sering bisa dibeli murah dengan berbagai diskon dan bundle dari berbagai situs. AppGameKit dapat dipakai untuk membuat game, baik desktop, web, maupun mobile.

Engine utama AppGameKit ini ditulis dalam C++, dan bisa dipanggil langsung dari C++ jika kita mau, tapi mode untuk menjalankan kode BASIC adalah interpretasi. Bahasa BASIC yang didukung adalah generasi 2, jadi kita bisa membuat tipe data sendiri, tapi tidak bisa memakai OOP.

Beberapa tahun lalu ketika mencoba ini, rasanya cukup fun. Cukup mudah membuat game sederhana. Bahkan ada mode sangat bagus untuk membuat game mobile: mode broadcast. Asalkan device Android kita ada di subnet yang sama, kita cukup menjalankan AppGameKit Player di Android, tekan Broadcast di PC, dan akan muncul gamenya di device Androidnya, instan.

SmileBASIC 4

Saya mendengar berita rilis SmileBASIC di Hacker News, jadi ini relatif baru buat saya. SmileBASIC ini juga tersedia untuk game console Nintendo 3DS. Walaupun sulit sekali memprogram dengan layar kecil 3DS, ternyata aplikasi ini cukup populer sehingga mereka membuat versi Nintendo Switchnya.

Bandingkan jumlah memorinya dengan PC XT

Nintendo Switch bisa dihubungkan ke monitor dengan HDMI dan bisa dihubungkan juga ke keyboard dan mouse USB (dan bahkan juga headset USB). Tanpa keyboard USB, kita bisa memprogram dengan keyboard di layar sentuh, tapi ini kurang efisien. Jadi selama saya mencoba SmileBASIC, saya selalu menggunakan keyboard dan mouse dan layar besar (SmileBASIC mendukung sampai dengan resolusi 1280×720).

AppGameKit mudah diprogram, tapi memiliki kelemahan: kita harus mencari sendiri asset (gambar, musik, efek suara) untuk game atau membeli pack terpisah. Sedangkan SmileBASIC memiliki ratusan sprite built in, puluhan music background built in, dan juga puluhan sound effect. Tersedia juga editor bitmap built in jika gambar yang ada tidak seperti yang kita mau.

Tapi SmileBASIC memiliki kelemahan dari segi fitur bahasa BASIC-nya. Meskipun menggunakan BASIC generasi 2 (tanpa line number), tapi SmileBASIC tidak mendukung user defined type.

Penutup

Bahasa BASIC memang sudah tidak terlalu banyak dipakai dibandingkan masa kejayaannya, tapi belajar bahasa ini cukup menyenangkan. Masih ada orang yang bisa mendapatkan uang dari memprogram BASIC. Beberapa game ditulis dalam bahasa BASIC, demikian juga banyak aplikasi sederhana ditulis dalam BASIC. Beberapa aplikasi dalam berbagai bank masih ditulis dengan Visual Basic 6. Meski demikian, jika Anda pemula dan baru mulai belajar pemrograman sebaiknya belajar bahasa lain saja, seperti Python atau PHP.

Belajar bahasa BASIC bisa dipelajari sekedar untuk iseng. Bahasanya sederhana, tidak butuh banyak waktu untuk belajar, tidak seperti belajar Rust, Haskell atau Scala. Bisa juga belajar BASIC supaya bisa belajar membuat interpreter atau compilernya.

Multithreading dan Multiprocessing di Python

Python adalah salah satu bahasa yang sangat saya sukai, tapi bukan berarti bahasa ini tidak memiliki kekurangan, di tulisan ini saya ingin menunjukkan kekurangan Python dalam memproses data kompleks dengan Python murni (pemrosesan tidak dilakukan di modul native). Kompleks di sini maksudnya pemrosesannya CPU bound (batasan programnya adalah kemampuan CPU, bukan memori atau I/O).

Kelebihan python jelas sangat banyak: mudah dipakai, librarynya banyak, sintaksnya bersih dan mudah dibaca. Library Python untuk berbagai hal (terutama AI) sudah sangat banyak dan banyak yang ditulis dalam native code (C/C++). Dengan library eksternal yang ditulis dalam bahasa C/C++ (kebanyakan library AI), kita tidak perlu memikirkan apapun dalam hal pemrosesan data besar dan kompleks, karena itu dilakukan di level native code.

Multithreading dan Global Interpreter Lock

Dalam banyak bahasa (misalnya C atau Java): jika saya punya algoritma yang lambat untuk data besar, dan datanya masih masuk memori, saya biasanya bisa menyelesaikannya dengan thread. Saya hanya perlu mempartisi data, membuat banyak thread dan mengolah tiap potongan data di thread yang berbeda. Partisi data di sini biasanya mudah: jika bentuknya array, tinggal dibagi saja per N element.

Di Python, solusi dengan multithreading tidak akan berjalan baik karena adanya Global Interpreter Lock. Atau tepatnya lagi: bisa jalan, tapi tidak lebih cepat dari single thread. Adanya GIL berarti di kebanyakan kasus beberapa thread di Python tidak bisa jalan berbarengan. Threading di Python hanya berguna untuk proses yang menunggu I/O (akses file, akses socket, dsb). Jadi jika kita punya prosessor 32 core sekalipun, Python tidak bisa optimal memakai semua core tersebut dengan module threading.

Kadang-kadang mengganti interpreter dengan pypy sudah cukup menyelesaikan masalah komputasi yang berat. Pypy adalah implementasi interpreter Python yang memakai teknologi JIT (Just In Time Compiler), jadi lebih efisien walaupun tetap satu thread saja. Tapi Pypy ini tidak selalu berhasil:

  • Pypy tidak 100% kompatibel dengan Python
  • Ada modul-modul yang tidak bisa dipakai di Pypy
  • Versi Pypy ketinggalan dibandingkan versi Python resmi

Solusi lain seperti Cython juga bisa dipakai, tapi lebih rumit dari sekedar mengganti interpreter.

Multiprocessing

Python memiliki modul multiprocessing, artinya kita bisa menjalankan sebuah fungsi di subproses baru. Overhead untuk memulai proses baru cukup besar, jadi ada kita bisa juga memakai process Pool supaya lebih optimal. Di titik ketika kita membuat subprocess, isi memori saat ini di proses utama akan di-clone ke memori yang sifatnya copy-on-write (COW). Artinya: selama memori tersebut tidak diubah, maka tidak akan dibuat salinannya, hanya akan dishare (dipakai bersama).

Lalu bagaimana kita bisa memanggil fungsi di proses baru dan mendapatkan hasilnya di proses utama? jawabannya adalah: setiap parameter akan di-serialize dan dikirim ke subproses baru. Hasil kembalian fungsi diserialize oleh subproses dan dikembalikan ke proses utama. Ini cukup untuk menyelesaikan banyak masalah. Tapi pendekatan ini tidak sempurna. Proses serialization dilakukan menggunakan modul pickle. Ini titik yang harus diwaspadai, perhatikan contoh kecil ini di mana kita memproses XML dengan minidom.

from xml.dom import minidom
import pickle

myxml = """<?xml version="1.0"?>
<test>
	<item id="11">
		<name>red thing </name>
		<price>100</price>
	</item>
	<item id="12">
		<name>blue thing</name>
		<price>200</price>
	</item>
</test>
"""

doc = minidom.parseString(myxml)

items = doc.getElementsByTagName("item")

firstitem = items[0]

print(firstitem.toxml())

with open("test.bin", "wb") as f:
    f.write(pickle.dumps(firstitem))

Hasil dari pickle bisa dilihat di file test.bin dan jika dilihat dengan editor akan terlihat bahwa ada ‘blue thing’, padahal tidak ada di firstitem. Modul pickle akan melihat semua atribut yang dimiliki oleh sebuah node dan mempickle semuanya, dalam hal ini: parent-nya juga, dan karena parent memiliki semua child, maka semua child-nya juga ikut.

Jika kita menggunakan modul multiprocessing dan mengirimkan satu node ke subprocess, maka yang niatnya hanya mengirimkan satu node, ternyata mengirimkan seluruh DOM ke subprocess. Secara umum, ketika mengirimkan objek apapun, maka kita mulai harus memikirkan: ketika serialisasi dilakukan, apa saja yang akan dibawa oleh objek tersebut.

Objek yang diserialize ini tidak masuk ke memori COW, tapi Python akan mengalokasikan memori baru. Bayangkan jika kita mengirimkan copy seluruh DOM ke semua subprocess (misalnya ada 10 subprocess), hasilnya: penggunaan memori jadi sangat besar. Jika DOM-nya besar, proses serialisasi dan deserialisasi ini juga sangat lambat.

Dalam kasus tertentu, cara yang lebih efisien adalah: kita letakkan apa yang ingin kita proses di objek global, objek global ini akan di-clone ke proses baru (tidak melalui serialization). Ketika kita memanggil subprocess, kita hanya mengirimkan string atau bilangan yang berupa ID. Subprocess kemudian bisa mengakses variabel global berdasarkan ID tersebut. Cara ini efisien dalam mentransfer data dari proses utama ke subprocess, tapi tidak bisa dilakukan untuk sebaliknya. Ketika mengembalikan hasil proses, kita tetap harus memikirkan objek yang kita kembalikan. Untuk nilai kembalian ini, tidak ada trik yang mudah, kita harus berhati-hati agar serialisasi tidak berjalan liar.

Modul multiprocessing juga memiliki beberapa kelas khusus supaya kita bisa mentransfer data dengan lebih efisien antar proses (Queue, Pipe, Manager), tapi penggunaanna cukup rumit dan tidak intuitif, dan rawan salah. Ini juga tidak menyelesaikan masalah serialisasi data kompleks.

Mungkin sebagian orang akan bilang: ah kan gampang, cukup kita lihat setiap kelas yang kita miliki dan cek satu persatu membernya supaya jelas apa yang diserialisasi. Ada beberapa masalah:

  • Kode kadang ditulis anggota team lain, dan tidak jelas tipe data sebuah variabel member, lebih sulit lagi jika ada list atau dictionary yang tipe datanya tidak jelas
  • Kadang kita memakai library orang lain (contoh kecil: XML, Graph), yang tidak mudah untuk dimodifikasi. Andaikan bisa dimodifikasi: kita harus memaintain library itu jika ada versi baru

Untuk masalah pertama: Python saat ini memiliki type hint, kita bisa memberikan hint apa tipe sebuah variabel/member. Tipe data tidak akan dicek oleh interpreter python, tapi IDE yang kita pakai bisa membantu mengecek tipe data. Untuk masalah kedua: ini lebih sulit, kita bisa menulis serializer/pickler khusus untuk object tertentu (bisa dibaca di dokumentasi Python), atau menggunakan setstate__ dan __getstate untuk memproses secara khusus member variable tertentu.

Solusi Third Party

Dulu orang menyarankan: pakai saja Jython (implementasi Python dalam Java) atau IronPython (implementasi Python dalam .NET), keduanya tidak memakai Global Interpreter Lock, jadi thread bisa berjalan cepat. Tapi masalah dengan solusi dari pihak lain adalah: maintenance. Sekarang ini baik Jython maupun IronPython tidak dimaintain lagi. Belum lagi dukungan modul yang kurang lengkap.

Saat ini ada beberapa pendekatan lain untuk memudahkan masalah multithread/multiprocess, misalnya Ray, tapi ini masih kurang populer dan kode kita harus diubah untuk mengikuti model komputasi Ray, library ini pun punya masalahnya sendiri. Tentunya saya tetap khawatir dengan library apa saja dari 3rd party bahwa proyek semacam ini tidak bertahan lama.

Data Ukuran Sedang

Masalah yang saya contohkan di atas itu adalah kasus yang sudah saya temui beberapa kali. Jika dari awal diketahui bahwa data akan sangat besar, saya akan memecah data menjadi banyak bagian, lalu membuat proses khusus yang bisa memproses data yang sudah dipecah, atau bahkan menggunakan sistem terdistribusi. Jika data sangat kecil, saya juga tidak akan menggunakan threading sama sekali, cukup satu proses.

Perlu dicatat bahwa jika kita terlalu buru-buru berusaha memakai sistem terdistribusi untuk data yang masih muat di memori, performancenya justru tidak bagus (belum lagi biaya ekstra yang dikeluarkan untuk banyak server). Artikel tahun 2014 ini merupakan contoh yang bagus bahwa kadang command line tools bisa 235 kali lebih cepat dari Hadoop. Ini kasus yang sangat ekstrem, tapi pelajaran yang bisa dipetik adalah: kalau datanya kurang besar, overhead memakai Hadoop atau sistem lain kadang sangat besar.

Contoh yang biasa saya temui adalah: ada program yang hanya memproses sebuah file, biasanya ukurannya puluhan megabyte, tapi kemudian filenya bertumbuh jadi beberapa gigabyte, nilai ini besar (beberapa puluh kali lebih besar dari input biasanya) tapi masih jauh di bawah memori yang tersedia (misalnya server dengan memori 16-256 GB). Program yang tadinya butuh beberapa detik menjadi beberapa menit atau bahkan puluhan menit, sedangkan ketika diubah menjadi multiprocess, tiba-tiba pemakaian memori Python menjadi beberapa puluh gigabyte (karena masalah serialization) dan malah lebih lambat lagi. Memperbaiki skrip seperti ini bisa butuh waktu yang cukup lama.

Secara umum saya tidak suka dengan pendekatan multiprocessing ini: saya memakai bahasa yang managemen memorinya otomatis (contoh: java, C#, Python, Javascript) karena ingin memori diatur otomatis oleh sistem. Kalau memang ingin mengatur memori manual sendiri, lebih baik saya memakai C/C++ sekalian.

Penutup

Python tetap merupakan salah satu bahasa yang saya sukai dan saya tetap akan memakainya untuk berbagai hal. Posting ini hanya untuk menunjukkan bahwa Python juga memiliki keterbatasan. Dengan memahami batasan ini, kita bisa mempertimbangkan:menggunakan pendekatan lain dalam memproses data dengan Python atau memakai bahasa lain yang lebih cocok untuk masalah tersebut.

Perlu dicatat juga bahwa tiap bahasa bisa berubah, baik sintaksnya maupun implementasinya. Contohnya: NodeJS baru punya Worker Thread di Node JS 10 sebagai fitur experimental dan harus diaktifkan dengan flag khusus. Baru pada Node JS 12 yang baru dirilis April tahun 2019 API Worker Thread dianggap stabil dan layak dipakai produksi. Jadi mungkin saja suatu saat masalah multithread ini akan berubah di Python.

Mengenal Bahasa Pemrograman Lua

Di kesempatan kali ini saya akan memperkenalkan bahasa Lua. Bahasa ini sebenarnya sudah ada cukup lama (sejak 1993), tapi kurang terkenal secara umum. Lua lebih sering dijadikan bahasa scripting untuk aplikasi dan sering kali nama Lua tidak disebut sama sekali. Lua artinya “Bulan” (moon) dalam bahasa portugis. Lua diciptakan oleh Roberto Ierusalimschy, Luiz Henrique de Figueiredo, dan Waldemar Celes, anggota dari Computer Graphics Technology Group (Tecgraf) di Pontifical Catholic University of Rio de Janeiro, di Brazil.

Lua bisa dipakai untuk membuat program/aplikasi seperti bahasa lain, tapi kelebihan bahasa ini  adalah dari awal dirancang untuk diembed dalam sebuah aplikasi lain (terutama dalam bahasa sejenis C). Di-embed ini artinya bisa  bisa dipakai untuk sistem plugin atau bisa dipakai untuk mengontrol bagian business/game/app logic sementara bagian yang butuh performance tinggi memakai C atau bahasa lain. Lua juga bisa jadi file konfigurasi yang sangat fleksibel.

Lua dipakai di banyak software misalnya Adobe Lightroom, Nmap, Wireshark, dan lighttpd. Seperti saya sebutkan di awal, sering kali berbagai aplikasi menyebutkan bahasa Lua ini hanya sekilas, dan fitur aplikasinya yang lebih ditekankan dengan nama lain, misalnya dokumentasi Nmap menyebutkan tentang Nmap Scripting Engine (NSE) tapi Lua hanya disebut sekilas di tengah. Lua juga banyak dipakai di berbagai game (misalnya World Of Warcraft, Angry Birds) dan game engine. Interpreter Lua ukurannya sangat kecil sehingga bisa dipakai untuk embedded system/IOT (misalnya NodeMCU dan eLua).

Bahasa Lua

Bahasa Lua menurut saya relatif sederhana, tapi juga sangat fleksibel. Sintaksnya tidak aneh, sedikit mirip BASIC atau pascal. Contohnya seperti ini:

print "hello, berapa umur kamu?"
umur = io.read("*n")
if umur >= 17 then
   print("Halo kakak")
else
   print("Halo adik")
end

Bagi sebagian orang mungkin ini agak aneh mengingat bahasa sekarang sudah tidak lagi memakai kata-kata (“then”, “end”) tapi lebih memakai simbol ({} di C/C++/Java/.NET, indentasi di Python), tapi untuk yang dulu memakai Pascal dan BASIC ini terasa cukup wajar. Sebagian besar syntax Lua mirip dengan bahasa lain, tapi ada beberapa hal yang agak aneh. Di Lua, operator kurang dari, lebih besar, sama dengan, semuanya sama dengan bahasa lain (<, >, =) tapi untuk tidak sama dengan, syntax yang dipakai adalah ~=. Syntax untuk komentar juga agak kurang standar, memakai “–” untuk satu baris, dan memakai –[[ untuk komentar multi baris –]].

Secara umum bahasa Lua cukup mudah dibaca bahkan oleh pemula, tidak seperti bahasa Forth (yang saya bahas sebelumnya) atau bahasa fungsional seperti Scala.

Tipe primitif yang didukung Lua adalah: string, number, dan integer. Struktur data utama di Lua adalah table. Secara umum: tabel memetakan sesuatu ke sesuatu yang lain (seperti Map/Dictionary di bahasa lain). Hampir semua hal di Lua diimplementasikan dengan table. Sebuah array di Lua juga adalah tabel yang memetakan indeks dengan sebuah nilai, dengan nilai index awal default adalah 1. Masalah index ini agak mengganggu bagi saya, karena di bahasa lain biasanya array berbasis 0. Sebuah tabel bisa berisi tabel lain untuk merepresentasikan array multi dimensi.

Sebuah struktur (struct) juga bisa direpresentasikan dalam bentuk tabel (memetakan nama field sebagai string ke nilainya). Di Lua, sebuah fungsi juga bisa masuk ke dalam tabel. Contohnya kita bisa saja membuat tabel yang berisi berbagai operasi matriks yang berisi pemetaan nama operasi, misalnya “multiply” dengan implementasi fungsi perkalian matriks.

Lua juga memiliki konsep metatable, yang bisa digunakan untuk mendefinisikan operasi terhadap sebuah variabel. Dengan setmetatable(variabel, sebuahtabel), kita bisa mendefinisikan berbagai operator dan fungsi yang berlaku untuk variabel tersebut. Jadi jika kita ingin membuat sebuah variabel menjadi sebuah tipe matriks, kita bisa membuatnya dengan setmetatable(variabel1, tabel_operasi_matriks).

Lua tidak memiliki konsep OOP tapi dengan menggunakan berbagai trik table, metatable dan fleksibilitas function di Lua, kita bisa mengimplementasikan Object Oriented Programming. Saya sendiri kurang menyukai ini, kalau saya ingin memprogram dengan paradigma OO, saya akan memilih bahasa lain.

Fitur Lua lain yang menarik adalah coroutine, generalisasi dari subrutin, tapi fitur ini jarang saya pakai. Sebenarnya fitur ini powerful, tapi ketika melakukan embeding Lua ke bahasa lain (terutama C), fitur ini cukup merepotkan karena tidak mudah dipetakan ke C.

Embeding Lua ke bahasa C

Seperti sudah saya sebutkan di atas, fitur Lua adalah sangat cocok untuk diembed ke sebuah aplikasi. Sebenarnya bahasa lain seperti Python dan Ruby juga bisa diembed ke bahasa lain (misalnya C), tapi ada banyak kelebihan Lua:

  • Ukurannya sangat kecil, ratusan kilobyte. Python butuh minimal beberapa megabyte (disk space dan memori)
  • Mendukung multi thread (satu thread menjalankan instance Lua terpisah). Untuk embedding multi thread di Python sangat sulit, dan Python memiliki Global Interpreter Lock
  • Interfacing sangat mudah, baik coding manual atau dengan bantuan SWIG

Implementasi utama Lua adalah dalam bahasa C, walau ada juga Lua untuk Java. Kebanyakan bahasa memiliki build system yang kompleks dan butuh banyak library tambahan. Dalam kasus Lua: kita cuma perlu copy paste semua file .c (kecuali lua.c) ke project kita dan itu sudah cukup. Sebagai catatan: selain implementasi utama Lua, ada juga LuaJIT yang sangat cepat namun memiliki banyak batasan terutama di sistem 64 bit.

Setelah memasukkan semua file C dalam project kita, di bagian program yang butuh Lua, kita perlu menginclude beberapa file:

               
#include "lua.h"       
#include "lualib.h"       
#include "lauxlib.h"

Lalu kita bisa menginstansiasi interpreter Lua. Jika diperlukan, kita bisa membuat banyak instance (misalnya satu di setiap thread). Setelah itu biasanya kita ingin agar library standar Lua diaktifkan, jadi kita memakai luaL_openlibs.

lua_State *L = luaL_newstate(); 
luaL_openlibs(L);

Biasanya skrip Lua akan diletakkan di luar program, supaya bisa diubah dengan mudah tanpa rekompilasi. Untuk meload dari file, kita bisa memakai luaL_loadfile atau luaL_loadstring jika ingin memakai string. Di banyak Game, file lua dienkrip di file (agar tidak mudah dimodifikasi), lalu didekrip dimemor, dan diload menggunakan luaL_loadstring.

 
int err = luaL_loadfile(L, "myscript.lua");
if (err) {
       fprintf(stderr, "Failed loading file: %s\n", lua_tostring(L, -1));
       exit(1);
}

Dalam passing parameter ke Lua dan dari Lua, kita menggunakan konsep stack. Kita bisa memasukkan nilai ke stack dengan lua_pushstring/lua_pushnumber/lua_pushinteger, dan bisa mengambil nilai di stack dengan lua_tointeger, lua_tostring, lua_tonumber dan beberapa fungsi lain. Jadi ketika memanggil fungsi di Lua: kita melakukan push value ke stack, dan untuk mengambil nilai dari lua kita akses hasilnya di stack juga.

Kita bisa mencari fungsi yang diimplementasikan di Lua dengan lua_getglobal(L, "namafungsi"), ini akan mempush reference fungsi ke stack. Setelah itu kita bisa mempush nilai parameter untuk fungsi, memanggil fungsinya, dan mendapatkan hasilnya di stack.

 
lua_getglobal(L, "myfunction"); 
lua_pushinteger(L, 1); //param 1
lua_pushinteger(L, 2);  //param 2
int err = lua_pcall(L, 
    2 /*number of arguments*/, 
    1 /*number of result*/, 
    0 /*indeks ke error handler, jika 0 maka error akan dipush ke stack*/);

Membuat fungsi di C yang bisa dipanggil oleh Lua juga sangat mudah. Misalnya kita ingin membuat fungsi max yang menerima 2 double dari Lua dan mengembalikan angka terbesar, maka kita cukup membuat ini:

 
int max_for_lua(lua_State *L) 
{
  double num1 = lua_tointeger(L, 1); //param 1 dari Lua
  double num2 = lua_tointeger(L, 2); //param 2 dari Lua

  double max = num1>num2?num1:num2; //logika utama

  lua_pushnumber(L, max); //push ke stack Lua
  return 1; //hanya ada satu nilai kembalian
}

Tentunya keberadaan fungsi ini perlu diberitahukan ke Lua agar bisa diakses oleh skrip Lua. Nama fungsi di C dan Lua bisa berbeda, contohnya fungsi max_for_lua di atas bisa didaftarkan menjadi “my_max” seperti ini:

 
lua_pushcfunction(lua_state, max_for_lua);
lua_setglobal(lua_state, "my_max");

Penutup

Sering kali saya menemukan di mana saya butuh eksperimen cepat dan tidak ingin sering mengcompile ulang kode program saya. Contoh penggunaan yang baru-baru ini saya lakukan adalah ketika eksplorasi fitur tertentu di iOS. Jika saya mengandalkan objective C atau Swift saja, tiap kali saya mengubah kode (biasanya hanya logic tertentu), akan butuh beberapa detik sampai beberapa puluh detik sampai bisa menjalankan ulang aplikasinya. Walau hanya menekan tombol “run” di XCode yang terjadi di latar belakang adalah: kodenya harus dicompile, dilink, ditandangani secara digital, dicopy ke device, lalu dijalankan. Dengan memindahkan logic ke Lua, saya hanya perlu me-mount direktori dokumen aplikasi dengan ifuse, mengedit file Lua, lalu merestart aplikasinya.

Menambahkan sebuah scripting ke language ke aplikasi memang butuh waktu tapi di sisi lain juga sangat fleksibel karena skrip bisa dimodifikasi dengan cepat dan tidak seperti kode C, tidak perlu dikompilasi ulang. Jika Anda sudah beberapa kali menambahkan fitur scripting ke sebuah aplikasi, menambahkan ke aplikasi berikutnya tidak butuh waktu lama. Untuk aplikasi kompleks kita bisa memakai Simplified Wrapper and Interface Generator (SWIG) untuk mempercepat membuat interface dari C/C++ ke Lua.

Semoga tulisan singkat ini cukup untuk memperkenalkan bahasa Lua dan mungkin juga membuat Anda tertarik untuk menambahkan Lua ke aplikasi yang Anda buat.

Dari keyboard ditekan sampai muncul karakter di layar (bagian 3)

Di bagian sebelumnya sudah dibahas mengenai bagaimana layout text harus dilakukan. Setelah semuanya selesai, maka teks bisa ditampilkan ke layar. Di level aplikasi biasanya kita hanya perlu memanggil semua fungsi dasar untuk menampilkan teks. Di sisi library dan sistem operasi, masih ada langkah ekstra yang harus dilakukan. Teks perlu digambar ke sebuah buffer dan buffer perlu ditampilkan di sebuah permukaan gambar. Driver grafik akan menampilkan buffer tersebut ke layar.

Untuk aplikasi desktop, proses menampilkan teks ini di sisi programmer semudah memanggil fungsi print (mode teks) atau mengeset property tertenty. Jika kita memprogram microcontroller atau embedded system, akan lebih terasa proses berat yang harus dilakukan ketika menampilkan sesuatu. Contoh paling sederhana adalah 7 segment display.

Programmer perlu menyala matikan tiap garis dalam 7 segment display untuk menampilkan angka tertentu.

Display saat ini biasanya menggunakan akses per piksel. Mungkin yang paling sederhana adalah 8×8 dot matrix. Di sini kita perlu mengontrol tiap titik untuk menampilkan huruf/angka/bentuk yang kita mau.

Semakin canggih hardware, biasanya pekerjaan programmer high level akan semakin mudah. Contohnya adalah display LCD jenis seperti ini:

Jpeg

Demikian seri singkat kali ini. Saya akan berusaha membuat seri baru yang membahas dari segi yang lebih low level lagi.

Dari keyboard ditekan sampai muncul karakter di layar (bagian 2)

Meneruskan dari tulisan sebelumnya, sekarang kita ke bagian aplikasi. Pertama aplikasi perlu menerima “event” dari Desktop/Window environment. Event ini bermacam-macam, misalnya permintaan untuk meminimize Window, event gerakan mouse, dan event tombol keyboard ditekan.

Setelah tombol ditekan, maka aplikasi akan melakukan update pada struktur datanya. Sebagai pengingat, contoh aplikasi yang saya berikan ada Word Processor. Ini bergantung dari mode saat ini (apakah mode insert atau overwrite, apakah dokumen sifatnya read only dsb). Struktur data yang digunakan pun bisa sangat beragam, tergantung dari fitur yang dimiliki aplikasi.

Dalam sebuah word processor, berbagai algoritma perlu dijalankan sebelum tampilan diupdate di layar. Beberapa algoritmanya misalnya: hyphenation (pemotongan kata), pemeriksa ejaan (dalam kasus tertentu biasanya nanti kata tersebut perlu digaris bawahi merah), perhitungan jumlah kata.

Ketika data di memori perlu ditampilkan di layar, maka ada banyak langkah lagi yang harus dilakukan. Mulai dari pencarian font, layout setiap karakter, dsb.

Sebuah font perlu diload sebelum bisa ditampilkan di layar. Font jaman dulu merupakan font bitmap biasa, tapi font sekarang biasanya merupakan true type font yang memiliki banyak informasi untuk tiap glyph dalam font tersebut. Jika kita memiliki satu kata yang ingin ditampilkan, maka ada kemungkinan:

  • Satu huruf dipetakan ke satu glyph
  • Satu huruf dipetakan ke lebih dari satu glyph, contohnya jika ada diacritic
  • Dua huruf atau lebih dipetakan menjadi satu glyph (contohnya jika ada ligature)

Jika dokumen terdiri dari lebih dari satu bahasa maka hal ini menjadi lebih kompleks lagi, ada bahasa yang ditulis dari kiri ke kanan dan dari kanan ke kiri. Berbagai hal ini biasanya dilakukan dalam tahap yang dinamakan “text-shaping”. Setelah semuanya siap, maka teks bisa ditampilkan di layar.

Bagian berikutnya akan membahas bagaimana teks ditampilkan di layar.

Dari keyboard ditekan sampai muncul karakter di layar (bagian 1)

Seri tulisan ini akan mencoba menjelaskan apa yang terjadi dari sejak kita menekan tombol di keyboard sampai muncul huruf di layar. Untuk mempersingkat, saya akan mengambil beberapa asumsi:

  • Keyboard ditekan di sebuah aplikasi yang terbuka (jadi sudah selesai booting)
  • Sistem operasi yang dipakai modern (bukan DOS, tapi Windows/Linux/OS X)
  • Aplikasi yang akan saya contohkan adalah sebuah word processor (supaya sederhana, rencananya saya akan menulis dari mengetik di address bar sampai muncul web page)

Keyboard

Ketika tombol di tekan di keyboard, sebuah saklar akan aktif. Ada banyak jenis saklar ini (bisa dibaca di wikipedia) tapi intinya sebuah sirkuit akan tertutup ketika tombol ditekan. Karena jumlah tombol ada banyak, maka digunakan sirkuit matriks pada keyboard untuk mengurangi jumlah koneksi.

Keyboard tidak tahu apa yang tertulis di atas sebuah tombol. Tombol pertama di keyboard QWERTY adalah huruf ‘Q’, tapi di keyboard AZERTY menjadi ‘A’.  Keyboard hanya akan mengirimkan kode sebuah tombol, nanti urusan setting di sistem operasi yang akan menginterpretasikan tombol itu menjadi suatu huruf tertentu.

Sebuah keyboard memiliki prosessor, sebuah chip khusus atau microcontroller yang akan melakukan scanning pada matrix keyboard. Di keyboard lama (PS/2), jika ada tombol ditekan maka akan mengirimkan sebuah interrupt ke PC. Di keyboard USB, PC akan melakukan polling (biasanya 150Hz), artinya dalam 1 detik komputer akan bertanya 150 kali pada keyboard: apakah ada tombol yang sedang ditekan?

Jika ingin mengenal bagian ini lebih dalam, kita bisa membeli Arduino, dan keypad matrix sederhana seperti dalam gambar berikut ini.

Atau seperti ini yang lebih jelas terlihat koneksinya

Kita juga bisa membeli keycap satuan untuk keyboard mekanis, dan bahkan merangkai sendiri circuit boardnya. Microcontroller yang digunakan dalam proyek DIY seperti ini biasanya Atmega32u yang mudah diprogram.

Jika ingin membuat keyboard custom sendiri juga tidak sulit. Misalnya kita bisa membuat keyboard yang mengetik alamat email jika satu tombol ditekan. Atau mungkin kita tidak ingin memakai tombol tapi pedal.

Sistem Operasi

Input dari keyboard akan diterima oleh sistem operasi. Untuk keyboard USB, driver HID (human interface device) akan menerima data dari keyboard. Di titik ini, setiap sistem operasi memiliki jalurnya sendiri dalam menangani keyboard.

Di Linux/Unix, jika kita memakai XWindow, maka X Server akan membaca input dari /dev/input/* dan meneruskan eventnya ke berbagai Window yang muncul. Di Windows driver akan mengirimkan message ke System Message Queue. 

Shell/Desktop

Ketika sebuah tombol ditekan, maka ada Desktop manager yang akan mengatur: saat ini aplikasi yang aktif yang mana? mana yang menerima input keyboard? kadang ada lebih dari satu aplikasi yang menerima input keyboard,  ada aplikasi yang mendaftarkan diri sebagai shortcut handler (misalnya ketika tombol print screen ditekan, maka isi layar akan disimpan ke DropBox).

Berikutnya …

Di sebuah aplikasi akan ada event handler yang menangani input dari keyboard. Ini akan saya bahas di bagian berikutnya.

Membaca Source Code

Ada beberapa pertanyaan yang ditujukan ke saya yang jawabannya mudah jika sang penanya bisa membaca source code. Sayangnya skill membaca source code ini sering kali tidak dimiliki, padahal sangat diperlukan oleh programmer dan juga para reverse engineer.

Saat ini mungkin sekitar 90% pertanyaan programming bisa dicari jawabannya dengan Google, dan biasanya jawabannya akan ditemukan di situs stack overflow,  di blog seseorang, dan kadang di beberapa situs yang memang membahas topik khusus yang dicari.

Tentunya jawaban-jawaban tersebut bisa ditemukan kalau Anda bisa berbahasa Inggris. Minimal tahu beberapa kata kunci untuk mencari, dan sisanya adalah kemampuan membaca teks berbahasa Inggris. Segala macam pertanyaan sederhana sudah ada yang menanyakan, misalnya “how to split string in <java/c/javascript/..>”. Sering kali jawabannya disertai source code fungsinya, misalnya untuk pertanyaan cara split string di C yang tidak ada di library standar.

Selain berbagai jawaban di stack overflow, dan artikel di berbagai situs, ada juga berbagai buku dan dokumen yang bisa dibaca untuk mencari jawaban. Tapi ada satu sumber yang paling akurat untuk pertanyaan programming: source code. Kadang informasi dari dokumentasi bisa salah atau belum diupdate, tapi informasi dari source code tidak akan salah.

Saat ini sebagian besar software yang dipakai sudah open source. Berbagai sistem operasi sourcenya terbuka (Linux, FreeBSD, OS X, dan masih banyak lagi), compiler dan interpreter (gcc, llvm, go, php, python, dsb), editor text biasa ( emacs, notepad++, vi, dsb) atau bahkan IDE (IntelliJ, Eclipse, Visual Studio Code, dsb). Berbagai game, editor audio, konverter video, dsb semuanya ada versi open sourcenya. Berbagai library sudah ada untuk melakukan hampir apa saja, dari mulai decoding format file sampai  mengenali wajah dan suara.

Tidak semua software memiliki dokumentasi yang lengkap. Contohnya: meskipun jutaan orang memakai OS X, dan kernel OS X ini open source, sedikit sekali dokumentasi dan buku mengenai internal kernel OS X. Jika untuk software yang sangat populer saja dokumentasinya tidak banyak, tentunya masih ada ratusan ribu software lain yang dokumentasinya lebih sedikit lagi.

Dokumentasi sebuah fungsi di sebuah bahasa juga sering kali tidak menyebut detail implementasi, yang kadang penting dari sisi security, misalnya dokumentasi mt_rand dan mt_srand di PHP tidak menyebutkan bagaimana proses seeding dilakukan jika tidak dinyatakan secara eksplisit. Tentunya ini bisa ditanyakan ke stack overflow, dan dalam kasus ini ada yang menjawab untuk versi PHP yang spesifik (dan dia mendapatkan jawabannya dari source code).

Dari sisi security, kita bisa menemukan kelemahan dari membaca source code. Dalam kasus mt_rand, sudah pernah ada dua bug ditemukan (tahun 2008 dan 2011). Bug di software yang menggunakan PHP sehubungan dengan penggunaan mt_rand juga masih sering ditemukan (misalnya salah satu yang relatif baru  di Oktober 2017).

Membaca source code tanpa alasan yang kuat memang jarang dilakukan orang. Tapi ketika terpaksa, sebaiknya kita mampu membaca kode orang lain. Sering kali kita tidak perlu membaca dalam, biasanya hanya sekedar permukaan saja atau langsung spesifik ke bagian tertentu, tergantung kebutuhan. Membaca source code besar tidak bisa seperti membaca novel dari halaman pertama ke terakhir. Membaca source code besar  harus memakai berbagai pendekatan.

Pendekatan pertama adalah skimming, sekedar melihat berbagai file yang ada, lalu membuka file tersebut. Cek apakah ada komentarnya. Hal paling penting adalah mengetahui apakah file tersebut dipakai atau tidak. Sebagian file ternyata tidak dipakai, sebagian ternyata hanya dikompilasi jika fitur tertentu diaktifkan (sedangkan defaultnya fitur tersebut tidak aktif).

Hal penting lain yang bisa didapat dari sekedar skimming adalah: library apa yang dipakai oleh program ini atau program eksternal apa yang dipanggil oleh program ini? Kadang ternyata source code yang kita temukan tidak sesuai harapan karena ternyata hanya membungkus library atau command line lain.

Pendekatan berikutnya adalah menjalankan program tersebut. Ini bisa langsung dilakukan jika program tidak perlu dicompile dan tidak butuh server tertentu (contoh: sebagian program butuh server database). Jika program perlu dicompile, langkah ini juga memberikan banyak informasi. Kita jadi tahu file apa saja yang ternyata dicompile, jadi tahu library apa yang dilink, dsb.

Jika sudah bisa menjalankan program, akan lebih baik lagi jika bisa mendebug program. Beberapa program memang sangat sulit didebug, contohnya kernel Linux di Android butuh setup yang tidak mudah, plus perlu belajar memakai GDB. Tapi jika debugger bisa dipakai, maka ini akan memudahkan pemahaman program. Jika debugger tidak bisa dipakai, maka cara lain adalah menggunakan “print” debugging.

Sebagai catatan: berbagai langkah bisa dilakukan bersamaan. Mengkompilasi source code kadang butuh waktu lama (berjam-jam) atau mendownload requirement (berbagai library yang dibutuhkan) butuh waktu berjam-jam. Sementara kompilasi dilakkan,  kode bisa dibaca. Kalau kodenya terlalu besar untuk didownload (contoh: ukuran source code Qt ukurannya ratusan megabyte) bisa source code versi online yang dibaca (misalnya di github).

Sebuah IDE akan sangat membantu untuk membaca program: mencari tahu di mana sebuah fungsi didefinisikan, dari mana saja fungsi dipanggil bisa dialakukan dengan mudah. Untuk memahami program sangat besar (misalnya kernel Linux) biasanya sebuah IDE kurang bisa dipakai (terlalu lambat). Sebagai alternatif,  program cross reference (misalnya LXR) bisa dipakai.  

Jika program memiliki unit test, maka test ini juga bisa menjadi cara untuk memahami fungsi tertentu. Unit test hanya memanggil satu modul program saja untuk menguji kebenarannya, jadi kita bisa mengerti satu bagian kecil program tersebut.

Perhatikan juga bahwa kadang aplikasi yang populer sudah sangat kompleks karena memiliki banyak optimasi. Contohnya jika hanya ingin memahami membuat web server, maka bisa diawali dengan membaca source code sederhana, lalu kemudian bisa diteruskan dengan membaca yang lebih rumit. Jika Anda belum paham dasar web server, dan langsung membaca source code Apache, maka kemungkinan besar Anda akan bingung.

Beberapa hal butuh dasar teori yang baik, contohnya: memahami source code library kompresi butuh dasar teori mengenai kompresi. Memahami implementasi encoder/decoder MP3 juga akan mustahil jika tidak punya dasar teori mengenai pendengaran manusia (dan banyak konsep matematika).

Hal yang paling penting ketika membaca source code adalah: motivasi. Tanpa motivasi tertentu, membaca source code memang sangat membosankan. Motivasi membaca source code ini bisa banyak, dan tergantung masing-masing orang. Beberapa contoh hal yang pernah memotivasi saya:

  • Ada konfigurasi yang tidak jalan, dan saya tidak mengerti kenapa, sedangkan dokumentasinya kurang jelas
  • Saya ingin memahami bug tertentu (misalnya ini)
  • Saya perlu tahu implementasi algoritma tertentu
  • Saya perlu mencari bug untuk tujuan pentesting. Misalnya ada software lama tapi tidak menemukan eksploitnya di web

Semoga semua tips di atas bisa membantu Anda mencoba membaca source code orang lain.