Code performance and load testing
I used to mess up code performance and load testing because I thought they are similar, but actually not, so I write this article to introduce them and help myself to have more concepts about these two things. I will use Golang and Ruby to show some examples when talking about these two-term in the following section. Below are the definitions of code performance and load testing.
code performance
: measuring the performance of the code. find out which implementation of the code (same result after executing) is faster.
load testing
: load Testing is a type of Performance Testing used to determine a system’s behavior under both normal and peak conditions. It is used to ensure that the application performs satisfactorily when many users access it at the same time.
Code Performance: Benchmark
In many program languages, Benchmark is a commonly used tool for measuring the execution time of code. I will show Golang and Ruby examples in the following parts.
- Golang Example
Golang testing
package provides Benchmark tool. In the xx_test.go
file, we just need to add the function name starting with Benchmark
like func BenchmarkXxx()
. when running the command go test -bench=.
, it will trigger the benchmark function and output the report. The sample code is like below
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a")
}
}
The benchmark function must run the target code b.N
times. During benchmark execution, b.N is adjusted until the benchmark function lasts long enough to be timed reliably.
for example, if we have a repeat function in repeat.go
file like below.
// repeat.go
package main
func Repeat(char string) string {
var repeated string
for i := 0; i < 5; i++ {
repeated += char
}
return repeated
}
then in repeat_test.go
, we could place the BenchmarkRepeat
function.
package main
import "testing"
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a")
}
}
and after running go test -bench=.
It will print out the report as below.
It shows that the loop ran 11931190 times
at a speed of 93.45 ns
per loop.
- Ruby Example
In Ruby, there are also some gems that allow us to use Benchmark to test the performance of our code. benchmark/ips
is one of the gems. We just need to run gem install benchmark-ips
in our computer and then we could use it. We could test a repeat function that will print the text passed to it 5 times(the output result is just like the repeat function we write in Golang which shows above). The sample code is like below. Just place the name of this test and the code you want to test in the block of Benchmark.ips
.
require 'benchmark/ips'
def repeat(text)
text*5
end
Benchmark.ips do |x|
x.report("repeat") { repeat('a') }
end
After running it, the result will show as below format. It shows that within 5.034288
seconds, the maximum time of repeat function could be executed is 22.382
million times and the percent of standard deviation is 3.4%
.
And we could compare the performance of different ways to write the repeat function but have the same output.
require 'benchmark/ips'
def repeat1(text)
text*5
end
def repeat2(text)
"#{text}#{text}#{text}#{text}#{text}"
end
Benchmark.ips do |x|
x.report("repeat1") { repeat1('a') }
x.report("repeat2") { repeat2('a') }
end
It looks like the repeat2
function is much faster than repeat1
.
Loading Testing: K6
There are many loading testing tools. Here could find the list and the introduction of the loading testing tools. In this article, I will only introduce one loading testing tool — K6. It is a modern load-testing tool scriptable in ES6 JS with support for HTTP/1.1, HTTP/2.0, and WebSocket. K6 allows us to write a script on javascript for loading testing. No matter the web server is built by which language like Golang or Ruby, we could use K6 to do the test. we just need to install K6. If using macOS Homebrew, we could run the below script in the terminal. The detailed installation instruction could find here.
brew install k6
After installation is done, we need to create a javascript file that includes K6 and run it to start the loading test. The sample code in K6 online document is as below. The load test is testing for the performance of sending HTTPS GET
request to https://test.k6.io
.
// script.js
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('https://test.k6.io');
sleep(1);
}
We need to run k6 run script.js
command in the terminal and it will trigger the loading test. By default, It will start the loading test with only 1 virtual user. If we want to add more users or longer the duration, could pass vus
and duration
as parameters in the command, like below.
k6 run --vus 10 --duration 30s script.js
or include the options in your JavaScript file. Then could just run k6 run script.js
to start the loading test. No need to pass the parameters. The sample code is as below.
// script.js
import http from 'k6/http';
import { sleep } from 'k6';
export const options = {
vus: 10,
duration: '30s',
};
export default function () {
http.get('https://test.k6.io');
sleep(1);
}
When you run a test, K6 outputs a plain-text logo, your test progress, and some test details. After the test finishes, K6 prints the full details and summary statistics of the test metrics.
In the result, key metrics are below:
http_req_duration
: the duration time for one request from sending out and getting the response.http_req_failed
: the ratio and number of failed requests.http_reqs
: the total number of requests and the number of requests sent out each second.
Below are two examples of using K6 to do a loading test. One is a simple web server built by Golang. The other is a Ruby on Rails website which deploys on fly.io .
- Golang example
The Golang project is here. Basically, it’s a simple web server project. Just running go run main.go
, then the server will start in localhost:8080
. If send HTTPS GET
request to localhost:8080/greeting
, it will print out greeting words Hello Gopher
on the browser. I want to test the loading of sending HTTPS GET
request to localhost:8080/greeting
, so the K6 script script.js
is like below.
// script.js
import http from "k6/http";
import { sleep } from "k6";
export default function () {
http.get("https://localhost:8080/greeting");
sleep(1);
}
then we could run k6 run script.js
in the root path where script.js
at. The result is as below.
It shows that when only one user sends GET
request to our Golang web server. the http_req_duration
is 534µs
, the http_req_failed
is 0
and forhttp_reqs
, the total number of requests is 1
and the number of requests sent out each second is around 0.99/s
.
We could add more virtual users and longer the duration to see the result. Run k6 run --vus 10 --duration 30s script.js
, and the result is as below.
It shows that when there are 10 users sending GET
request to our Golang web server within 30 seconds. the http_req_duration
is 675µs
, the http_req_failed
is 0
and forhttp_reqs
, the total number of requests are 300
and the number of requests sent out each second is around 0.977/s
.
It looks like the Golang web server performance didn’t change a lot when the number of users increase from 1 to 10.
- Ruby on Rails example
I have a Ruby on Rails project which is my origami works website, and I already deploy it to fly.io . The website URL is http://huei-origami.fly.dev/
, and I want to test GET
request tohttp://huei-origami.fly.dev/
, so the K6 script script.js
is as below.
import http from "k6/http";
import { sleep } from "k6";
export default function () {
http.get("http://huei-origami.fly.dev/");
sleep(1);
}
First, running k6 run --vus 10 --duration 30s script.js
, and the result is as below.
With only 10 virtual users, the average ofhttp_req_duration
is 225ms
, the http_req_failed
is 0
and forhttp_reqs
, the total number of requests is 374
and the number of requests sent out each second is around 11.87/s
.
Now increase the number of virtual users to 100. run k6 run --vus 100 --duration 30s script.js
The result shows below.
With 100 virtual users, the average ofhttp_req_duration
is 486ms
, the http_req_failed
is 0
and forhttp_reqs
, the total number of requests is 3100
and the number of requests sent out each second is around 95.54/s
.
According to the result, the rails website performance will be two times worse if increase the number of users is from 10 increase to 100.