Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ida-rtslab/public-code/2024_energy-aware_placement
1 result
Show changes
Showing
with 130542 additions and 47 deletions
File deleted
File deleted
......@@ -22,20 +22,21 @@ public class MixedReality_Optimization {
//Parameters for the different studies
int numberOfFunctionInstances = 2;
Boolean usingUniformLoadDistribution = false;
int standardDeviation = 10;
Boolean linksAreLoaded = true;
Boolean randomStartLocation = false;
int standardDeviation = 30;
Boolean linksAreLoaded = false;
Boolean oneCoreAllocation= true;
Boolean randomStartLocation = true;
Boolean colocationScenario = false; // Note: the colocation scenario is only run if the random start location is not activated
String appId = "MR_Service"; // identifier of the application
Application application = createApplication(appId);
//TODO Check whether this is still actual
System.out.println("Description of the experiment:");
System.out.println("- Number of functions instances: " + numberOfFunctionInstances);
if (usingUniformLoadDistribution)
{System.out.println("- Load distribution: NORMAL with std " + standardDeviation);}
{System.out.println("- Load distribution: UNIFORM");}
else{
System.out.println("- Load distribution: UNIFORM");
System.out.println("- Load distribution: NORMAL with std " + standardDeviation);
}
System.out.println(" The (mean) load increases by 10% per scenario");
if(linksAreLoaded){
......@@ -43,6 +44,11 @@ public class MixedReality_Optimization {
}else{
System.out.println("- Load on links: 0%");
}
if(oneCoreAllocation){
System.out.println("- A function instance is allocated up to one CPU core");
}else{
System.out.println("- A function instance is allocated all remaining CPU capacity");
}
if (randomStartLocation)
{System.out.println("- Start location: RANDOM ");}
else{
......@@ -52,20 +58,27 @@ public class MixedReality_Optimization {
//Determine startLocation = at which device the request is received. This is the placing device
String startLocation;
if (randomStartLocation){
Random randomLoc = new Random();
Integer randomStart=randomLoc.nextInt(11);
startLocation= "gateway_" + randomStart.toString();
System.out.println("Random start location is "+startLocation);
System.out.println("Random start location is decided per repetition");
startLocation=null; //Will be set later in the random case
}
else{
//For this scenario the start node is NOT random between scenarios
Integer randomStart=0;
startLocation= "gateway_" + randomStart;
System.out.println("Non random start location is "+startLocation);
if(colocationScenario){
//For this scenario the start node is NOT random between scenarios
Integer randomStart=0; //Start device of 0 with the current function instance placement enables the possibility of colocation on the requesting device
startLocation= "gateway_" + randomStart;
System.out.println("Non random start location is "+startLocation);
}
else {
//For this scenario the start node is NOT random between scenarios
Integer randomStart=4;
startLocation= "gateway_" + randomStart;
System.out.println("Non random start location is "+startLocation);
}
}
System.out.println("Creating the network to be used");
Network network = new Network(application,startLocation);
Network network = new Network(application,startLocation,oneCoreAllocation);
System.out.println("Placing the function instances");
placeFunctionInstances(numberOfFunctionInstances,network.getEdgeDevices());
......@@ -80,6 +93,16 @@ public class MixedReality_Optimization {
for (int a = 0; a< Config.REPETITION_NUMBER; a++) {
System.out.println("Repetition " + a);
//For the random beginning device, we want to change it here
if(randomStartLocation){
// Get new random beginning device
Random randomLoc = new Random(Config.SEED+a);
Integer randomStart=randomLoc.nextInt(11);
startLocation= "gateway_" + randomStart.toString();
System.out.println("Random start location is "+startLocation);
network.modifyPlacingDevice(startLocation);
}
//for each utilization scenario
for (int i = 0; i < Config.LOAD_SCENARIO_NUMBER; i++) {
System.out.println("Utilization scenario " + i);
......
package device;
public interface EnergyModel {
Double getDynamicPowerForUtilization (Double utilization);
}
package device;
import utils.Config;
import static java.lang.Math.floor;
public class PiecewiseLinear implements EnergyModel{
//Power data from Ahvar et al. - Parasilo server
int[] powerPerCore = new int[]{98,148,161,176,178,180,184,200,208,212,215,217,218,221,230,237,241};
int[] powerDynPerCore =new int[17];
public PiecewiseLinear(){
for (int i=0; i<powerDynPerCore.length;i++){
powerDynPerCore[i]=powerPerCore[i]-powerPerCore[0];
}
}
@Override
public Double getDynamicPowerForUtilization(Double utilization) {
double powerRes=0.0;
if(utilization>=100.0){
powerRes=powerDynPerCore[powerDynPerCore.length-1];
}
else{
int j = (int) floor(utilization* Config.DEFAULT_DEVICE_NB_CORES/100);
powerRes = (powerDynPerCore[j+1]-powerDynPerCore[j])*Config.DEFAULT_DEVICE_NB_CORES*(utilization/100)+((j+1)*powerDynPerCore[j]-j*powerDynPerCore[j+1]);
}
return powerRes;
}
}
......@@ -2,6 +2,8 @@ package system;
import application.Application;
import device.Device;
import device.EnergyModel;
import device.PiecewiseLinear;
import org.apache.commons.math3.util.Pair;
import placement.RequestPlacementOutput;
import utils.Config;
......@@ -40,7 +42,11 @@ public class Network {
String placingDevice;
public Network(Application applicationConsidered, String placingDevice){
EnergyModel energyModel;
Boolean oneCoreAllocation;
public Network(Application applicationConsidered, String placingDevice, boolean oneCoreAllocation_){
application= applicationConsidered;
this.placingDevice=placingDevice;
......@@ -80,6 +86,9 @@ public class Network {
createNetworkGraph();
dijkstraAlg = new DijkstraShortestPath<>(graph);
energyModel = new PiecewiseLinear();
oneCoreAllocation=oneCoreAllocation_;
}
private void createNetworkGraph(){
......@@ -173,13 +182,14 @@ public class Network {
Double currentUtilization = utilization.get(device);
//Get size to be calculated
Double size = application.getTargetFunctionCPUSize(functionInstance);
//Get available capacity
Double availableCapacity = Config.DEFAULT_DEVICE_CAPACITY * (1-(currentUtilization / 100));//Note: this is allocating all the available capacity
//Get capacity allocated to the function instance
Double allocatedCapacity=getAllocatedCapacity(currentUtilization);
//Calculate according to formula
if (availableCapacity==0){
if (allocatedCapacity==0){
deviceTiming=Double.valueOf(Config.INFINITE);
}else {
deviceTiming = (size / availableCapacity);
deviceTiming = (size / allocatedCapacity);
}
//Add to the Map variable
time_devices.put(device+"-"+functionInstance, deviceTiming);
......@@ -187,6 +197,23 @@ public class Network {
return deviceTiming;
}
private Double getAllocatedCapacity(Double currentUtilization){
//Get available capacity
Double availableCapacity = Config.DEFAULT_DEVICE_CAPACITY_PER_CORE*Config.DEFAULT_DEVICE_NB_CORES * (1-(currentUtilization / 100));
//A function instance gets up to one core of capacity or the full remaining capacity
Double allocatedCapacity=0.0;
if(availableCapacity-Config.DEFAULT_DEVICE_CAPACITY_PER_CORE >=0 && oneCoreAllocation){
//At least one core is available, we allocate one core
allocatedCapacity= (double) Config.DEFAULT_DEVICE_CAPACITY_PER_CORE;
}
else{
//We allocate what is available
allocatedCapacity=availableCapacity;
}
return allocatedCapacity;
}
private double calculateWeight(Device source, Device destination){
//For this scenario the weight is the latency calculated as the distance between the nodes divided by 100
double distance = calculateDistance(source.getLocationInformation(),destination.getLocationInformation())/100;
......@@ -226,12 +253,12 @@ public class Network {
return totalLatency;
}
private double getDynamicPowerForUtilization(double utilizationWithServicePlaced, double dynPower){
/*private double getDynamicPowerForUtilization(double utilizationWithServicePlaced, double dynPower){
//We consider a linear model were the dynamic power part is 0 at 0% utilization (OBS it still has idle power but this is another constant)
// and Dyn power att 100% utilization
//utilization in percentage
return dynPower*(utilizationWithServicePlaced/100);
}
}*/ //TODO Move to a new Linear energy model class
public double getLinkEnergyConsumption(String startFunctionInstanceName, String destinationFunctionInstanceName,
String startLocation, String destinationLocation){
......@@ -354,10 +381,16 @@ public class Network {
//Get Pidle
int p_device_idle = Config.DEFAULT_DEVICE_P_IDLE;
//GetPdyn
int p_device_dyn = Config.DEFAULT_DEVICE_P_DYN;
//Calculate utilization after allocation
double utilizationAfterAllocation = calculateUtilizationAfterAllocation(deviceName);
//Calculate energy
energy_consumption = p_device_idle * deviceTiming + p_device_dyn * deviceTiming; //Assumes 100% utilization when selected
energy_consumption = p_device_idle * deviceTiming + energyModel.getDynamicPowerForUtilization(utilizationAfterAllocation) * deviceTiming;
if(energy_consumption <0){
System.out.println("PRoblem");
}
//Add to Map variables
energy_devices.put(functionInstanceName, energy_consumption);
......@@ -365,15 +398,25 @@ public class Network {
return energy_consumption;
}
private double calculateUtilizationAfterAllocation(String device){
Double currentUtilization = utilization.get(device);
Double allocatedCapacity = getAllocatedCapacity(currentUtilization);
//Calculate share of utilization of this allocated capacity
Double allocatedUtilization= allocatedCapacity/(Config.DEFAULT_DEVICE_CAPACITY_PER_CORE*Config.DEFAULT_DEVICE_NB_CORES)*100; //In percentage
return currentUtilization+allocatedUtilization;
}
public double getInstanceMarginalEnergyConsumption(String functionInstanceName){
// 1- Getting the hardware name
Pair<String, String> nameSplit = Utils.splitLongFunctionInstanceName(functionInstanceName);
String deviceName = nameSplit.getKey();
//Check whether we already have info about this
double energy_residual_consumption;
double energy_marginal_consumption;
if(energy_residual_devices.get(functionInstanceName)!=null){
energy_residual_consumption=energy_residual_devices.get(functionInstanceName);
energy_marginal_consumption =energy_residual_devices.get(functionInstanceName);
}else{
//We need to calculate it
......@@ -382,24 +425,25 @@ public class Network {
//Get Pidle
int p_device_idle = Config.DEFAULT_DEVICE_P_IDLE;
//GetPdyn
int p_device_dyn = Config.DEFAULT_DEVICE_P_DYN;
//Calculate utilization after allocation
double utilizationAfterAllocation = calculateUtilizationAfterAllocation(deviceName);
//Get utilization value for this device
Double beforeUtilization = utilization.get(deviceName);
if (beforeUtilization == 0) {
energy_residual_consumption = p_device_idle * deviceTiming + p_device_dyn * deviceTiming; //Assumes the utilization gets up to 100%
energy_marginal_consumption = p_device_idle * deviceTiming + energyModel.getDynamicPowerForUtilization(utilizationAfterAllocation) * deviceTiming;
} else {
double utilizationWithServicePlaced = 100.0; //Assumption, see above
double utilizationIncrease = utilizationWithServicePlaced - beforeUtilization;
//OBS Assumes a linear power consumption model
double p_device_dyn_util = getDynamicPowerForUtilization(utilizationIncrease, p_device_dyn);//How much power does the utilization increase due to the placement requires?
energy_residual_consumption = p_device_dyn_util * deviceTiming; //Only the dynamic part
//How much power does the utilization increase due to the placement requires?
double powerIncrease = energyModel.getDynamicPowerForUtilization(utilizationAfterAllocation) - energyModel.getDynamicPowerForUtilization(beforeUtilization);
energy_marginal_consumption = powerIncrease * deviceTiming; //Only the dynamic part
}
//Add to Map variables
energy_residual_devices.put(functionInstanceName, energy_residual_consumption);
energy_residual_devices.put(functionInstanceName, energy_marginal_consumption);
}
return energy_residual_consumption;
return energy_marginal_consumption;
}
public void evaluatePlacementOutput(RequestPlacementOutput output, String startLocationMathematical, String endLocationMathematical){
......@@ -509,7 +553,7 @@ public class Network {
energyResLinkDevice = energyResLink + energyResDevice;
//Print the results in a usable way
System.out.println("Evaluated placement: Time " + timeLinkDevice + " Energy " + energyLinkDevice + " Residual energy " + energyResLinkDevice + "In details - time link "+ timeLink + " time device " + timeDevice + " energy link " + energyLink + " energy device " + energyDevice + " energy res link "+energyResLink + " energy res device "+energyResDevice);
System.out.println("Evaluated placement: Completion time " + timeLinkDevice + " Overall energy " + energyLinkDevice + " Marginal energy " + energyResLinkDevice + " In details - completion time link "+ timeLink + " completion time device " + timeDevice + " overall energy link " + energyLink + " overall energy device " + energyDevice + " marginal energy link "+energyResLink + " marginal energy device "+energyResDevice);
}
}
......@@ -540,7 +584,7 @@ public class Network {
//Devices
List<String> deviceNames = new ArrayList<>();
for (Device device : edgeDevices) {
if (device.getName().contains("gateway")) {//TODO: with current setup should be all of them so function not needed
if (device.getName().contains("gateway")) {//Note: with current setup should be all of them so this is only to double check
deviceNames.add(device.getName());
}
}
......@@ -597,4 +641,9 @@ public class Network {
public List<Device> getEdgeDevices(){
return edgeDevices;
}
// Used for the random beginning device case
public void modifyPlacingDevice(String newPlacingDevice){
this.placingDevice=newPlacingDevice;
}
}
......@@ -2,13 +2,13 @@ package utils;
public class Config {
public static final int LOAD_SCENARIO_NUMBER = 11;
public static final int REPETITION_NUMBER = 25;
public static final int REPETITION_NUMBER = 40;
public static final int DEFAULT_LINK_CAPACITY =500;//MBytes/sec
public static final int DEFAULT_LINK_P_IDLE =1;
public static final int DEFAULT_LINK_P_DYN =9;
public static final int DEFAULT_DEVICE_CAPACITY =500;//MI
public static final int DEFAULT_DEVICE_P_IDLE =98;//W - Data fron Ahvar - Parasilo zero core was 750
public static final int DEFAULT_DEVICE_P_DYN =148;//W - Data fron Ahvar - Parasilo one core was 250
public static final int DEFAULT_DEVICE_CAPACITY_PER_CORE =500;//MI
public static final int DEFAULT_DEVICE_P_IDLE =98;//W - Data from Ahvar - Parasilo zero core
public static final int DEFAULT_DEVICE_NB_CORES =16;// Data from Ahvar - Parasilo
public static final int INFINITE = 10000;//Very big number so that link/devices with 100% utilization are discarded as impossible
public static final int SEED = 165746;
}
# Energy-aware Distributed Microservice Request Placement at the Edge
# Energy Metrics for Edge Microservice Request Placement Strategies
On this repository you can find the material presented in the following article:
Toczé, Klervie, and Simin Nadjm-Tehrani. "Energy-aware Distributed Microservice Request Placement at the Edge." arXiv preprint [arXiv:2408.13748](https://arxiv.org/abs/2408.13748) (2024).
Toczé, Klervie, and Simin Nadjm-Tehrani. "Energy Metrics for Edge Microservice Request Placement Strategies." Article accepted at ICPE25.
This repository contains three different types of files:
- `/Code`: the Java simulator files
- `/Results`: the results files which are presented in the paper, both the raw files and the processed ones
- `/Analysis`: the analysis scripts used on the results files to obtain the figures presented in the paper
## Running the simulator
Note: this work is using Gurobi as an optimization solver so you will need a Gurobi license for the simulation to run.
The simulator has been implemented and run on Windows.
### Installation
To install the Java simulator, follow these steps:
1. Download [Intellij IDEA](https://www.jetbrains.com/idea/).
2. Clone this repository to your machine.
3. Open the `/Code` folder as a new project in Intellij IDEA.
4. Install any missing JDK that Intellij IDEA requires by clicking on the button.
5. Verify that the .jars files from `/Code/jars` are added to the project. They should appear in `Project Structure/Project Settings/Modules`. If not, add them using the "+".
6. Install [Gurobi](https://support.gurobi.com/hc/en-us/articles/4534161999889-How-do-I-install-Gurobi-Optimizer) (Full installation) and [activate your license](https://support.gurobi.com/hc/en-us/articles/12872879801105-How-do-I-retrieve-and-set-up-a-Gurobi-license).
They offer free academic licenses. Make sure that the Gurobi jar in the `/Code/jars` folder corresponds to the one in your installation. If not, update the jar file in the folder to the one from your installation.
### Running
To run a simulation:
1. Update if necessary the `/Code/src/utils/Config.java` file with the wanted settings.
2. Update if necessary lines 23-29 in the `/Code/src/MixedReality_Optimization.java` file with the wanted parameters. These correspond to a run group and have to be modified to obtain another run group.
3. Right click on the `/Code/src/MixedReality_Optimization.java` file in the project tree and select "Run".
4. After the simulation has finished running, save the output log if you want to further analyze it.
To run the same epxeriments as in the paper, use the following parameter settings in `/Code/src/MixedReality_Optimization.java`.
| Run group type | Name |numberOfFunctionInstances | usingUniformLoadDistribution | standardDeviation | linksAreLoaded | oneCoreAllocation| randomStartLocation | colocationScenario |
| -------- | ------- |-------- | ------- |-------- | ------- |-------- | ------- |-------- |
| 1 | Initial study | 2 | false | 10 | true | true | false | false |
| 2 | Normally distributed load | 2 | false | 10 | false | true | false | false |
| 3 | Fixed load | 2 | true | N/A (any value is fine) | false |true | false | false|
| 4 | Larger standard deviation | 2 | false | 30 | false |true | false | false |
| 5 | 4 function instances | 4 | false | 10 | false | true |false | false |
| 6 | 6 function instances | 6 | false | 10 | false | true | false | false |
| 7 | Full co-location | 6 | false | 10 | false | true| false | true |
| 8 | Random beginning device - Normally distributed load | 2 | false | 10 |false | true | true | false |
| 9 | Random beginning device - Fixed load | 2 | true | N/A (any value is fine) | false |true | true | false |
| 10 | Random beginning device - Larger standard deviation | 2 | false | 30 | false |true | true | false |
| 11 | Random beginning device - 4 function instances | 4 | false | 10 | false | true | true | false |
| 12 | Random beginning device - 6 function instances | 6 | false | 10 | false | true | true | false |
Note: The `/Code/src/utils/Config.java` file contains a seed to enable the reproduction of the experiments. Based on what you are using the simulator for, you may want to change it or disable it.
The seed is used to generate the utilization levels as well as to choose the random request device if this parameter is set to true.
## Analysis
Note: the analysis requires that you have [Python](https://www.python.org/downloads/) and e.g. [RStudio](https://posit.co/download/rstudio-desktop/) installed.
The analysis of the output is performed in two phases:
1. The output log (a `.txt` file) is parsed using Python scripts to create `.csv` files containing the useful data for the graphs
2. The obtained `.csv` files are loaded into a R script which takes care of rendering the curves
The repository content is being updated.
The `/Analysis` folder contains two Python scripts and one R script.
- `Analysis/resultLogParsing.py` extracts information about energy consumption (Overall and Marginal) and completion time for all placements contained in the log.
- `Analysis/resultLogParsing_UtilizationDifferent.py` extracts information about the utilization levels of the devices used in the placement solution, but only for the cases where the marginal and overall objectives lead to different placements.
- `Analysis/evaluationCurvesAndAnalysis.R` contains the code used for drawing the figures and extracting some statistics presented in the paper.
To run the Python script:
1. Make sure to update the files names in the script files. This is done in two places: (1) on line 5, input the name of the resulting `.csv` file, and (2) on line 14 or 17, input the name of the input log file.
2. Execute the script.
To run the R script:
1. Install the ggplot2 package and any other packages necessary (Rstudio should prompt you with the ones that are necessary).
2. Run the two first lines to load ggplot2 and set the working directory to the `/Results` folder, e.g. using the "Run" button.
3. Select the code block corresponding to the curve or analysis you want. Each code block between `####Begin` and `####End` is runnable independently.
4. Make sure that the file name(s) is/are the correct one(s).
5. Run the selected lines, e.g. using the "Run" button.
##Results
The `/Results` folder has one subfolder per run group type.
In each subfolder, you can find:
- A `.txt` file: this is the raw output log
- One or several `.csv` files: these are the output of the parsing step. The name of the file indicates which parsing script was applied.
- One or several `.pdf` files: these are the figures, drawn using the R script.
There is an additional folder for the energy model figure.
The files are named in a systematic way, with files corresponding to the same run group having the same name possibly with a prefix and/or suffix.
## Previous versions
A previous version of this work is presented in the following article:
Toczé, Klervie, and Simin Nadjm-Tehrani. "Energy-aware Distributed Microservice Request Placement at the Edge." arXiv preprint [arXiv:2408.13748](https://arxiv.org/abs/2408.13748) (2024).
The commit corresponding to this work is 863013c1ff59d7cccfe9eb33d70210efd44ebb56.
## Authors
Klervie Toczé (klervie.tocze@liu.se or k.m.tocze@vu.nl)
Klervie Toczé (k.m.tocze@vu.nl)
## License
CC BY-NC 4.0.
......
This diff is collapsed.