Profiling Runtime Performance¶
Improvements in your BI implementation might be required to increase its runtime performance, minimize code execution times, increase throughput, and reduce latency. To do that, you need to identify performance bottlenecks, implications of concurrent executions, which code segments take longer to execute, and areas to improve performance. This can be done by using a profiler.
Profiler is a tool that monitors the BI runtime and its operations such as function calls. It can be used to understand the behavior and troubleshoot the performance issues of a Ballerina program and optimize it.
Note
Profiler is an experimental feature, which supports only a limited set of functionality. The commands associated with the tool might change in future releases.
Features¶
-
Profile a local session of a BI program through offline instrumentation.
-
Perform CPU profiling, which finds out where the CPU time is going.
-
Generate a flame graph, which shows the function call stack and the execution times of each function call. It provides an interface for viewing runtime performance and the ability to search, diagnose, and find performance bottlenecks.
Note
Profiling is a high-powered activity with a heavy overhead and can slow down your application.
Profile a BI program¶
To profile a Ballerina package, you can use the bal profile CLI command inside the root directory of the BI project.
Consider the following step-by-step guide to profile a Ballerina package.
Step 1: Create a BI project
-
Create a BI project named
sort -
Go to the file explorer view and replace the contents of the
main.balfile with the following Ballerina code, which creates an array of random integers, sorts them, and verifies the output.
import ballerina/io;
import ballerina/random;
public function main() returns error? {
int[] arr = check createRandomIntArray(check float:pow(10, 2).cloneWithType(int));
int[] sortedArr = bubbleSort(arr);
boolean isSorted = isSortedArray(sortedArr);
io:println("Is the array sorted? " + isSorted.toString());
}
public isolated function bubbleSort(int[] arr) returns int[] {
int n = arr.length();
int temp = 0;
boolean swapped = false;
foreach int i in 0 ... n - 2 {
foreach int j in 1 ... n - 1 - i {
if (arr[j - 1] > arr[j]) {
temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
swapped = true;
}
}
if (!swapped) {
break;
}
}
return arr;
}
isolated function isSortedArray(int[] sortedArr) returns boolean {
foreach int i in 0 ..< sortedArr.length() - 1 {
if (sortedArr[i] > sortedArr[i + 1]) {
return false;
}
}
return true;
}
isolated function createRandomIntArray(int size) returns int[]|error {
int[] array = [];
foreach int i in 0 ..< size {
array.push(check random:createIntInRange(0, int:MAX_VALUE));
}
return array;
}
Step 2: Run the profiler to get the profile details
- Run and profile the Ballerina package using the
bal profileCLI command via the VSCode terminal.
$ bal profile
You view the output below.
Compiling source
profiler_demo/sort:0.1.0
Generating executable
target/bin/sort.jar
================================================================================
Ballerina Profiler: Profiling...
================================================================================
Note: This is an experimental feature, which supports only a limited set of functionality.
[1/6] Initializing...
[2/6] Copying executable...
[3/6] Performing analysis...
[4/6] Instrumenting functions...
○ Instrumented module count: 31
○ Instrumented function count: 1016
[5/6] Running executable...
Is the array sorted? true
[6/6] Generating output...
○ Execution time: 3 seconds
○ Output: target/bin/ProfilerOutput.html
-------------------------------------------------------------------------------
Step 3: Examine the profile details
- Open the
target/bin/ProfilerOutput.htmlfile using a web browser window to examine the Ballerina Profiler output to find the slow-running functions and performance bottlenecks, which can then be addressed to improve the program’s overall performance.
Ballerina profiler output: Flame graph content and features¶
The flame graph generated by the Ballerina Profiler contains the following details.
-
Function call stack
-
Time taken to execute each function and its percentage
Flame graph also has the following features.
- Search a function.
-
Clear the search.
-
Click on a function call and zoom the view.
- Reset the view.
Examine the output and find performance bottlenecks¶
When you observe the flame graph generated by the Ballerina Profile for the above code, you can see the bubbleSort function has taken the most time to execute.
Let's try to optimize the sorting function to increase the performance of the Ballerina application.
Optimize the Ballerina code¶
In this example, you will use another sorting algorithm to reduce the time taken to execute the application. Replace the main.bal file with the code below, which uses the merge sort algorithm instead of the bubble sort algorithm for sorting.
import ballerina/io;
import ballerina/random;
public function main() returns error? {
int[] arr = check createRandomIntArray(check float:pow(10, 2).cloneWithType(int));
int[] sortedArr = mergeSort(arr);
boolean isSorted = isSortedArray(sortedArr);
io:println("Is the array sorted? " + isSorted.toString());
}
public isolated function mergeSort(int[] arr) returns int[] {
int n = arr.length();
int width = 1;
while (width < n) {
int l = 0;
while (l < n) {
int r = int:min(l + (width * 2 - 1), n - 1);
int m = int:min(l + width - 1, n - 1);
merge(arr, l, m, r);
l += width * 2;
}
width *= 2;
}
return arr;
}
isolated function merge(int[] a, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int[] L = [];
int[] R = [];
foreach int i in int:range(0, n1, 1) {
L[i] = a[l + i];
}
foreach int i in int:range(0, n2, 1) {
R[i] = a[m + i + 1];
}
int i = 0;
int j = 0;
int k = l;
while (i < n1 && j < n2) {
if L[i] <= R[j] {
a[k] = L[i];
i += 1;
} else {
a[k] = R[j];
j += 1;
}
k += 1;
}
while (i < n1) {
a[k] = L[i];
i += 1;
k += 1;
}
while (j < n2) {
a[k] = R[j];
j += 1;
k += 1;
}
}
isolated function isSortedArray(int[] sortedArr) returns boolean {
foreach int i in 0 ..< sortedArr.length() - 1 {
if (sortedArr[i] > sortedArr[i + 1]) {
return false;
}
}
return true;
}
isolated function createRandomIntArray(int size) returns int[]|error {
int[] array = [];
foreach int i in 0 ..< size {
array.push(check random:createIntInRange(0, int:MAX_VALUE));
}
return array;
}
When you profile the code again, you can see that you have reduced the time taken for sorting substantially and have improved the performance of the application.
Profile a Ballerina service¶
Consider the following step-by-step guide to profile a BI project that contains an HTTP service.
1. Create a new BI project and replace the main.bal file with the below code.
import ballerina/http;
type Country record {
string country;
int population;
string continent;
int cases;
int deaths;
};
type Data record {|
string country;
string continent;
int population;
decimal caseFatalityRatio;
|};
http:Client diseaseEp = check new ("https://disease.sh/v3");
final Country[] & readonly countries;
function init() returns error? {
countries = check diseaseEp->/covid\-19/countries;
}
service /covid19/countries on new http:Listener(8080) {
resource function get summary() returns json {
Data[] listResult = from var {country, continent, population, cases, deaths} in countries
where hasSignificantPopulation(population, deaths)
let decimal caseFatalityRatio = <decimal>deaths / <decimal>cases * 100
order by caseFatalityRatio descending
limit 1000
select {country, continent, population, caseFatalityRatio};
return getTopSortedValues(listResult);
}
isolated resource function get names() returns json {
return from var {country, population, deaths} in countries
where hasNonZeroPopulation(population, deaths)
select {country};
}
}
isolated function getTopSortedValues(Data[] listResult) returns json[] {
json[] sortedArray = listResult.sort("ascending", getKey);
return getTopElements(sortedArray);
}
isolated function getTopElements(json[] sortedArray) returns json[] {
return from var i in sortedArray
limit 10
select i;
}
isolated function hasSignificantPopulation(int population, int deaths) returns boolean {
return population >= 100 && deaths >= 10;
}
isolated function hasNonZeroPopulation(int population, int deaths) returns boolean {
return population >= 0 && deaths >= 0;
}
isolated function getKey(record {|string country; string continent; int population; decimal caseFatalityRatio;|} recordVal) returns string {
return recordVal.country;
}
2. Run the CLI command bal profile in the root directory to run the BI project and profile it. This will run the service while profiling it.
$ bal profile
Compiling source
profiler_demo/covid19_stats:0.1.0
Generating executable
target/bin/covid19_stats.jar
================================================================================
Ballerina Profiler: Profiling...
================================================================================
Note: This is an experimental feature, which supports only a limited set of functionality.
[1/6] Initializing...
[2/6] Copying executable...
[3/6] Performing analysis...
[4/6] Instrumenting functions...
○ Instrumented module count: 44
○ Instrumented function count: 2935
[5/6] Running executable...
3. Send 10 requests to each resource endpoint using the curl command.
$ curl localhost:8080/covid19/countries/names
$ curl localhost:8080/covid19/countries/summary
4. Stop the service by sending the SIGINT signal to the program. You can use Ctrl+C in the service running terminal to send the signal to it. This will stop both the program and profiling it and will generate the profiler output.
[6/6] Generating output...
○ Execution time: 58 seconds
○ Output: target/bin/ProfilerOutput.html
--------------------------------------------------------------------------------
5. Open the target/bin/ProfilerOutput.html file using a web browser to observe the flame graph generated by the profiler.
By observing the flame graph you can see that the get summary resource function takes more time than the get names resource function. You can use the profiler output data to improve the performance of the Ballerina service.





