---
title: "Keuntungan dan Kerugian `string` di OpenResty - API7.ai"
description: "Artikel ini memperkenalkan teknik optimasi yang harus diikuti dalam OpenResty: memproses permintaan dengan singkat, sederhana, dan cepat serta menghindari pembuatan data perantara. Artikel ini juga menunjukkan teknik optimasi untuk string."
url: "https://api7.ai/id/learning-center/openresty/advantages-and-disadvantages-of-string"
---

# Keuntungan dan Kerugian \`string\` di OpenResty

API7.ai

December 8, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

Pada artikel sebelumnya, kita telah mengenal fungsi-fungsi blocking umum di OpenResty, yang sering disalahgunakan oleh pemula. Mulai dari artikel ini, kita akan masuk ke inti dari optimasi performa, yang akan melibatkan banyak teknik optimasi yang dapat membantu kita dengan cepat meningkatkan performa kode OpenResty, jadi jangan anggap remeh.

Dalam proses ini, kita perlu menulis lebih banyak kode pengujian untuk merasakan bagaimana menggunakan teknik-teknik optimasi ini dan memverifikasi efektivitasnya sehingga kita dapat memanfaatkannya dengan baik.

## Di Balik Layar Tips Optimasi Performa

Teknik optimasi adalah bagian dari "praktik", jadi sebelum kita melakukannya, mari kita bahas "teori" dari optimasi.

Metode optimasi performa akan berubah seiring dengan iterasi LuaJIT dan OpenResty. Beberapa metode mungkin akan dioptimalkan langsung oleh teknologi dasar dan tidak perlu lagi dikuasai; pada saat yang sama, akan ada beberapa teknik optimasi baru. Oleh karena itu, yang paling penting adalah menguasai konsep konstan di balik teknik-teknik optimasi ini.

Mari kita lihat beberapa ide kritis tentang performa dalam pemrograman OpenResty.

### Teori 1: Memproses permintaan harus singkat, sederhana, dan cepat

OpenResty adalah server web, sehingga sering menangani 1.000+, 10.000+, atau bahkan 100.000+ permintaan klien secara bersamaan. Oleh karena itu, untuk mencapai performa keseluruhan tertinggi, kita harus memastikan bahwa permintaan individu diproses dengan cepat dan berbagai sumber daya, seperti memori, dipulihkan.

- "Singkat" yang disebutkan di sini berarti siklus hidup permintaan harus singkat agar tidak mengambil sumber daya untuk waktu yang lama tanpa melepaskannya; bahkan untuk koneksi panjang, batas waktu atau jumlah permintaan harus ditetapkan untuk melepaskan sumber daya secara teratur.
- "Sederhana" yang kedua mengacu pada melakukan hanya satu hal dalam satu API. Pecahkan logika bisnis yang kompleks menjadi beberapa API dan pertahankan kode tetap sederhana.
- Terakhir, "cepat" berarti jangan memblokir thread utama dan jangan menjalankan terlalu banyak operasi CPU. Bahkan jika Anda harus melakukannya, jangan lupa untuk bekerja dengan metode lain yang kami perkenalkan di artikel sebelumnya.

Pertimbangan arsitektur ini tidak hanya cocok untuk OpenResty, tetapi juga untuk bahasa dan platform pengembangan lebih lanjut, jadi saya harap Anda dapat memahami dan memikirkannya dengan seksama.

### Teori 2: Hindari menghasilkan data sementara

Menghindari data yang tidak berguna dalam proses sementara bisa dibilang adalah teori optimasi yang paling dominan dalam pemrograman OpenResty. Mari kita lihat contoh kecil untuk menjelaskan data yang tidak berguna dalam proses sementara.

```
$ resty -e 'local s= "hello"
s = s .. " world"
s = s .. "!"
print(s)
'
```

Dalam cuplikan kode ini, kita melakukan beberapa operasi penyambungan pada variabel `s` untuk mendapatkan hasil `hello world!`. Tetapi hanya keadaan akhir `hello world!` dari `s` yang berguna. Nilai awal `s` dan penugasan sementara adalah semua data sementara yang harus dihasilkan sesedikit mungkin.

Alasannya adalah bahwa data sementara ini akan membawa kerugian performa inisialisasi dan GC. Jangan meremehkan kerugian ini; jika ini muncul dalam kode panas seperti loop, performa akan jelas menurun. Saya juga akan menjelaskan ini nanti dengan contoh string.

## `string` bersifat tidak berubah

Sekarang, kembali ke topik artikel ini, `string`. Di sini, saya menekankan fakta bahwa `string` bersifat tidak berubah di Lua.

Tentu saja, ini tidak berarti bahwa `string` tidak dapat disambung, dimodifikasi, dll., tetapi ketika kita memodifikasi `string`, kita tidak mengubah `string` asli tetapi membuat objek `string` baru dan mengubah referensi ke `string`. Jadi secara alami, jika `string` asli tidak memiliki referensi lain, itu akan dipulihkan oleh GC (garbage collection) Lua.

Manfaat yang jelas dari `string` yang tidak berubah adalah menghemat memori. Dengan cara ini, hanya akan ada satu salinan `string` yang sama dalam memori, dan variabel yang berbeda akan menunjuk ke alamat memori yang sama.

Kerugian dari desain ini adalah ketika datang ke penambahan dan pemulihan `string`, setiap kali Anda menambahkan `string`, LuaJIT harus memanggil `lj_str_new` untuk menanyakan apakah `string` sudah ada; jika tidak, perlu membuat `string` baru. Jika Anda melakukan ini sangat sering, itu akan memiliki dampak besar pada performa.

Mari kita lihat contoh konkret dari operasi penyambungan `string` seperti yang ada dalam contoh ini, yang ditemukan di banyak proyek open source OpenResty.

```
$ resty -e 'local begin = ngx.now()
local s = ""
-- `for` loop, menggunakan `..` untuk melakukan penyambungan string
for i = 1, 100000 do
    s = s .. "a"
end
ngx.update_time()
print(ngx.now() - begin)
'
```

Apa yang dilakukan kode sampel ini adalah melakukan 100.000 penyambungan `string` pada variabel `s` dan mencetak waktu berjalan. Meskipun contoh ini agak ekstrem, ini memberikan gambaran yang baik tentang perbedaan sebelum dan setelah optimasi performa. Tanpa optimasi, kode ini berjalan selama 0,4 detik di laptop saya, yang masih relatif lambat. Jadi bagaimana kita harus mengoptimalkannya?

Dalam artikel sebelumnya, jawabannya diberikan, yaitu menggunakan `table` untuk melakukan lapisan enkapsulasi, menghapus semua `string` sementara dan hanya menyimpan data asli dan hasil akhir. Mari kita lihat implementasi kode konkretnya.

```
$ resty -e 'local begin = ngx.now()
local t = {}
-- for loop yang menggunakan array untuk menyimpan string, menghitung panjang array setiap kali
for i = 1, 100000 do
    t[#t + 1] = "a"
end
-- Menyambung string menggunakan metode concat dari array
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Kita dapat melihat bahwa kode ini menyimpan setiap string secara bergantian dengan `table`, dan indeks ditentukan oleh `#t + 1`, yaitu panjang `table` saat ini ditambah `1`. Terakhir, gunakan fungsi `table.concat` untuk menggabungkan setiap elemen array. Ini secara alami melewati semua string sementara dan menghindari 100.000 kali `lj_str_new` dan GC.

Itulah analisis kode kita, tetapi bagaimana hasil optimasinya? Kode yang dioptimalkan hanya membutuhkan 0,007 detik, yang berarti peningkatan performa lebih dari 50 kali. Dalam proyek nyata, peningkatan performa mungkin bahkan lebih terasa karena dalam contoh ini kita hanya menambahkan satu karakter `a` setiap kali.

Bagaimana perbedaan performa jika `string` baru memiliki panjang 10x `a`?

Apakah 0,007 detik kode sudah cukup baik untuk pekerjaan optimasi kita? Tidak, masih bisa dioptimalkan. Mari kita ubah satu baris kode lagi dan lihat hasilnya.

```
$ resty -e 'local begin = ngx.now()
local t = {}
-- for loop, menggunakan array untuk menyimpan string, mempertahankan panjang array itu sendiri
for i = 1, 100000 do
    t[i] = "a"
end
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Kali ini, kita mengubah `t[#t + 1] = "a"` menjadi `t[i] = "a"`, dan hanya dengan satu baris kode, kita dapat menghindari 100.000 panggilan fungsi untuk mendapatkan panjang array. Ingat operasi untuk mendapatkan panjang array yang kita sebutkan di bagian `table` sebelumnya? Ini memiliki kompleksitas waktu `O(n)`, operasi yang relatif mahal. Jadi, di sini kita cukup mempertahankan indeks array kita sendiri untuk melewati operasi mendapatkan panjang array. Seperti kata pepatah, jika Anda tidak mampu mengacaukannya, Anda bisa menghindarinya.

Tentu saja, ini adalah cara penulisan yang lebih sederhana. Kode berikut menggambarkan lebih jelas bagaimana mempertahankan indeks array sendiri.

```
$ resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
local s = table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

## Mengurangi `string` sementara lainnya

Kesalahan yang baru saja kita bicarakan, `string` sementara yang disebabkan oleh penyambungan `string`, sangat jelas. Dengan beberapa pengingat dari kode sampel di atas, saya yakin kita tidak akan membuat kesalahan serupa lagi. Namun, ada beberapa `string` sementara yang lebih tersembunyi yang dihasilkan di OpenResty, yang jauh lebih sulit dideteksi. Misalnya, fungsi penanganan `string` yang akan kita bahas di bawah ini sering digunakan. Bisakah Anda bayangkan bahwa itu juga menghasilkan `string` sementara?

Seperti yang kita ketahui, fungsi `string.sub` mengambil bagian tertentu dari `string`. Seperti yang kita sebutkan sebelumnya, `string` di Lua tidak berubah, jadi mengambil string baru melibatkan `lj_str_new` dan operasi GC berikutnya.

```
resty -e 'print(string.sub("abcd", 1, 1))'
```

Fungsi dari kode di atas adalah mengambil karakter pertama dari `string` dan mencetaknya. Secara alami, ini pasti menghasilkan `string` sementara. Apakah ada cara yang lebih baik untuk mencapai efek yang sama?

```
resty -e 'print(string.char(string.byte("abcd")))'
```

Tentu saja. Melihat kode ini, kita pertama-tama menggunakan `string.byte` untuk mendapatkan kode numerik dari karakter pertama dan kemudian menggunakan `string.char` untuk mengubah angka menjadi karakter yang sesuai. Proses ini tidak menghasilkan `string` sementara apa pun. Oleh karena itu, paling efisien untuk menggunakan `string.byte` untuk melakukan pemindaian dan analisis terkait `string`.

## Memanfaatkan dukungan SDK untuk tipe `table`

Setelah mempelajari cara mengurangi `string` sementara, apakah Anda ingin mencobanya? Kemudian, kita dapat mengambil hasil dari kode sampel di atas dan mengeluarkannya ke klien sebagai konten badan respons. Pada titik ini, Anda dapat berhenti sejenak dan mencoba menulis kode ini sendiri terlebih dahulu.

```
$ resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
local response = table.concat(t, "")
ngx.say(response)
'
```

Jika Anda dapat menulis kode ini, Anda sudah lebih maju dari kebanyakan pengembang OpenResty. API Lua OpenResty sudah mempertimbangkan penggunaan `table` untuk penyambungan `string`, jadi dalam `ngx.say`, `ngx.print`, `ngx.log`, `cosocket:send`, dan API lain yang mungkin mengambil banyak `string`, ia menerima tidak hanya `string` sebagai parameter, tetapi juga menerima `table` sebagai parameter.

```
resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
ngx.say(t)
'
```

Dalam cuplikan kode terakhir ini, kita menghilangkan `local response = table.concat(t, "")`, langkah penyambungan `string`, dan langsung meneruskan `table` ke `ngx.say`. Ini menggeser tugas penyambungan `string` dari tingkat Lua ke tingkat C, menghindari pencarian, pembuatan, dan GC `string` lagi. Untuk `string` panjang, ini adalah peningkatan performa yang signifikan.

## Ringkasan

Setelah membaca artikel ini, kita dapat melihat bahwa banyak optimasi performa OpenResty berurusan dengan berbagai detail. Oleh karena itu, kita perlu mengetahui LuaJIT dan API Lua OpenResty dengan baik untuk mencapai performa optimal. Ini juga mengingatkan kita bahwa jika kita telah melupakan konten sebelumnya, kita harus meninjau dan mengkonsolidasinya tepat waktu.

Terakhir, pikirkan masalah ini: tulis string `hello`, `world`, dan `!` ke log kesalahan. Bisakah kita menulis kode sampel tanpa penyambungan `string`?

Juga, jangan lupa pertanyaan lain dalam teks. Bagaimana perbedaan performa dalam kode berikut jika `string` baru memiliki panjang 10x `a`?

```
$ resty -e 'local begin = ngx.now()
local t = {}
for i = 1, 100000 do
    t[#t + 1] = "a"
end
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Anda juga dipersilakan untuk membagikan artikel ini dengan teman-teman Anda untuk belajar dan berkomunikasi.

Share article link

Share

Related Posts

[![Non-blocking I/O - Kunci untuk meningkatkan performa OpenResty](https://static.api7.ai/2022/12/05/638dce5075e0f.png)](https://api7.ai/learning-center/openresty/blocking-io)

## [Non-blocking I/O - Kunci untuk meningkatkan performa OpenResty](https://api7.ai/learning-center/openresty/blocking-io)

[![OpenResty FAQ | Bagaimana OpenResty Digunakan dalam Praktik](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | Bagaimana OpenResty Digunakan dalam Praktik](https://api7.ai/learning-center/openresty/faq-5)

Table of Contents

- [Di Balik Layar Tips Optimasi Performa](#di-balik-layar-tips-optimasi-performa)

  - [Teori 1: Memproses permintaan harus singkat, sederhana, dan cepat](#teori-1-memproses-permintaan-harus-singkat-sederhana-dan-cepat)
  - [Teori 2: Hindari menghasilkan data sementara](#teori-2-hindari-menghasilkan-data-sementara)

- [`string` bersifat tidak berubah](#string-bersifat-tidak-berubah)

- [Mengurangi `string` sementara lainnya](#mengurangi-string-sementara-lainnya)

- [Memanfaatkan dukungan SDK untuk tipe `table`](#memanfaatkan-dukungan-sdk-untuk-tipe-table)

- [Ringkasan](#ringkasan)
