How to tune applications with DatArcs Optimizer

Seldomly do application developers know exactly how users will use their applications. In order to maximize performance, developers tune their applications according to synthetic loads and use cases.

Some applications have dynamic tuning capabilities, such as in databases, where the application behavior can change in response to the applied load. While this technique is beneficial, it is time-consuming to develop and may not capture all the important use-cases.

Starting with version 0.10 of DatArcs Optimizer, application developers can perform dynamic tuning automatically.

In this post, we’ll show how this can be done using an example application written in bash.

Tuning a simple application – one knob and one metric

Below is an example application that has one knob that controls its behavior and one metric that describes its performance. The knob integer value is communicated via a file named “knob”, and the metric integer value is communicated via a file named “metric”.

#!/bin/bash
while true ; do
        knob=`cat knob`
        metric=$(( 50 - (knob-7) * (knob-7) ))
        echo "$metric" | tee metric.tmp
        mv metric.tmp metric
        sleep 0.2
done

Passing knob and metric values via files as we did above is not recommended unless proper lock mechanisms are used. We did it here only to keep the example simple.

We’ll now write a plugin to DatArcs Optimizer that will sample “metric” and feed it into Optimizer as a metric with the name: “example_app.my_target_metric”. To do this, we’ll write a new C++ class that inherits from MetricsPlugin defined in metricsPlugin.h provided with Optimizer. The constructor defines the metrics, and sample_system() samples the metrics and feeds them to Optimizer.

#include <fstream>
#include "metricsPlugin.h"

class examplePlugin : public MetricsPlugin {
public:
        examplePlugin();
        ~examplePlugin() {};
        void sample_system(MetricsPluginData *current_values);
        std::string get_name() {return "example_app";};
private:
        MetricsPlugin::Metric m_target_metric;
};

examplePlugin::examplePlugin() {
        m_target_metric.name="my_target_metric";
        m_target_metric.aggregated=false;
}

void examplePlugin::sample_system(MetricsPluginData *current_values) {
        std::ifstream f("metric");
        int metric_val;
        f >> metric_val;
        current_values->insert(m_target_metric,metric_val);
}

extern "C" MetricsPlugin* create_object() {
        return new examplePlugin;
}

extern "C" void destroy_object( MetricsPlugin* object ) {
        delete (examplePlugin *) object;
}

We then compile the plugin as follows:

$ gcc -c -fpic example_app_plugin.cpp
$ gcc -shared -o libexampleplugin.so example_app_plugin.o

Putting everything together via the knobs.yaml configuration file:

domain:
  common:
    knobs:
      my_knob:
        options: [0,4,8,12]
        get_script: "cat knob"
        set_script: "echo $value > knob"
    metrics:
      include: [example_app.*]
      target: example_app.my_target_metric
      plugins: [libexampleplugin.so]

We’ll start with a knob value of 0:

$ echo 0 > knob

We’re now ready to start tuning:

$ datarcs-optimizer --knobs=no-embedded --knobs=knobs.yaml

Optimizer will now attempt to find the optimal knob value from the possible options 0,4,8,12. The target metric as a function of time is plotted below:

It took Optimizer around 90 seconds to properly tune our simple application and give it a 49x boost in its target metric.

Applications with phases

We’ve shown above a very simplistic example of an application. In reality, applications have phases, and in each phase the behavior of the application may be different. We’ll add phases to our simple bash application:

#!/bin/bash
phase=0
counter=0
while true ; do
        ((counter++))
        if [ $counter -ge 150 ] ; then
                phase=$(( (phase + 1) % 2 ))
                counter=0
        fi
        max=$((5+phase*2))
        knob=`cat knob`
        metric=$(( 50 - (knob-max) * (knob-max) ))
        echo -e "${metric}\n${phase}" | tee metric.tmp
        mv metric.tmp metric
        sleep 0.2
done

In the revised application, we have two phases, as follows:

Phase Target metric (x = knob value)
0 50-(x-5)^2
1 50-(x-7)^2

The revised application will spend 30 seconds in each phase, and then switch. In each phase, the target metric behaves differently, depending on the knob setting. This behavior resembles real applications with different phases of execution. In order to feed the new metric to Optimizer, we’ll alter the plugin:

class examplePlugin : public MetricsPlugin {
public:
        examplePlugin();
        ~examplePlugin() {};
        void sample_system(MetricsPluginData *current_values);
        std::string get_name() {return "example_app";};
private:
        MetricsPlugin::Metric m_target_metric, m_phase_metric;
};

examplePlugin::examplePlugin() {
        m_target_metric.name="my_target_metric";
        m_target_metric.aggregated=false;
        m_phase_metric.name="phase";
        m_phase_metric.aggregated=false;
}

void examplePlugin::sample_system(MetricsPluginData *current_values) {
        std::ifstream f("metric");
        int metric_val;
        f >> metric_val;
        current_values->insert(m_target_metric,metric_val);
        f >> metric_val;
        current_values->insert(m_phase_metric,metric_val);
}

We’ll now tune the application again:

In the first 3 minutes, Optimizer learned about the workload, which switched phases every 30 seconds. When enough data was gathered, Optimizer attempted to tune each phase separately. Approximately 6 minutes into the run, Optimizer found the best knob setting for each of the two phases, and switched to the correct setting after each phase change.

Optimizer can work with hundreds of application metrics in order to determine the phase of the application and to tune for each specific phase. Moreover, new knobs can be easily added in knobs.yaml.

The big picture

Application tuning is a complex task, involving many moving parts and use of advanced algorithms. DatArcs Optimizer can be used to automate application tuning, freeing application developers to focus on the business logic.

Application tuning can be employed in conjunction with Optimizer’s CPU and OS tuning features for even greater gains.

Stay tuned for announcements of support for specific applications in the coming months!

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply