PPL 2020 — Document Builder: Performa Sebuah Kode

Firman Hadi
pepeel
Published in
6 min readApr 13, 2020
Sumber: https://hipwallpaper.com/gotta-go-fast-wallpapers/

Mungkin Anda pernah membuat aplikasi tanpa memperhatikan performanya, atau mungkin sampai sekarang masih seperti itu? Sebenarnya tidak masalah apabila Anda membuat suatu aplikasi skala kecil atau untuk main-main. Namun hal tersebut menjadi hal yang sangat serius bagi para pengembang aplikasi yang digunakan oleh ratusan ribu atau bahkan jutaan orang.

Pentingnya Performa

Contoh hasil pencarian di Google.

Gambar di atas adalah hasil pencarian saya dengan kata kunci ‘indonesia’ di Google. Apa maksud dari teks yang dilingkari merah tersebut? Google berhasil mencari sekitar tiga milyar hasil dokumen dengan kata kunci ‘indonesia’ selama 1 detik. Lalu kenapa? Perhatikan bahwa hasil tersebut sangatlah cepat! Google juga ‘harus bisa’ memberikan hasil secepat dan seakurat mungkin agar kita sebagai pengguna memiliki pengalaman yang baik. Bayangkan apabila pencarian untuk ‘indonesia’ saja memakan waktu 10–20 detik, mungkin Anda sudah lupa apa yang ingin Anda cari atau sudah pindah ke search engine lain.

Cerita di atas merupakan salah satu bukti bahwa performa sangat penting untuk sebuah aplikasi. Tentu saja, mencapai performa tersebut tidaklah mudah. Perlu diketahui juga, performa sebuah aplikasi tidak akan selalu sama. Semakin banyak pengguna aplikasi kita, bisa saja load time aplikasi kita melambat.

Contoh Kasus: Menampilkan Data dari Database

Perhatian: Untuk mempermudah, contoh kasus berikut akan menggunakan bahasa Python dan framework Django, namun sebenarnya hal ini juga berlaku untuk bahasa maupun framework lain.

Misalkan, Anda mempunyai sebuah database person yang berisi first_name, last_name, dan instrument yang masing-masing menunjukkan nama depan, nama belakang, dan instrumen musik yang dimainkan. Karena sudah banyak yang menggunakan aplikasi Anda, entry pada database tersebut sekarang sudah sebanyak 400.000 baris.

Anda kemudian mempunyai endpoint get/ yang mengembalikan 50 data pertama. Untuk itu, Anda memanfaatkan ORM Django untuk mengambil semua data, lalu mengembalikan 50 data pertama saja.

def get_limited(request):
persons = list(Person.objects.all())
result = []
for i in range(50):
person = persons[i]
result.append({
'first_name': person.first_name,
'last_name': person.last_name,
'instrument': person.instrument
})
return JsonResponse(result, safe=False)

Pada awal aplikasi tersebut dikembangkan, kode yang Anda buat seperti di atas mempunyai performa yang cukup cepat. Seiring waktu berjalan, Anda mulai mendapatkan komplain dari pengguna aplikasi Anda bahwa aplikasi Anda sangat lambat. Anda pun bingung karena benar bahwa aplikasi Anda lambat saat diakses melalui browser.

Analisis menggunakan Apache Benchmark (ab)

Apache Benchmark (atau perintah ab) merupakan aplikasi benchmark yang biasanya digunakan untuk stress testing suatu server. Apache Benchmark bekerja dengan mengirimkan beberapa request sekaligus dengan concurrency yang disesuaikan kemudian akan ditampilkan hasil analisisnya berupa connection time maupun besaran bytes yang ditransfer. Apache Benchmark biasanya sudah sepaket dalam package bernama apache2-utils. Anda dapat mencari di internet tentang cara install Apache Benchmark di sistem operasi Anda.

Perhatian: Berhati-hatilah dalam menggunakan aplikasi tersebut, melakukan request terlalu banyak terhadap server dapat menyebabkan denial of service dan hanya gunakan untuk server yang Anda miliki!

Anda pun mulai menggunakan Apache Benchmark untuk melihat hasil benchmark pada endpoint get/ yang tadi dengan perintah berikut:

ab -n 100 -c 50 http://127.0.0.1/get/

Perintah tersebut berarti mengirim 100 request HTTP GET ke endpoint get/ dengan concurrency 50 yang artinya dalam 100 request tersebut, hanya boleh dijalankan sebanyak 50 request sebanyak satu waktu. Analoginya seperti Anda ingin mengeluarkan air sebanyak 100 liter dengan membuka sebanyak 50 keran. Anda bisa melihat penjelasan tentang concurrency di StackOverflow.

Setelah menjalankan perintah tersebut dan menunggu cukup lama, keluarlah hasil berikut:

Ternyata, rata-rata waktu yang dibutuhkan untuk 1 request adalah 3.6 detik dan aplikasi Anda hanya bisa memproses sebanyak 0.27 request per detiknya! Pantas saja pengguna Anda komplain tentang hal ini. Apabila tidak ditangani secepatnya, maka pengguna Anda bisa meninggalkan aplikasi Anda. Anda mulai mencari sumber masalahnya.

Analisis menggunakan Profiler

Profiling bisa dikatakan sebagai proses analisis terhadap kode yang dijalankan. Analisis yang dihasilkan biasanya juga merupakan waktu eksekusi, stack trace, atau memori yang digunakan. Aplikasi profiler pun juga dikhususkan untuk satu bahasa saja, seperti cProfile untuk Python, JProfiler untuk Java, atau profiler bawaan V8 untuk NodeJS. Untuk contoh kali ini, yang akan digunakan adalah django-silk yang dibuat untuk framework Django di Python dan sudah menyediakan UI. Cara pemasangan dapat dilihat di sini.

Sekarang, sudah dipasang Silk untuk profiling dan dapat diakses pada path silk/ .

Contoh tampilan django-silk

Sekarang, terlihat bahwa akses untuk endpoint get/ paling lama memakan waktu 4.1 detik secara keseluruhan. Apabila di-klik, maka kita bisa melihat rincian hasilnya.

Rincian profiler untuk method get_limit().

Dari sini, kita bisa melihat bahwa bottleneck-nya berasal dari package models dari dalam Django sendiri yang melakukan query pada database. Apa mungkin masalahnya ada pada query yang kita lakukan?

Rincian SQL pada profiler
Query yang dilakukan

Ternyata, query yang dilakukan adalah mengambil semua entry yang ada! Karena kita memiliki data sebanyak 400.000 baris, tentu saja hal ini akan sangat memakan waktu, dan kita sebenarnya hanya membutuhkan 50 baris pertama saja. Dari hasil tersebut, kita akan mencoba melakukan optimisasi pada bagian query-nya:

def get_limit(request):
persons = list(Person.objects.all()[:50])
result = []
for person in persons:
result.append({
'first_name': person.first_name,
'last_name': person.last_name,
'instrument': person.instrument
})
return JsonResponse(result, safe=False)

Berdasarkan dokumentasi Django, melakukan slicing pada Person.objects.all() akan melakukan query dengan perintah LIMIT , dan hanya akan menghasilkan 50 data pertama saja. Mari kita buktikan dengan mengakses endpoint get/ dengan kode yang sudah kita ubah:

Rincian profiler untuk method get_limit() setelah modifikasi
Rincian SQL pada profiler setelah modifikasi
Query yang dilakukan setelah modifikasi

Sekarang, request menjadi sekitar 2 ribu kali lebih cepat! Semua berkat limit yang dilakukan pada query. Apabila kita melakukan load test yang sama kembali, hasilnya adalah sebagai berikut:

Yay! Sekarang kita bisa melakukan 52 request per detiknya dengan rata-rata waktu request selama 20 ms. Sekarang, pengguna aplikasi Anda tidak akan komplain lagi.

Optimisasi Semua Kode!

Mungkin membuat kode yang cepat terlihat sangat penting dan banyak memberikan manfaat. Dalam mengembangkan aplikasi atau prototyping Anda tidak diharapkan untuk memberikan solusi tercepat dalam suatu masalah. Memikirkan solusi yang benar dan cepat membutuhkan waktu yang lebih lama dibandingkan solusi yang benar.

Melakukan optimisasi pada kode di saat yang belum diperlukan seringkali disebut premature optimization. Premature optimization sendiri tidak selalu memberikan hasil yang baik dan seringkali hanya membuang-buang waktu saja. Hanya lakukan optimisasi apabila permasalahan Anda memerlukan itu.

Perlu diperhatikan juga bahwa kode yang ‘cepat’ seringkali merupakan kode yang ‘sulit dibaca’ karena optimisasi biasanya dilakukan pada level teknis seperti manipulasi bit, atau penggunaan algoritma maupun struktur data yang jarang dikenal orang. Sebagai contoh, berikut adalah contoh kode C untuk menghitung 1/√x:

float invsqrt(float x) {
return 1.0/sqrt(x);
}

dan berikut adalah versi cepat (pada masanya) dari kode di atas.

Kode tersebut adalah kode yang digunakan oleh game 3D bernama Quake III Arena. Pada masanya, dibutuhkan perhitungan 1/√x yang cukup cepat untuk melakukan 3D processing.

Kesimpulan

Kita telah membahas tentang pentingnya performa aplikasi yang kita buat. Performa aplikasi dapat menentukan apakah seorang pengguna nyaman dalam menggunakan aplikasi kita. Terdapat beberapa aplikasi yang bisa kita gunakan dalam mengukur performa aplikasi seperti load tester (Apache Benchmark) atau profiler. Walau demikian, terkadang melakukan optimisasi kode di awal (premature optimization) tidaklah diperlukan dan dapat mengorbankan readability suatu kode. Lebih baik untuk melakukan optimisasi kode apabila kita memang memerlukannya.

Sekian dari penjelasan saya tentang performa kode dan analisisnya. Jangan lupa untuk melihat artikel oleh anggota tim yang lain.

--

--