This is a walk-through of developing a single-knob application under RAPID(C), swaptions. Checkout the benchmark suite PARSEC for details.
A single knob controls the number of iterations of the Monte-Carlo simulation for each swaption.
The code needed for the walk-through has been provided.
0.1) Checkout the repo for RAPID(C) library.
Below is the structure of the repo. From now on, we use [root] to refer to the top dir.
[root] ├── cppSource # source of RAPID(C) c++ library │ └── build # output of built library, rsdg.a ├── docs # [ignore this]source for the website portal ├── modelConstr # source of python scripts │ ├── appExt # example app-specific methods │ ├── data # experiment data │ ├── merge # [ignroe this] future work │ └── source # python script source └── walkthrough # materials for this walkthrough ├── example_debug # example outputs in the walkthrough ├── example_outputs #.. ├── example_training_outputs#.. ├── instrumented # instrumented code └── orig # original app code
0.2) Build the RAPID(C) c++ library
First build the required dependencies
$ sudo apt-get install libcurl-dev
$ sudo pip install lxml
make sure the path to libcurl is updated in [root]/cppSource/Makefile
cd cppSource
make
The generated static library is [root]/cppSource/build/rsdg.a
0.3) Build the original Swaptions Binary
$ cd walkthrough/orig
$ make
The output is a binary file, [root]/walkthrough/orig/swaptions
0.4) Install Gurobi
$ gurobi_cl -v
The first step is to generate the structure of RSDG for the application. It should contain all the dependencies and other constraints.
To do that, run the following commands:
$ cd [root]
$ mkdir run & cd run
$ python ../modelConstr/source/rapid.py --stage 1 --desc ../walkthrough/instrumented/depfileswaptions
In the command above,
There will be two directories being generated
[root]/run/output: contains the output after stage 1. Now it should contain 2 files.
– swaptions.xml: The RSDG structure. See example swaptions.xml.
– trainingset: The initial trianing set served as groundtruth to validate the model. See example trainingset
In this step, RAPID(C) will determine all the weights for each node in the RSDG. To do that, it needs to know how to run the application, i.e. all the command line arguments, the binary location, etc. It would run the application with multiple configurations and measure their Cost (execution time). Then it would examine the output and measure the QoS metric for each run with different configuration.
2.1) Tell RAPID(C) how to run the application
Take a look at the implementation of class AppMethods in root/modelConstr/source/Classes.py.
This is the parent class prepared for developers to plugin their own implementations for their apps. Besides all the prepared built-in methods, developers are supposed to override two methods, train(), and runGT().
Now take a look at the call line for these two methods in root/modelConstr/source/stage_2/trainApp.py
RAPID(C) will first call runGT() to generate the groundTruth, and then call train() with a internally generated config_table and two pre-defined output paths.
The developers job is to implement their own object of AppMethods and plug it into RAPID(C)’s script.
2.2) runGT()
Now let’s take a look at the example implementation of AppMethod in root/modelConstr/appExt/swaptionMet.py
Please update the varibale “bin_swaptions” in the top of the class definition to point to your location of the compiled binary for swaptions.
In short, in our example, runGT() will be called by RAPID(C) to run swaptions once with default setting, and then move the output to another place.
2.3) train(config_table, costFact, mvFact)
Then let’s take another look at the example implementation of train(). It runs each configuration iteratively. During this process, the execution time will be measured.
In short, train() iterates through all configurations in config_table and run them by assemblying different command line arguments. After each run, train() also records the cost and QoS of each run by measuring the execution time and calculating the QoS by calling a method, checkSwaption().
2.4) Tell RAPID(C) how to evaluate the QoS, checkSwaption()
In the previous step, the output of each run is ./output.txt and the ground truth file generated before is ./training_outputs/groundtruth.txt. It calls checkSwaption() to measure the QoS before backing up the output.
Take a look at the checkSwaption function in this class. This function describes how to calculate the QoS for each output files generated before.
In short, this function compares the output file against the ground truth file and return the mean error. This error served as the QoS metric of our application.
2.5) Hook the methods to RAPID(C)
After the previous steps, an inherited class of AppMethods shall be created in a separate module. Now let’s take a look at how to hook this module to RAPID(C) so that it can be loaded dynamically.
In the main script, root/modelConstr/source/rapid.py, RAPID(C) loads the module given a path to our class file and create an instance of our class.
This instance will be used later on as described in 2.1).
Now let’s run the script, assuming we’re under our run directory [root/run]
python ../modelConstr/source/rapid.py --stage 4 --desc ../walkthrough/instrumented/depfileswaptions --model linear --met ../modelConstr/appExt/swaptionMet.py
The outputs layout should be:
root/run ├── debug # debug LP files and solutions │ ├── fittingcost.lp │ ├── fittingmv.lp │ ├── maxcost.sol │ └── maxmv.sol ├── outputs # run outputs │ ├── cost.rsdg # RSDG coeffs for Cost │ ├── modelValid.csv # Cost prediction validation │ ├── mv.rsdg # RSDG coeffs for MV │ ├── swaptions-cost.fact # measurement of Cost │ ├── swaptions-mv.fact # measurement of MV │ ├── swaptions.profile # combined Cost and MV │ ├── swaptions.xml # final RSDG in XML format │ └── trainingset # initial training set └── training_outputs #training outputs ├── grountTruth.txt ├── output1000000.txt ├── output100000.txt ... └── output900000.txt
We can compare our results against the example outputs under root/walkthrough/example_XXX.
In Swaptions, the main work is in this loop. [ beg, end ] describes the total number of swaptions (argument -ns in command line). Then for each swaption, it calls a routine to do the computation with arguments, one of which is numOfSwitch (argument -sm in command line).
Our knob is this numOfSwith which controls the number of simulations for each iteration.
Key insight: Tune this knob dynamically to optimize the QoS within the budget.
Now that we are done with all the preparation for RAPID(C), RAPID(C) library is ready to be integrated into the source.
a) Include the Header
b) Add some essential global variables
To setup RAPID(C) and let it dynamically configure the application, we set up a few more variables.
c) Modify main() to accept additional cmd line args
d) Setup a RAPID(C) manager
Write a setupMission() that inits the mission.
The default solver in RAPID(C) is Gurobi, and the alternative is LpSolve. The solver can be executed locally or remotely. Please only use rsdgMission::REMOTE if the solver cannot be installed on the machine.
UNIT_PER_CHECK indicates how often does RAPID(C) check the usage and re-configure. It is supposed to be used together with setUnit() where it says how many units have to be done for the mission.
setBudget() sets the budget (in milliseconds) to execute the mission.
Note that there is one argument &change_Simulation_Num_Cont in regContService(). This is the “action” to be taken when the optimization result comes back for this service.
The procedure behind this is:
In our Swaptions example, we need the “action” to change the numOfSwitch according to the paramter value.
e) Create an action
f) Tell RAPID(C) when it finishes a work-unit.
At the time when a job is done, RAPID(C) has to be notified.
In our case, at the end of each loop iteration, add the following line.
Refer to the modified makefile, root/walkthrough/instrumented/Makefile and see the changes that adds the compiled static library to the source.
The original run command for Swaptions is as shown below:
../orig/swaptions -ns 100 -sm 100000 -nt 1
The version with RAPID(C) involvement:
../orig/swaptions -ns 100 -sm 100000 -nt 1 -rsdg -b [BUDGET_IN_SEC] -xml outputs/swaptions.xml -u [UNIT_PER_CHECK] -cont