APLCache.java
Home
/
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
aplcachetf /
extension /
APLCache.java
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.text.MessageFormat;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
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.info(MessageFormat.format("Calculating metrics for {0} methods...", this.methods.size()));
this.methods.stream().parallel().forEach(Method::calculateMetrics);
}
private void calculateThresholds(int kStdDev) {
LOGGER.info(MessageFormat.format("Calculating thresholds for {0} methods with {1} stdDev...", this.methods.size(), kStdDev));
Thresholds.reset();
Thresholds.population = getPopulation();
this.methods.forEach(Method::calculateThresholds);
LOGGER.info(MessageFormat.format("\tAverage ExecutionTime: {0}", Thresholds.getAverageExecutionTime()));
LOGGER.info(MessageFormat.format("\tStdDv ExecutionTime: {0}", Thresholds.getStdDevExecutionTime()));
LOGGER.info(MessageFormat.format("\tThreshold ExecutionTime: {0}", Thresholds.expensivenessThreshold(kStdDev)));
LOGGER.info(MessageFormat.format("\tAverage HitRatio: {0}", Thresholds.getAverageHitRatio()));
LOGGER.info(MessageFormat.format("\tStdDv HitRatio: {0}", Thresholds.getStdDevHitRatio()));
LOGGER.info(MessageFormat.format("\tThreshold HitRatio: {0}", Thresholds.hitThreshold(kStdDev)));
LOGGER.info(MessageFormat.format("\tAverage MissRatio: {0}", Thresholds.getAverageMissRatio()));
LOGGER.info(MessageFormat.format("\tStdDv MissRatio: {0}", Thresholds.getStdDevMissRatio()));
LOGGER.info(MessageFormat.format("\tThreshold MissRatio: {0}", Thresholds.missThreshold(kStdDev)));
LOGGER.info(MessageFormat.format("\tAverage Shareability: {0}", Thresholds.getAverageShareability()));
LOGGER.info(MessageFormat.format("\tStdDv Shareability: {0}", Thresholds.getStdDevShareability()));
LOGGER.info(MessageFormat.format("\tThreshold Shareability: {0}", Thresholds.shareabilityThreshold(kStdDev)));
LOGGER.info(MessageFormat.format("\tAverage Distance: {0}", Thresholds.getAverageDistance()));
LOGGER.info(MessageFormat.format("\tStdDv Distance: {0}", Thresholds.getStdDevDistance()));
LOGGER.info(MessageFormat.format("\tThreshold Distance: {0}", Thresholds.distanceThreshold(kStdDev)));
}
private void filterCacheableInputs(int kStdDev) {
LOGGER.info(MessageFormat.format("Filtering inputs of {0} methods under {1} stdDev threshold...", 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.info(MessageFormat.format("Removing not reusable inputs from {0} methods", this.methods.size()));
this.methods.forEach(Method::removeSingleOccurrences);
LOGGER.info(MessageFormat.format("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.info(MessageFormat.format("Removed {0} of {1} not reusable methods", removedMethods, initialMethodsSize));
}
public void recommend(String outputPath) {
recommend(0, outputPath);
}
public void recommend(int kStdDev, String outputPath) {
LOGGER.info(MessageFormat.format("Recommending TTL per method for {0} methods", this.methods.size()));
removeSingleOccurrences();
calculateMetrics();
this.methods.forEach(Method::removeNotRecommendedInputs);
this.methods.removeIf(method -> method.getEstimatedSavedTime() == 0);
calculateThresholds(kStdDev);
filterCacheableInputs(kStdDev);
LOGGER.info(MessageFormat.format("{0} cacheable methods detected", this.methods.size()));
this.methods.sort((method1, method2) -> Double.compare(method2.getEstimatedSavedTime(), method1.getEstimatedSavedTime()));
this.methods.forEach(method -> {
if (method.getBestTFMetrics() != null) {
System.out.println(method.getName()
+ " Occurrences " + method.occurrences().count()
+ " Inputs " + method.groupsOfOccurrences().count()
+ " TTL " + method.getBestTFMetrics().getTtl()
+ " Saves " + method.getBestTFMetrics().getSavedTime()
+ " Hits " + method.getBestTFMetrics().getHits()
+ " Computation " + method.getBestTFMetrics().getComputationTime()
+ " TimeInCache " + method.getBestTFMetrics().getTimeInCache()
+ " Idle " + method.getBestTFMetrics().getIdleTime()
+ " Stales " + method.getBestTFMetrics().getStales());
} else {
System.out.println(method.getName()
+ " Occurrences " + method.occurrences().count()
+ " EstimatedSavedTime " + method.getEstimatedSavedTime()
+ " EstimatedIdleTime " + method.getEstimatedIdleTime()
+ " Inputs " + method.groupsOfOccurrences().count());
}
});
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.getMetrics().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(it -> it.occurrences().count()).reduce(Long::sum).orElse(0L);
}
}