aplcachetf
Changes
.gitignore 34(+34 -0)
pom.xml 75(+75 -0)
src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/GroupOfOccurrences.java 120(+120 -0)
Details
.gitignore 34(+34 -0)
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..4a482b8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+# ignore eclipse project files
+.project
+.classpath
+
+# ignore Intellij Idea project files
+.idea
+*.iml
+
+# Netbeans
+/nbproject/
+/nbactions.xml
+
+# Netbeans deploy
+/build/
+
+# Maven deploy
+/target/
+
+# Ant deploy
+/dist/
+
+# Class files
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
pom.xml 75(+75 -0)
diff --git a/pom.xml b/pom.xml
new file mode 100755
index 0000000..0c0f4d9
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <name>APLCacheTF</name>
+ <groupId>br.ufrgs.inf.prosoft.aplcachetf</groupId>
+ <artifactId>APLCacheTF</artifactId>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>br.ufrgs.inf.prosoft.trace</groupId>
+ <artifactId>Trace</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>br.ufrgs.inf.prosoft.cache</groupId>
+ <artifactId>Cache</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>br.ufrgs.inf.prosoft.tfcache</groupId>
+ <artifactId>TFCache</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>br.ufrgs.inf.prosoft.aplcache</groupId>
+ <artifactId>APLCache</artifactId>
+ <version>1.0</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>
+ ${project.build.directory}/lib
+ </outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <configuration>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ <classpathPrefix>lib</classpathPrefix>
+ <mainClass>br.ufrgs.inf.prosoft.aplcachetf.Main</mainClass>
+ </manifest>
+ <manifestEntries>
+ <Class-Path>lib/</Class-Path>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/adapter/TraceReader.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/adapter/TraceReader.java
new file mode 100644
index 0000000..d1f63d4
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/adapter/TraceReader.java
@@ -0,0 +1,67 @@
+/*
+ * 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.adapter;
+
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.Method;
+import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
+import br.ufrgs.inf.prosoft.tfcache.metadata.OccurrenceConcrete;
+import br.ufrgs.inf.prosoft.tfcache.metadata.OccurrenceReference;
+import br.ufrgs.inf.prosoft.trace.Parameter;
+import br.ufrgs.inf.prosoft.trace.Trace;
+import br.ufrgs.inf.prosoft.trace.TraceReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author romulo
+ */
+public class TraceReader extends br.ufrgs.inf.prosoft.trace.reader.TraceReader {
+
+ private static final Logger LOGGER = Logger.getLogger(TraceReader.class.getName());
+
+ public static List<Method> groupByMethods(List<Trace> traces) {
+ Map<String, List<Occurrence>> methodHasOccurrences = new HashMap<>();
+ while (!traces.isEmpty()) {
+ Trace trace = traces.remove(0);
+ try {
+ Occurrence occurrence;
+ if (trace instanceof TraceReference) {
+ TraceReference traceReference = (TraceReference) trace;
+ occurrence = new OccurrenceReference(traceReference.getIndex(), traceReference.getInstance(), trace.getStartTime(), trace.getEndTime(), trace.getUserSession());
+ } else {
+ occurrence = new OccurrenceConcrete(trace.getInstance(),
+ trace.getParameters().stream()
+ .map(Parameter::getData)
+ .collect(Collectors.toList()).toArray(),
+ trace.getReturn().getData(),
+ trace.getStartTime(),
+ trace.getEndTime(),
+ trace.getUserSession()
+ );
+ }
+ try {
+ methodHasOccurrences.get(trace.getName()).add(occurrence);
+ } catch (Exception ex) {
+ List<Occurrence> occurrences = new ArrayList<>();
+ occurrences.add(occurrence);
+ methodHasOccurrences.put(trace.getName(), occurrences);
+ }
+ } catch (Exception e) {
+ LOGGER.log(Level.INFO, "Trace discarted: {0}", trace);
+ }
+ }
+ return methodHasOccurrences.entrySet().stream()
+ .map(entry -> new Method(entry.getKey(), entry.getValue()))
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/APLCache.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/APLCache.java
new file mode 100644
index 0000000..cf68fd3
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/APLCache.java
@@ -0,0 +1,112 @@
+/*
+ * 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 filterUncacheableInputs(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);
+
+ int initialMethodsSize = 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} methods with no inputs recommemded", new Object[]{removedMethods, initialMethodsSize});
+ }
+ }
+
+ public void recommend(String outputPath) {
+ recommend(0, outputPath);
+ }
+
+ public void recommend(int kStdDev, String outputPath) {
+ calculateMetrics();
+ calculateThresholds(kStdDev);
+ filterUncacheableInputs(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);
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/GroupOfOccurrences.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/GroupOfOccurrences.java
new file mode 100644
index 0000000..9719bbd
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/GroupOfOccurrences.java
@@ -0,0 +1,120 @@
+/*
+ * 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.metadata;
+
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.Thresholds;
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.Metrics;
+import br.ufrgs.inf.prosoft.tfcache.Simulator;
+import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+
+/**
+ *
+ * @author root
+ */
+public class GroupOfOccurrences {
+
+ private static final Logger LOGGER = Logger.getLogger(GroupOfOccurrences.class.getName());
+
+ private final String parameters;
+ private final List<Occurrence> occurrences;
+ private Metrics bestMetrics;
+
+ public GroupOfOccurrences(String parameters, List<Occurrence> occurrences) {
+ this.parameters = parameters;
+ this.occurrences = occurrences;
+ }
+
+ public String getParameters() {
+ return this.parameters;
+ }
+
+ public Stream<Occurrence> occurrences() {
+ return this.occurrences.stream();
+ }
+
+ public int getOccurrencesSize() {
+ return this.occurrences.size();
+ }
+
+ public Metrics getBestMetrics() {
+ if (this.bestMetrics == null) {
+ throw new RuntimeException("hitsPerTimeInCache must be calculated");
+ }
+ return this.bestMetrics;
+ }
+
+ public Long getTtl() {
+ return getBestMetrics().getTtl();
+ }
+
+ public Long getSavedTime() {
+ return getBestMetrics().getSavedTime();
+ }
+
+ public Double getHitsPerTimeInCache() {
+ return getBestMetrics().getHitsPerTimeInCache();
+ }
+
+ public void calculateHitsPerTimeInCache() {
+ if (this.bestMetrics != null) {
+ LOGGER.log(Level.WARNING, "HitsPerTimeInCache already calculated");
+ }
+ this.bestMetrics = new Metrics();
+ if (this.occurrences.size() < 2) {
+ return;
+ }
+ long maxTTL = this.occurrences.get(this.occurrences.size() - 1).getStartTime() - this.occurrences.get(0).getStartTime();
+ LongStream.range(1, maxTTL).parallel().forEach(actualTTL -> {
+ Simulator.simulate(occurrences(), actualTTL, this.bestMetrics);
+ });
+ }
+
+ protected void calculateMetrics() {
+ calculateHitsPerTimeInCache();
+ if (this.occurrences.size() == 1) {
+ this.bestMetrics.addSameOccurrence(this.occurrences.get(0));
+ return;
+ }
+ for (int i = 0; i < this.occurrences.size(); i++) {
+ Occurrence occurrence = this.occurrences.get(i);
+ Object returnValue = occurrence.getReturnValue();
+ for (int j = i + 1; j < this.occurrences.size(); j++) {
+ Occurrence other = this.occurrences.get(j);
+ if (EqualsBuilder.reflectionEquals(returnValue, other.getReturnValue())) {
+ this.bestMetrics.addSameOccurrence(occurrence);
+ continue;
+ }
+ this.bestMetrics.addDifferentReturnOccurrence();
+ }
+ }
+ }
+
+ protected void calculateThresholds() {
+ Thresholds.sumExecutionTime += this.bestMetrics.getSameOccurrencesTotalExecutionTime();
+ Thresholds.executionTimes.add(this.bestMetrics.getSameOccurrencesTotalExecutionTime());
+
+ Thresholds.sumHitRatio += this.bestMetrics.getHitRatio();
+ Thresholds.hitRatios.add(this.bestMetrics.getHitRatio());
+
+ Thresholds.sumMissRatio += this.bestMetrics.getMissRatio();
+ Thresholds.missRatios.add(this.bestMetrics.getMissRatio());
+
+ Thresholds.sumShareability += this.bestMetrics.getShareability();
+ Thresholds.shareabilities.add(this.bestMetrics.getShareability());
+
+ if (this.bestMetrics.getHitsPerTimeInCache() > 0) {
+ Thresholds.sumHitsPerTimeInCache += this.bestMetrics.getHitsPerTimeInCache();
+ Thresholds.hitsPerTimeInCache.add(this.bestMetrics.getHitsPerTimeInCache());
+ }
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/Method.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/Method.java
new file mode 100644
index 0000000..6a9af4d
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metadata/Method.java
@@ -0,0 +1,148 @@
+/*
+ * 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.metadata;
+
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.CacheabilityPatternDecider;
+import br.ufrgs.inf.prosoft.tfcache.Metrics;
+import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author root
+ */
+public class Method {
+
+ private static final Logger LOGGER = Logger.getLogger(Method.class.getName());
+
+ private final String name;
+ private List<Occurrence> occurrences;
+ private List<GroupOfOccurrences> groupsOfOccurrences;
+ private Metrics bestMetrics;
+
+ public Method(String name, List<Occurrence> occurrences) {
+ this.name = name;
+ if (occurrences == null) {
+ throw new RuntimeException("Occurrences is null");
+ }
+ if (occurrences.isEmpty()) {
+ throw new RuntimeException("Occurrences is empty");
+ }
+ this.occurrences = occurrences;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public Metrics getBestMetrics() {
+ if (this.bestMetrics == null) {
+ throw new RuntimeException("hitsPerTimeInCache must be calculated");
+ }
+ return this.bestMetrics;
+ }
+
+ public Long getTtl() {
+ return getBestMetrics().getTtl();
+ }
+
+ public Long getSavedTime() {
+ return getBestMetrics().getSavedTime();
+ }
+
+ public Double getHitsPerTimeInCache() {
+ return getBestMetrics().getHitsPerTimeInCache();
+ }
+
+ public Stream<Occurrence> occurrences() {
+ if (this.occurrences == null) {
+ throw new RuntimeException("Occurrences already consumed");
+ }
+ return this.occurrences.stream();
+ }
+
+ public Stream<GroupOfOccurrences> groupsOfOccurrences() {
+ if (this.groupsOfOccurrences == null) {
+ groupByInput();
+ }
+ return this.groupsOfOccurrences.stream();
+ }
+
+ public int getOccurrencesSize() {
+ if (this.occurrences == null) {
+ if (this.groupsOfOccurrences == null) {
+ throw new RuntimeException("groupsOfOccurrences is null");
+ }
+ return this.groupsOfOccurrences.stream().map(GroupOfOccurrences::getOccurrencesSize).reduce(Integer::sum).orElse(0);
+ }
+ return this.occurrences.size();
+ }
+
+ private void groupByInput() {
+ if (this.occurrences == null) {
+ throw new RuntimeException("Occurrences already consumed");
+ }
+ Map<String, List<Occurrence>> inputHasOccurrences = new ConcurrentHashMap<>();
+ occurrences().forEach(occurrence -> {
+ String parameters = occurrence.getParametersSerialised();
+ inputHasOccurrences.compute(parameters, (key, oldValue) -> {
+ if (oldValue == null) {
+ oldValue = new ArrayList<>();
+ }
+ oldValue.add(occurrence);
+ return oldValue;
+ });
+ });
+ this.groupsOfOccurrences = inputHasOccurrences.entrySet().stream()
+ .map(entry -> new GroupOfOccurrences(entry.getKey(), entry.getValue()))
+ .collect(Collectors.toList());
+ this.occurrences = null;
+ }
+
+ public void calculateMetrics() {
+ if (this.groupsOfOccurrences == null) {
+ groupByInput();
+ }
+ this.groupsOfOccurrences.stream().forEach(GroupOfOccurrences::calculateMetrics);
+ long savedTime = groupsOfOccurrences().map(GroupOfOccurrences::getSavedTime).reduce(Long::sum).get();
+ GroupOfOccurrences max = groupsOfOccurrences().max((group1, group2) -> Long.compare(group1.getSavedTime(), group2.getSavedTime())).get();
+ long ttl = max.getTtl();
+ double hitsPerTimeInCache = max.getHitsPerTimeInCache();
+ this.bestMetrics = new Metrics(ttl, savedTime, hitsPerTimeInCache);
+ }
+
+ public void calculateThresholds() {
+ this.groupsOfOccurrences.stream().forEach(GroupOfOccurrences::calculateThresholds);
+ }
+
+ public void filterCacheableInputs() {
+ int initialInputsSize = this.groupsOfOccurrences.size();
+ this.groupsOfOccurrences.removeIf(CacheabilityPatternDecider::isNotCacheable);
+ int removedInputs = initialInputsSize - this.groupsOfOccurrences.size();
+ if (removedInputs > 0) {
+ LOGGER.log(Level.INFO, "\tRemoved {0} of {1} uncacheable inputs from method {2}", new Object[]{removedInputs, initialInputsSize, this.name});
+ }
+ initialInputsSize = this.groupsOfOccurrences.size();
+ this.groupsOfOccurrences.removeIf(groupOfOccurrence -> groupOfOccurrence.getTtl() == 0);
+ removedInputs = initialInputsSize - this.groupsOfOccurrences.size();
+ if (removedInputs > 0) {
+ LOGGER.log(Level.INFO, "\tRemoved {0} of {1} single-occurrence inputs from method {2}", new Object[]{removedInputs, initialInputsSize, this.name});
+ }
+ }
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityMetrics.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityMetrics.java
new file mode 100644
index 0000000..541d854
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityMetrics.java
@@ -0,0 +1,34 @@
+package br.ufrgs.inf.prosoft.aplcachetf.extension.metrics;
+
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.GroupOfOccurrences;
+import java.util.Optional;
+
+public class CacheabilityMetrics {
+
+ public static int K_STANDARD_DEVIATION = 0;
+
+ public static boolean isStaticData(GroupOfOccurrences groupOfOccurrences) {
+ return groupOfOccurrences.getBestMetrics().getNumberOfDifferentReturnOccurrences() == 0;
+ }
+
+ public static Optional<Boolean> changeMoreThanUsed(GroupOfOccurrences groupOfOccurrences) {
+ //+/- k sds
+ return Optional.of(groupOfOccurrences.getBestMetrics().getMissRatio() > Thresholds.missThreshold(K_STANDARD_DEVIATION));
+ }
+
+ public static Optional<Boolean> usedByManyRequests(GroupOfOccurrences groupOfOccurrences) {
+ return Optional.of(groupOfOccurrences.getBestMetrics().getHitsPerTimeInCache() >= Thresholds.hitsPerTimeInCacheThreshold(K_STANDARD_DEVIATION));
+ }
+
+ public static Optional<Boolean> isUserSpecific(GroupOfOccurrences groupOfOccurrences) {
+ if (groupOfOccurrences.getBestMetrics().getAmountOfIdentifiedSameOccurences() == 0) {
+ return Optional.empty();
+ }
+ //the less shareable, the more user specific
+ return Optional.of(groupOfOccurrences.getBestMetrics().getShareability() < Thresholds.shareabilityThreshold(K_STANDARD_DEVIATION));
+ }
+
+ public static Optional<Boolean> isExpensive(GroupOfOccurrences groupOfOccurrences) {
+ return Optional.of(groupOfOccurrences.getBestMetrics().getSameOccurrencesAverageExecutionTime() >= Thresholds.expensivenessThreshold(K_STANDARD_DEVIATION));
+ }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityPatternDecider.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityPatternDecider.java
new file mode 100644
index 0000000..93fac50
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/CacheabilityPatternDecider.java
@@ -0,0 +1,42 @@
+package br.ufrgs.inf.prosoft.aplcachetf.extension.metrics;
+
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.GroupOfOccurrences;
+import java.util.Optional;
+
+public class CacheabilityPatternDecider {
+
+ /**
+ * Flowchart definition
+ *
+ * @param groupOfOccurrences
+ * @return
+ */
+ public static boolean isCacheable(GroupOfOccurrences groupOfOccurrences) {
+ //Is the data completely static?
+ boolean isStaticData = CacheabilityMetrics.isStaticData(groupOfOccurrences);
+ if (isStaticData) { // staticity yes
+ return true;
+ }
+ // staticity no/not sure
+ Optional<Boolean> changeMoreThanUsed = CacheabilityMetrics.changeMoreThanUsed(groupOfOccurrences);
+ if (changeMoreThanUsed.isPresent() && changeMoreThanUsed.get()) { //changeMoreThanUsed true
+ return false;
+ }
+ //changeMoreThanUsed not/not sure
+ Optional<Boolean> usedByManyRequests = CacheabilityMetrics.usedByManyRequests(groupOfOccurrences);
+ if (usedByManyRequests.isPresent() && !usedByManyRequests.get()) { //useByManyRequests no
+ return false;
+ }
+ Optional<Boolean> isUserSpecific = CacheabilityMetrics.isUserSpecific(groupOfOccurrences);
+ if (isUserSpecific.isPresent() && isUserSpecific.get()) {
+ return true;
+ }
+ Optional<Boolean> isExpensive = CacheabilityMetrics.isExpensive(groupOfOccurrences);
+ return isExpensive.isPresent() ? isExpensive.get() : true;
+ }
+
+ public static boolean isNotCacheable(GroupOfOccurrences groupOfOccurrences) {
+ return !isCacheable(groupOfOccurrences);
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Metrics.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Metrics.java
new file mode 100644
index 0000000..b486553
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Metrics.java
@@ -0,0 +1,124 @@
+/*
+ * 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.metrics;
+
+import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * @author root
+ */
+public class Metrics extends br.ufrgs.inf.prosoft.tfcache.Metrics {
+//exactly same method calls
+
+ private long sameOccurrences;
+
+ //number of same method calls with different return
+ private long differentReturnOccurrences;
+ private long sameOccurrencesExecutionTime;
+
+ private long amountOfIdentifiedSameOccurences;
+ private final Set<String> uniqueUsers;
+
+ public Metrics() {
+ sameOccurrences = 0L;
+ differentReturnOccurrences = 0L;
+ sameOccurrencesExecutionTime = 0L;
+ amountOfIdentifiedSameOccurences = 0L;
+ uniqueUsers = new HashSet<>();
+ }
+
+ public void addSameOccurrence(Occurrence occurrence) {
+ sameOccurrences++;
+ addSameOccurrencesTotalExecutionTime(occurrence.getExecutionTime());
+
+ if (occurrence.getUserId() != null && !occurrence.getUserId().equals("Anonymous")) {
+ uniqueUsers.add(occurrence.getUserId());
+ addIdentifiedSameOccurence();
+ }
+ }
+
+ public void addDifferentReturnOccurrence() {
+ differentReturnOccurrences++;
+ }
+
+ private void addSameOccurrencesTotalExecutionTime(Long executionTime) {
+ sameOccurrencesExecutionTime += executionTime;
+ }
+
+ private void addIdentifiedSameOccurence() {
+ amountOfIdentifiedSameOccurences++;
+ }
+
+ public long getSameOccurrencesTotalExecutionTime() {
+ return sameOccurrencesExecutionTime;
+ }
+
+ public double getSameOccurrencesAverageExecutionTime() {
+ return new BigDecimal(getSameOccurrencesTotalExecutionTime())
+ .divide(new BigDecimal(sameOccurrences), 5, RoundingMode.HALF_UP)
+ .doubleValue();
+ }
+
+ public Long getNumberOfOccurrences() {
+ return sameOccurrences + differentReturnOccurrences;
+ }
+
+ public Long getNumberOfSameOccurrences() {
+ return sameOccurrences;
+ }
+
+ public Long getNumberOfDifferentReturnOccurrences() {
+ return differentReturnOccurrences;
+ }
+
+ //from 0% to 100%
+ public double getHitRatio() {
+ BigDecimal bd = new BigDecimal(getNumberOfSameOccurrences() * 100);
+ return bd.divide(new BigDecimal(getNumberOfOccurrences()), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ //from 0% to 100%
+ public double getMissRatio() {
+ BigDecimal bd = new BigDecimal(getNumberOfDifferentReturnOccurrences() * 100);
+ return bd.divide(new BigDecimal(getNumberOfOccurrences()), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public double getShareability() {
+ Long amountOfIdentifiedSameOccurences = getAmountOfIdentifiedSameOccurences();
+ if (amountOfIdentifiedSameOccurences == 0) {
+ return 100;
+ }
+ BigDecimal bd = new BigDecimal(getAmountOfUniqueIdentifiedSameOccurences() * 100);
+ return bd.divide(new BigDecimal(amountOfIdentifiedSameOccurences), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public Long getAmountOfIdentifiedSameOccurences() {
+ return amountOfIdentifiedSameOccurences;
+ }
+
+ public int getAmountOfUniqueIdentifiedSameOccurences() {
+ return uniqueUsers.size();
+ }
+
+ public Long getAmountOfAnonymousSameOccurences() {
+ return sameOccurrences - amountOfIdentifiedSameOccurences;
+ }
+
+ @Override
+ public String toString() {
+ return "{"
+ + "\"hitRatio\":" + getHitRatio() + ","
+ + "\"missRatio\":" + getMissRatio() + ","
+ + "\"shareability\":" + getShareability()
+ + "}";
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Thresholds.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Thresholds.java
new file mode 100644
index 0000000..895c0d3
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/extension/metrics/Thresholds.java
@@ -0,0 +1,218 @@
+/*
+ * 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.metrics;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author romulo
+ */
+public class Thresholds {
+
+ public static double sumMissRatio;
+ public static double sumHitRatio;
+ public static double sumExecutionTime;
+ public static double sumShareability;
+ public static double sumHitsPerTimeInCache;
+
+ public static Double averageHitsPerTimeInCache;
+
+ public static Double stdDevHitRatio;
+ public static Double stdDevMissRatio;
+ public static Double stdDevExecutionTimeRatio;
+ public static Double stdDevHitsPerTimeInCache;
+ public static Double stdDevShareability;
+
+ public static long population;
+
+ public static final List<Double> hitRatios = new ArrayList<>();
+ public static final List<Double> missRatios = new ArrayList<>();
+ public static final List<Long> executionTimes = new ArrayList<>();
+ public static final List<Double> shareabilities = new ArrayList<>();
+ public static final List<Double> hitsPerTimeInCache = new ArrayList<>();
+
+ public static void reset() {
+ Thresholds.sumMissRatio = 0;
+ Thresholds.sumHitRatio = 0;
+ Thresholds.sumExecutionTime = 0;
+ Thresholds.sumShareability = 0;
+ Thresholds.sumHitsPerTimeInCache = 0;
+
+ Thresholds.averageHitsPerTimeInCache = null;
+
+ Thresholds.stdDevHitRatio = null;
+ Thresholds.stdDevMissRatio = null;
+ Thresholds.stdDevExecutionTimeRatio = null;
+ Thresholds.stdDevHitsPerTimeInCache = null;
+ Thresholds.stdDevShareability = null;
+
+ Thresholds.population = 0;
+
+ Thresholds.hitRatios.clear();
+ Thresholds.missRatios.clear();
+ Thresholds.executionTimes.clear();
+ Thresholds.shareabilities.clear();
+ Thresholds.hitsPerTimeInCache.clear();
+ }
+
+ /**
+ * General mean hit ratio of all calls
+ *
+ * @return
+ */
+ public static double getAverageHitRatio() {
+ if (population == 0) {
+ return 0;
+ }
+ return new BigDecimal(sumHitRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public static double getAverageMissRatio() {
+ if (population == 0) {
+ return 0;
+ }
+ return new BigDecimal(sumMissRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public static double getAverageExecutionTime() {
+ if (population == 0) {
+ return 0;
+ }
+ return new BigDecimal(sumExecutionTime).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public static double getAverageShareability() {
+ if (population == 0) {
+ return 0;
+ }
+ return new BigDecimal(sumShareability).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ public static double getStdDevHitRatio() {
+ if (population == 0) {
+ return 0;
+ }
+ if (stdDevHitRatio != null) {
+ return stdDevHitRatio;
+ }
+
+ double mean = getAverageHitRatio();
+ double temp = hitRatios.stream()
+ .map(hitRate -> (hitRate - mean) * (hitRate - mean))
+ .reduce(Double::sum)
+ .orElse(0D);
+ stdDevHitRatio = Math.sqrt(temp / population);
+ return stdDevHitRatio;
+ }
+
+ public static double getStdDevMissRatio() {
+ if (population == 0) {
+ return 0;
+ }
+ if (stdDevMissRatio != null) {
+ return stdDevMissRatio;
+ }
+
+ double mean = getAverageMissRatio();
+ double temp = missRatios.stream().map(missRatio -> (missRatio - mean) * (missRatio - mean))
+ .reduce(Double::sum)
+ .orElse(0D);
+ stdDevMissRatio = Math.sqrt(temp / population);
+ return stdDevMissRatio;
+ }
+
+ public static double getStdDevExecutionTimeRatio() {
+ if (population == 0) {
+ return 0;
+ }
+ if (stdDevExecutionTimeRatio != null) {
+ return stdDevExecutionTimeRatio;
+ }
+
+ double mean = getAverageExecutionTime();
+ double temp = executionTimes.stream()
+ .map(executionTime -> (executionTime - mean) * (executionTime - mean))
+ .reduce(Double::sum)
+ .orElse(0D);
+ stdDevExecutionTimeRatio = Math.sqrt(temp / population);
+ return stdDevExecutionTimeRatio;
+ }
+
+ public static double getStdDevHitsPerTimeInCache() {
+ if (population == 0) {
+ return 0;
+ }
+ if (stdDevHitsPerTimeInCache != null) {
+ return stdDevHitsPerTimeInCache;
+ }
+
+ double mean = getAverageHitsPerTimeInCache();
+ double temp = hitsPerTimeInCache.stream()
+ .map(frequency -> (frequency - mean) * (frequency - mean))
+ .reduce(Double::sum)
+ .orElse(0D);
+ stdDevHitsPerTimeInCache = Math.sqrt(temp / population);
+ return stdDevHitsPerTimeInCache;
+ }
+
+ public static double getStdDevShareability() {
+ if (population == 0) {
+ return 0;
+ }
+ if (stdDevShareability != null) {
+ return stdDevShareability;
+ }
+
+ double mean = getAverageShareability();
+ double temp = shareabilities.stream()
+ .map(shareability -> (shareability - mean) * (shareability - mean))
+ .reduce(Double::sum)
+ .orElse(0D);
+ stdDevShareability = Math.sqrt(temp / population);
+ return stdDevShareability;
+ }
+
+ public static double getAverageHitsPerTimeInCache() {
+ if (hitsPerTimeInCache.isEmpty()) {
+ return 0;
+ }
+ if (averageHitsPerTimeInCache != null) {
+ return averageHitsPerTimeInCache;
+ }
+
+ averageHitsPerTimeInCache = new BigDecimal(sumHitsPerTimeInCache).divide(new BigDecimal(hitsPerTimeInCache.size()), 5, RoundingMode.HALF_UP).doubleValue();
+ return averageHitsPerTimeInCache;
+ }
+
+//getting X% with most hits
+ public static double hitThreshold(int kStdDev) {
+ return getAverageHitRatio() + (kStdDev * getStdDevHitRatio());
+ }
+
+//getting X% with most misses
+ public static double missThreshold(int kStdDev) {
+ return getAverageMissRatio() + (kStdDev * getStdDevMissRatio());
+ }
+
+//getting X% most expensive methods
+ public static double expensivenessThreshold(int kStdDev) {
+ return getAverageExecutionTime() + (kStdDev * getStdDevExecutionTimeRatio());
+ }
+
+ public static double shareabilityThreshold(int kStdDev) {
+ return getAverageShareability() + (kStdDev * getStdDevShareability());
+ }
+
+//getting X% most frenquent
+ public static double hitsPerTimeInCacheThreshold(int kStdDev) {
+ return getAverageHitsPerTimeInCache() + (kStdDev * getStdDevHitsPerTimeInCache());
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/Main.java b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/Main.java
new file mode 100755
index 0000000..3193e94
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/aplcachetf/Main.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import br.ufrgs.inf.prosoft.aplcachetf.adapter.TraceReader;
+import br.ufrgs.inf.prosoft.aplcachetf.extension.APLCache;
+import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.Method;
+import br.ufrgs.inf.prosoft.trace.Trace;
+import br.ufrgs.inf.prosoft.trace.reader.Mode;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author romulo
+ */
+public class Main {
+
+ private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
+
+ public static void main(String[] args) {
+ System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT+%1$tL] [%4$-7s] [APLCacheTF] %5$s %n");
+
+ if (args.length < 2) {
+ System.err.println("--trace=<TracePath> --output=<OutputPath> [--mode=<complete|hashed|partial>] [--k=<kStandardDeviation>] [--window=<windowSize>] [--shift=<shiftTime>]");
+ System.exit(1);
+ }
+
+ Map<String, String> arguments = Stream.of(args).map(arg -> {
+ arg = arg.replaceFirst("--", "");
+ int indexOf = arg.indexOf("=");
+ if (indexOf == -1) {
+ return new String[]{arg, ""};
+ }
+ return new String[]{arg.substring(0, indexOf), arg.substring(indexOf + 1)};
+ }).collect(Collectors.toMap(array -> {
+ return array[0];
+ }, array -> {
+ return array[1];
+ }));
+
+ String tracePath = arguments.get("trace");
+ if (tracePath == null) {
+ System.err.println("<TracePath> is required");
+ System.exit(1);
+ }
+ String outputPath = arguments.get("output");
+ if (outputPath == null) {
+ System.err.println("<OutputPath> is required");
+ System.exit(1);
+ }
+ String mode = arguments.get("mode");
+ if (mode == null) {
+ mode = "complete";
+ LOGGER.log(Level.INFO, "Using default mode: {0}", mode);
+ }
+ int k = 0;
+ String kStr = arguments.get("k");
+ if (kStr == null) {
+ LOGGER.log(Level.INFO, "Using default k: {0}", k);
+ } else {
+ try {
+ k = Integer.valueOf(kStr);
+ } catch (NumberFormatException ex) {
+ System.err.println("<k> must be a number");
+ System.exit(1);
+ }
+ }
+
+ Long window = null;
+ try {
+ window = Long.valueOf(arguments.get("window"));
+ } catch (NumberFormatException ex) {
+ }
+ Long shift = null;
+ try {
+ shift = Long.valueOf(arguments.get("shift"));
+ } catch (NumberFormatException ex) {
+ }
+
+ LOGGER.log(Level.INFO, "Reading traces");
+ List<Trace> traces = TraceReader.parseFile(tracePath, Mode.valueOf(mode.toUpperCase()), window, shift);
+ LOGGER.log(Level.INFO, "Grouping {0} traces by methods", traces.size());
+ List<Method> methods = TraceReader.groupByMethods(traces);
+ LOGGER.log(Level.INFO, "grouped traces into {0} methods", methods.size());
+
+ APLCache aplcache = new APLCache(methods);
+ aplcache.recommend(k, outputPath);
+ }
+}