/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package br.ufrgs.inf.prosoft.aplcachetf.extension;
import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.Method;
import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.CacheabilityMetrics;
import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.Thresholds;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author root
*/
public class APLCache {
private static final Logger LOGGER = Logger.getLogger(APLCache.class.getName());
private final List<Method> methods;
public APLCache(List<Method> methods) {
this.methods = methods;
}
private void calculateMetrics() {
LOGGER.log(Level.INFO, "Calculating metrics for {0} methods...", this.methods.size());
this.methods.stream().parallel().forEach(Method::calculateMetrics);
}
private void calculateThresholds(int kStdDev) {
LOGGER.log(Level.INFO, "Calculating thresholds for {0} methods with {1} stdDev...", new Object[]{this.methods.size(), kStdDev});
Thresholds.reset();
Thresholds.population = getPopulation();
this.methods.stream().forEach(Method::calculateThresholds);
LOGGER.log(Level.INFO, "\tAverage ExecutionTime: {0}", Thresholds.getAverageExecutionTime());
LOGGER.log(Level.INFO, "\tStdDv ExecutionTime: {0}", Thresholds.getStdDevExecutionTimeRatio());
LOGGER.log(Level.INFO, "\tThreshold ExecutionTime: {0}", Thresholds.expensivenessThreshold(kStdDev));
LOGGER.log(Level.INFO, "\tAverage HitRatio: {0}", Thresholds.getAverageHitRatio());
LOGGER.log(Level.INFO, "\tStdDv HitRatio: {0}", Thresholds.getStdDevHitRatio());
LOGGER.log(Level.INFO, "\tThreshold HitRatio: {0}", Thresholds.hitThreshold(kStdDev));
LOGGER.log(Level.INFO, "\tAverage MissRatio: {0}", Thresholds.getAverageMissRatio());
LOGGER.log(Level.INFO, "\tStdDv MissRatio: {0}", Thresholds.getStdDevMissRatio());
LOGGER.log(Level.INFO, "\tThreshold MissRatio: {0}", Thresholds.missThreshold(kStdDev));
LOGGER.log(Level.INFO, "\tAverage Shareability: {0}", Thresholds.getAverageShareability());
LOGGER.log(Level.INFO, "\tStdDv Shareability: {0}", Thresholds.getStdDevShareability());
LOGGER.log(Level.INFO, "\tThreshold Shareability: {0}", Thresholds.shareabilityThreshold(kStdDev));
LOGGER.log(Level.INFO, "\tAverage HitsPerTimeInCache: {0}", Thresholds.getAverageHitsPerTimeInCache());
LOGGER.log(Level.INFO, "\tStdDv HitsPerTimeInCache: {0}", Thresholds.getStdDevHitsPerTimeInCache());
LOGGER.log(Level.INFO, "\tThreshold HitsPerTimeInCache: {0}", Thresholds.hitsPerTimeInCacheThreshold(kStdDev));
}
private void filterCacheableInputs(int kStdDev) {
LOGGER.log(Level.INFO, "Filtering inputs of {0} methods under {1} stdDev threshold...", new Object[]{this.methods.size(), kStdDev});
CacheabilityMetrics.K_STANDARD_DEVIATION = kStdDev;
this.methods.forEach(Method::filterCacheableInputs);
this.methods.removeIf(method -> method.groupsOfOccurrences().count() == 0);
}
private void removeSingleOccurrences() {
int initialMethodsSize = this.methods.size();
LOGGER.log(Level.INFO, "Removing not reusable inputs from {0} methods", this.methods.size());
this.methods.forEach(Method::removeSingleOccurrences);
LOGGER.log(Level.INFO, "Removing not reusable methods from {0} methods", this.methods.size());
this.methods.removeIf(method -> method.groupsOfOccurrences().count() == 0);
int removedMethods = initialMethodsSize - this.methods.size();
if (removedMethods > 0) {
LOGGER.log(Level.INFO, "Removed {0} of {1} not reusable methods", new Object[]{removedMethods, initialMethodsSize});
}
}
public void recommend(String outputPath) {
recommend(0, outputPath);
}
public void recommend(int kStdDev, String outputPath) {
LOGGER.log(Level.INFO, "Recommending TTL per method for {0} methods", this.methods.size());
removeSingleOccurrences();
calculateMetrics();
calculateThresholds(kStdDev);
filterCacheableInputs(kStdDev);
LOGGER.log(Level.INFO, "{0} cacheable methods detected", this.methods.size());
Collections.sort(this.methods, (m1, m2) -> Long.compare(m2.getSavedTime(), m1.getSavedTime()));
this.methods.forEach(method -> {
System.out.println(method.getName() + " Occurrences " + method.getOccurrencesSize() + " Inputs " + method.groupsOfOccurrences().count() + " TTL " + method.getTtl() + " HpTiC " + method.getHitsPerTimeInCache() + " Saves " + method.getSavedTime());
});
try (FileWriter fileWriter = new FileWriter(outputPath)) {
JsonObject jsonCacheableParameters = new JsonObject();
this.methods.forEach(method -> {
JsonObject cacheableParameters = new JsonObject();
method.groupsOfOccurrences().forEach(group -> {
cacheableParameters.addProperty(group.getParameters(), group.getTtl());
});
jsonCacheableParameters.add(method.getName(), cacheableParameters);
});
Gson gson = new GsonBuilder().setPrettyPrinting().create();
gson.toJson(jsonCacheableParameters, fileWriter);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "invalid <outputPath>");
}
}
private long getPopulation() {
return this.methods.stream().map(Method::getOccurrencesSize).reduce(Integer::sum).orElse(0);
}
}