tfcache

added configurations: staleness and kernel. replaced hitspertimeincache

9/1/2020 11:53:18 AM

Details

diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/configuration/Configuration.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/configuration/Configuration.java
new file mode 100644
index 0000000..10ddb8c
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/configuration/Configuration.java
@@ -0,0 +1,95 @@
+package br.ufrgs.inf.prosoft.tfcache.configuration;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author root
+ */
+public class Configuration {
+
+    private static final Logger LOGGER = Logger.getLogger(Configuration.class.getName());
+
+    private static String level = "method";
+    private static String output = null;
+    private static String changeability = "allow";
+    private static String staleness = "ignore";
+    private static String kernel = "exhaustive";
+
+    public static void setLevel(String level) {
+        if (level == null) {
+            LOGGER.log(Level.INFO, "Using default level: {0}", Configuration.level);
+            return;
+        }
+        if (!level.equals("method") && !level.equals("input")) {
+            LOGGER.log(Level.SEVERE, "Unrecognised option for level");
+            System.exit(1);
+        }
+        Configuration.level = level;
+    }
+
+    public static String getLevel() {
+        return level;
+    }
+
+    public static void setOutput(String output) {
+        if (output == null || output.isBlank()) {
+            return;
+        }
+        Configuration.output = output;
+    }
+
+    public static String getOutput() {
+        return output;
+    }
+
+    public static void setChangeability(String changeability) {
+        if (changeability == null) {
+            LOGGER.log(Level.INFO, "Using default changeability: {0}", Configuration.changeability);
+            return;
+        }
+        if (!changeability.equals("allow") && !changeability.equals("deny")) {
+            LOGGER.log(Level.SEVERE, "Unrecognised option for changeability");
+            System.exit(1);
+        }
+        Configuration.changeability = changeability;
+    }
+
+    public static String getChangeability() {
+        return changeability;
+    }
+
+    public static void setStaleness(String staleness) {
+        if (staleness == null) {
+            LOGGER.log(Level.INFO, "Using default staleness: {0}", Configuration.staleness);
+            return;
+        }
+        if (!staleness.equals("ignore") && !staleness.equals("shrink")) {
+            LOGGER.log(Level.SEVERE, "Unrecognised option for staleness");
+            System.exit(1);
+        }
+        Configuration.staleness = staleness;
+    }
+
+    public static String getStaleness() {
+        return staleness;
+    }
+
+    public static void setKernel(String kernel) {
+        if (kernel == null) {
+            LOGGER.log(Level.INFO, "Using default kernel: {0}", Configuration.kernel);
+            return;
+        }
+        if (!kernel.equals("exhaustive") && !kernel.equals("optimised")) {
+            LOGGER.log(Level.SEVERE, "Unrecognised option for kernel");
+            System.exit(1);
+        }
+        Configuration.kernel = kernel;
+    }
+
+    public static String getKernel() {
+        return kernel;
+    }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Main.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Main.java
index e33362d..f7ac970 100755
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Main.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Main.java
@@ -6,6 +6,7 @@
 package br.ufrgs.inf.prosoft.tfcache;
 
 import br.ufrgs.inf.prosoft.tfcache.adapter.TraceReader;
+import br.ufrgs.inf.prosoft.tfcache.configuration.Configuration;
 import br.ufrgs.inf.prosoft.tfcache.metadata.Method;
 import br.ufrgs.inf.prosoft.trace.Trace;
 import java.util.List;
@@ -27,9 +28,7 @@ public class Main {
         System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT+%1$tL] [%4$-7s] [TFCache] %5$s %n");
 
         if (args.length < 1) {
-            System.err.println("--trace=<TracePath>");
-            System.err.println("--trace=<TracePath> --level=<method|input> [--output=<outputPath>] [--changeability=<true|false>]");
-            System.err.println("--trace=<TracePath> --level=input --output=<outputPath>");
+            System.err.println("--trace=<TracePath> [--level=<method|input>] [--output=<outputPath>] [--changeability=<allow|deny>] [--staleness=<ignore|shrink>] [--kernel=<exhaustive|optimised>]");
             System.exit(1);
         }
 
@@ -47,35 +46,26 @@ public class Main {
         }));
 
         String tracePath = arguments.get("trace");
-        if (tracePath == null) {
+        if (tracePath == null || tracePath.isBlank()) {
             System.err.println("<TracePath> is required");
             System.exit(1);
         }
-        String level = arguments.get("level");
-        if (level == null) {
-            level = "method";
-            LOGGER.log(Level.INFO, "Using default level: {0}", level);
-        }
-        String changeability = arguments.get("changeability");
-        if (changeability == null) {
-            changeability = "false";
-            LOGGER.log(Level.INFO, "Using default changeability: {0}", changeability);
-        }
-        String output = arguments.get("output");
-        if (level.equals("input") && output == null) {
-            System.err.println("outputPath is required for input-level recommendations");
+
+        Configuration.setLevel(arguments.get("level"));
+        Configuration.setOutput(arguments.get("output"));
+        if (Configuration.getLevel().equals("input") && Configuration.getOutput() == null) {
+            LOGGER.log(Level.SEVERE, "Output is required for input-level recommendations");
             System.exit(1);
         }
+        Configuration.setChangeability(arguments.get("changeability"));
+        Configuration.setStaleness(arguments.get("staleness"));
+        Configuration.setKernel(arguments.get("kernel"));
 
         LOGGER.log(Level.INFO, "Reading traces");
         List<Trace> traces = TraceReader.parseFile(tracePath);
         LOGGER.log(Level.INFO, "Grouping {0} traces by methods", traces.size());
         List<Method> methods = TraceReader.groupByMethods(traces);
         TFCache tfCache = new TFCache(methods);
-        if (level.equals("input")) {
-            tfCache.recommend(changeability.equals("true"), output);
-        } else {
-            tfCache.recommend(changeability.equals("true"));
-        };
+        tfCache.recommend();
     }
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/GroupOfOccurrences.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/GroupOfOccurrences.java
index dfb2750..25ba662 100755
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/GroupOfOccurrences.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/GroupOfOccurrences.java
@@ -8,17 +8,11 @@ package br.ufrgs.inf.prosoft.tfcache.metadata;
 import br.ufrgs.inf.prosoft.tfcache.Metrics;
 import br.ufrgs.inf.prosoft.tfcache.Simulator;
 import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import java.util.stream.LongStream;
 import java.util.stream.Stream;
 
 /**
@@ -54,16 +48,24 @@ public class GroupOfOccurrences {
         return this.bestMetrics;
     }
 
-    public Long getTtl() {
+    public long getTtl() {
         return getBestMetrics().getTtl();
     }
 
-    public Long getSavedTime() {
+    public long getHits() {
+        return getBestMetrics().getHits();
+    }
+
+    public long getStales() {
+        return getBestMetrics().getStales();
+    }
+
+    public long getSavedTime() {
         return getBestMetrics().getSavedTime();
     }
 
-    public BigDecimal getHitsPerTimeInCache() {
-        return getBestMetrics().getHitsPerTimeInCache();
+    public BigDecimal getSavedTimePerTimeInCache() {
+        return getBestMetrics().getSavedTimePerTimeInCache();
     }
 
     public Stream<Occurrence> occurrences() {
@@ -89,23 +91,9 @@ public class GroupOfOccurrences {
         if (this.occurrences.size() < 2) {
             throw new RuntimeException("Not reusable input");
         }
+        this.occurrences.sort((occurrence1, occurrence2) -> Long.compare(occurrence1.getStartTime(), occurrence2.getStartTime()));
         this.bestMetrics = new Metrics();
-
-        Collection<Long> ttlsOfInterest = new HashSet<>();
-        for (int hits = 1; hits < this.occurrences.size(); hits++) {
-            for (int shift = 0; shift < this.occurrences.size() - hits; shift++) {
-                long ttl = 0;
-                for (int k = shift + 1; k < shift + 1 + hits; k++) {
-                    ttl += this.occurrences.get(k).getStartTime() - this.occurrences.get(k - 1).getEndTime();
-                }
-                if (ttl > 0) {
-                    ttlsOfInterest.add(ttl);
-                }
-            }
-        }
-        ttlsOfInterest.stream().parallel().forEach(actualTTL -> {
-            Simulator.simulate(occurrences(), actualTTL, this.bestMetrics);
-        });
+        Simulator.simulate(this.occurrences, this.bestMetrics);
     }
 
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/Method.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/Method.java
index bdfc484..29d3783 100755
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/Method.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/metadata/Method.java
@@ -9,9 +9,7 @@ import br.ufrgs.inf.prosoft.tfcache.Metrics;
 import br.ufrgs.inf.prosoft.tfcache.Simulator;
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -68,8 +66,8 @@ public class Method {
         return getBestMetrics().getSavedTime();
     }
 
-    public BigDecimal getHitsPerTimeInCache() {
-        return getBestMetrics().getHitsPerTimeInCache();
+    public BigDecimal getSavedTimePerTimeInCache() {
+        return getBestMetrics().getSavedTimePerTimeInCache();
     }
 
     public Stream<Occurrence> occurrences() {
@@ -115,7 +113,6 @@ public class Method {
         this.groupsOfOccurrences = inputHasOccurrences.entrySet().stream()
                 .map(entry -> new GroupOfOccurrences(entry.getKey(), entry.getValue()))
                 .collect(Collectors.toList());
-        this.occurrences = null;
     }
 
     public void removeChangeableInputs() {
@@ -172,7 +169,6 @@ public class Method {
                 .map(Occurrence::getParametersSerialised)
                 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
         return inputHasFrequency.values().stream().anyMatch(frequency -> frequency > 1);
-
     }
 
     public boolean isNotReusable() {
@@ -190,22 +186,8 @@ public class Method {
         if (this.occurrences.size() < 2) {
             return;
         }
-
-        Collection<Long> ttlsOfInterest = new HashSet<>();
-        for (int hits = 1; hits < this.occurrences.size(); hits++) {
-            for (int shift = 0; shift < this.occurrences.size() - hits; shift++) {
-                long ttl = 0;
-                for (int k = shift + 1; k < shift + 1 + hits; k++) {
-                    ttl += this.occurrences.get(k).getStartTime() - this.occurrences.get(k - 1).getEndTime();
-                }
-                if (ttl > 0) {
-                    ttlsOfInterest.add(ttl);
-                }
-            }
-        }
-        ttlsOfInterest.stream().parallel().forEach(actualTTL -> {
-            Simulator.simulate(occurrences(), actualTTL, this.bestMetrics);
-        });
+        this.occurrences.sort((occurrence1, occurrence2) -> Long.compare(occurrence1.getStartTime(), occurrence2.getStartTime()));
+        Simulator.simulate(this.occurrences, this.bestMetrics);
     }
 
     public void recommendTTLPerInput() {
@@ -213,13 +195,27 @@ public class Method {
             groupByInput();
             removeSingleOccurrences();
         }
-        groupsOfOccurrences().forEach(GroupOfOccurrences::calculateHitsPerTimeInCache);
+        groupsOfOccurrences().parallel().forEach(GroupOfOccurrences::calculateHitsPerTimeInCache);
         if (this.bestMetrics == null) {
             long savedTime = groupsOfOccurrences().map(GroupOfOccurrences::getSavedTime).reduce(Long::sum).get();
+            long hits = groupsOfOccurrences().map(GroupOfOccurrences::getHits).reduce(Long::sum).get();
+            long stales = groupsOfOccurrences().map(GroupOfOccurrences::getStales).reduce(Long::sum).get();
             GroupOfOccurrences max = groupsOfOccurrences().max((group1, group2) -> Long.compare(group1.getSavedTime(), group2.getSavedTime())).get();
             long ttl = max.getTtl();
-            BigDecimal hitsPerTimeInCache = max.getHitsPerTimeInCache();
-            this.bestMetrics = new Metrics(ttl, savedTime, hitsPerTimeInCache);
+            BigDecimal hitsPerTimeInCache = max.getSavedTimePerTimeInCache();
+            this.bestMetrics = new Metrics(ttl, hits, stales, savedTime, hitsPerTimeInCache);
+        }
+    }
+
+    public void removeNotRecommededInputs() {
+        if (this.groupsOfOccurrences == null || this.bestMetrics == null) {
+            throw new RuntimeException("Recommendations not called");
+        }
+        int initialCountofOccurrences = this.groupsOfOccurrences.size();
+        this.groupsOfOccurrences.removeIf(groupOfOccurrences -> groupOfOccurrences.getSavedTime() == 0);
+        int removedOccurrences = initialCountofOccurrences - this.groupsOfOccurrences.size();
+        if (removedOccurrences > 0) {
+            LOGGER.log(Level.INFO, "\tRemoved {0} of {1} inputs from method {2}", new Object[]{removedOccurrences, initialCountofOccurrences, this.name});
         }
     }
 
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Metrics.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Metrics.java
index 196d3bd..4f34d9f 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Metrics.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Metrics.java
@@ -21,61 +21,73 @@ import java.util.stream.Stream;
 public class Metrics implements Comparable<Metrics> {
 
     private long ttl;
+    private long hits;
+    private long stales;
     private long savedTime;
-    private BigDecimal hitsPerTimeInCache;
+    private BigDecimal savedTimePerTimeInCache;
 
     public Metrics() {
-        this.hitsPerTimeInCache = new BigDecimal(BigInteger.ZERO);
+        this.savedTimePerTimeInCache = new BigDecimal(BigInteger.ZERO);
     }
 
-    public Metrics(long ttl, long savedTime, BigDecimal hitsPerTimeInCache) {
+    public Metrics(long ttl, long hits, long stales, long savedTime, BigDecimal hitsPerTimeInCache) {
         this.ttl = ttl;
+        this.hits = hits;
+        this.stales = stales;
         this.savedTime = savedTime;
-        this.hitsPerTimeInCache = hitsPerTimeInCache;
+        this.savedTimePerTimeInCache = hitsPerTimeInCache;
     }
 
     public long getTtl() {
         return this.ttl;
     }
 
+    public long getHits() {
+        return this.hits;
+    }
+
+    public long getStales() {
+        return this.stales;
+    }
+
     public long getSavedTime() {
         return this.savedTime;
     }
 
-    public BigDecimal getHitsPerTimeInCache() {
-        return this.hitsPerTimeInCache;
+    public BigDecimal getSavedTimePerTimeInCache() {
+        return this.savedTimePerTimeInCache;
     }
 
-    public synchronized void keepBestMetrics(long ttl, long hits, long timeInCache, long savedTime) {
-        if (ttl < 0 || hits < 0 || timeInCache < 0 || savedTime < 0) {
-            throw new RuntimeException("Metrics cannot be under zero. TTL: " + ttl + " hits: " + hits + " timeInCache: " + timeInCache + " savedTime: " + savedTime);
+    public synchronized void keepBestMetrics(long ttl, long hits, long timeInCache, long stales, long savedTime) {
+        if (ttl < 0 || hits < 0 || timeInCache < 0 || stales < 0 || savedTime < 0) {
+            throw new RuntimeException("Metrics cannot be under zero. TTL: " + ttl
+                    + " hits: " + hits
+                    + " timeInCache: " + timeInCache
+                    + " stales: " + stales
+                    + " savedTime: " + savedTime);
         }
-        if (ttl == 0 || hits == 0) {
+        if (ttl == 0 || hits == 0 || savedTime == 0) {
             return;
         }
-        if (timeInCache == 0 || savedTime == 0) {
-            throw new RuntimeException("timeInCache and savedTime should not be zero");
+        if (timeInCache == 0) {
+            throw new RuntimeException("timeInCache should not be zero");
         }
-        BigDecimal hitsPerTimeInCache = new BigDecimal(hits).divide(new BigDecimal(timeInCache), MathContext.DECIMAL128);
-        if (this.ttl == 0
-                || this.hitsPerTimeInCache.compareTo(hitsPerTimeInCache) == -1
-                || (this.hitsPerTimeInCache == hitsPerTimeInCache && this.savedTime < savedTime)
-                || (this.hitsPerTimeInCache == hitsPerTimeInCache && this.savedTime == savedTime && this.ttl > ttl)) {
+        BigDecimal savedTimePerTimeInCache = new BigDecimal(savedTime).divide(new BigDecimal(timeInCache), MathContext.DECIMAL128);
+        if (this.ttl == 0 || compareTo(ttl, hits, stales, savedTime, savedTimePerTimeInCache) == -1) {
             this.ttl = ttl;
+            this.hits = hits;
+            this.stales = stales;
             this.savedTime = savedTime;
-            this.hitsPerTimeInCache = hitsPerTimeInCache;
+            this.savedTimePerTimeInCache = savedTimePerTimeInCache;
         }
     }
 
-    public static void removeDominatedMetrics(Collection<Metrics> metrics) {
-        Map<Long, List<Metrics>> groupBySavedTime = metrics.stream().collect(Collectors.groupingBy(Metrics::getSavedTime));
+    public static void removeDominatedMetrics(Collection<Metrics> allMetrics) {
+        Map<Long, List<Metrics>> groupBySavedTime = allMetrics.stream().collect(Collectors.groupingBy(Metrics::getSavedTime));
+        groupBySavedTime.remove(0L);
         groupBySavedTime.forEach((savedTime, value) -> {
-            if (savedTime == 0) {
-                value.clear();
-                return;
-            }
             Metrics max = value.stream().max(Metrics::compareTo).get();
-            value.removeIf(metric -> metric.getHitsPerTimeInCache().compareTo(max.getHitsPerTimeInCache()) == -1);
+            value.removeIf(metric -> metric.getSavedTimePerTimeInCache().compareTo(max.getSavedTimePerTimeInCache()) == -1);
         });
 
         List<Metrics> localMaxima = groupBySavedTime.entrySet().stream()
@@ -84,27 +96,43 @@ public class Metrics implements Comparable<Metrics> {
                 .orElse(Stream.empty())
                 .collect(Collectors.toList());
 
-        metrics.removeIf(metric -> !localMaxima.contains(metric));
+        allMetrics.removeIf(metrics -> !localMaxima.contains(metrics));
     }
 
     @Override
     public int compareTo(Metrics other) {
-        if (this.hitsPerTimeInCache.compareTo(other.hitsPerTimeInCache) == 1) {
+        return compareTo(other.ttl, other.hits, other.stales, other.savedTime, other.savedTimePerTimeInCache);
+    }
+
+    private int compareTo(long ttl, long hits, long stales, long savedTime, BigDecimal savedTimePerTimeInCache) {
+        if (this.savedTimePerTimeInCache.compareTo(savedTimePerTimeInCache) == 1) {
             return 1;
         }
-        if (this.hitsPerTimeInCache.compareTo(other.hitsPerTimeInCache) == -1) {
+        if (this.savedTimePerTimeInCache.compareTo(savedTimePerTimeInCache) == -1) {
             return -1;
         }
-        if (this.savedTime > other.savedTime) {
+        if (this.stales < stales) {
             return 1;
         }
-        if (this.savedTime < other.savedTime) {
+        if (this.stales > stales) {
             return -1;
         }
-        if (this.ttl < other.ttl) {
+        if (this.savedTime > savedTime) {
             return 1;
         }
-        if (this.ttl > other.ttl) {
+        if (this.savedTime < savedTime) {
+            return -1;
+        }
+        if (this.hits > hits) {
+            return 1;
+        }
+        if (this.hits < hits) {
+            return -1;
+        }
+        if (this.ttl < ttl) {
+            return 1;
+        }
+        if (this.ttl > ttl) {
             return -1;
         }
         return 0;
@@ -126,13 +154,12 @@ public class Metrics implements Comparable<Metrics> {
     }
 
     @Override
-    public Metrics clone() {
-        return new Metrics(this.ttl, this.savedTime, this.hitsPerTimeInCache);
-    }
-
-    @Override
     public String toString() {
-        return "TTL: " + this.ttl + " HpTiC: " + this.hitsPerTimeInCache + " SavedTime: " + this.savedTime;
+        return "TTL: " + this.ttl
+                + " STpTiC: " + this.savedTimePerTimeInCache
+                + " Stales: " + this.stales
+                + " Hits: " + this.hits
+                + " SavedTime: " + this.savedTime;
     }
 
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Simulator.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Simulator.java
index a573181..bdc912b 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/Simulator.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/Simulator.java
@@ -5,10 +5,18 @@
  */
 package br.ufrgs.inf.prosoft.tfcache;
 
+import br.ufrgs.inf.prosoft.tfcache.configuration.Configuration;
 import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
 import java.util.stream.Stream;
 
 /**
@@ -31,30 +39,99 @@ public class Simulator {
         simulate(occurrences, ttl, this.metrics);
     }
 
+    public static void simulate(List<Occurrence> occurrences, Metrics metrics) {
+        if (Configuration.getKernel().equals("exhaustive")) {
+            simulate(occurrences, generateAllTTLs(occurrences), metrics);
+        } else {
+            simulate(occurrences, generateTTLsOfInterest(occurrences), metrics);
+        }
+    }
+
+    public static void simulate(List<Occurrence> occurrences, Stream<Long> ttls, Metrics metrics) {
+        ttls.forEach(actualTTL -> {
+            simulate(occurrences.stream(), actualTTL, metrics);
+        });
+    }
+
     public static void simulate(Stream<Occurrence> occurrences, long ttl, Metrics metrics) {
         Map<String, Long> inputHasCachedTime = new HashMap<>();
+        Map<String, Object> inputHasOutput = new HashMap<>();
 
-        long actualSavedTime = 0;
-        long actualHits = 0;
-        long actualTimeInCache = 0;
+        long simulationSavedTime = 0;
+        long realSavedTime = 0;
+        long hits = 0;
+        long timeInCache = 0;
+        long stales = 0;
 
         Iterator<Occurrence> iterator = occurrences.iterator();
         while (iterator.hasNext()) {
             Occurrence occurrence = iterator.next();
-            long adjustedStartTime = occurrence.getStartTime() - actualSavedTime;
-            long adjustedEndTime = occurrence.getEndTime() - actualSavedTime;
-            if (inputHasCachedTime.containsKey(occurrence.getParametersSerialised()) && adjustedStartTime - inputHasCachedTime.get(occurrence.getParametersSerialised()) > ttl) {
-                inputHasCachedTime.remove(occurrence.getParametersSerialised());
-            }
+            long adjustedStartTime = occurrence.getStartTime() - simulationSavedTime;
+            long adjustedEndTime = occurrence.getEndTime() - simulationSavedTime;
             if (inputHasCachedTime.containsKey(occurrence.getParametersSerialised())) {
-                actualSavedTime += occurrence.getExecutionTime();
-                actualHits++;
+                if (adjustedStartTime - inputHasCachedTime.get(occurrence.getParametersSerialised()) > ttl) {
+                    inputHasCachedTime.remove(occurrence.getParametersSerialised());
+                }
+                if (Configuration.getStaleness().equals("shrink")) {
+                    if (Objects.deepEquals(inputHasOutput.get(occurrence.getParametersSerialised()), occurrence.getReturnValue())) {
+                        realSavedTime += occurrence.getExecutionTime();
+                    } else {
+                        stales++;
+                    }
+                }
+                simulationSavedTime += occurrence.getExecutionTime();
+                hits++;
             } else {
                 inputHasCachedTime.put(occurrence.getParametersSerialised(), adjustedEndTime);
-                actualTimeInCache += ttl;
+                if (Configuration.getStaleness().equals("shrink")) {
+                    inputHasOutput.put(occurrence.getParametersSerialised(), occurrence.getReturnValue());
+                }
+                timeInCache += ttl;
+            }
+        }
+        if (Configuration.getStaleness().equals("shrink")) {
+            metrics.keepBestMetrics(ttl, hits, timeInCache, stales, realSavedTime);
+        } else {
+            metrics.keepBestMetrics(ttl, hits, timeInCache, stales, simulationSavedTime);
+        }
+    }
+
+    public static Stream<Long> generateTTLsOfInterest(Stream<Occurrence> occurrences) {
+        return generateTTLsOfInterest(occurrences.collect(Collectors.toList()));
+    }
+
+    public static Stream<Long> generateTTLsOfInterest(List<Occurrence> occurrences) {
+        List<Long> windows = new ArrayList<>();
+        for (int i = 1; i < occurrences.size(); i++) {
+            long window = occurrences.get(i).getStartTime() - occurrences.get(i - 1).getEndTime();
+            if (window > 0) {
+                windows.add(window);
+            }
+        }
+
+        Set<Long> ttlsOfInterest = new HashSet<>(windows);
+        for (int hits = 2; hits <= windows.size(); hits++) {
+            for (int shift = 0; shift <= windows.size() - hits; shift++) {
+                long ttl = 0;
+                for (int k = shift; k < shift + hits; k++) {
+                    ttl += windows.get(k);
+                }
+                ttlsOfInterest.add(ttl);
+            }
+        }
+        return ttlsOfInterest.stream().parallel();
+    }
+
+    public static Stream<Long> generateAllTTLs(List<Occurrence> occurrences) {
+        long maxTTL = occurrences.get(occurrences.size() - 1).getStartTime() - occurrences.get(0).getEndTime();
+        long minTTL = Long.MAX_VALUE;
+        for (int i = 0; i < occurrences.size() - 1; i++) {
+            long ttl = occurrences.get(i + 1).getStartTime() - occurrences.get(i).getEndTime();
+            if (ttl > 0 && ttl < minTTL) {
+                minTTL = ttl;
             }
         }
-        metrics.keepBestMetrics(ttl, actualHits, actualTimeInCache, actualSavedTime);
+        return LongStream.rangeClosed(minTTL, maxTTL).boxed().parallel();
     }
 
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/tfcache/TFCache.java b/src/main/java/br/ufrgs/inf/prosoft/tfcache/TFCache.java
index b1a591b..c3b4b39 100755
--- a/src/main/java/br/ufrgs/inf/prosoft/tfcache/TFCache.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/tfcache/TFCache.java
@@ -5,6 +5,7 @@
  */
 package br.ufrgs.inf.prosoft.tfcache;
 
+import br.ufrgs.inf.prosoft.tfcache.configuration.Configuration;
 import br.ufrgs.inf.prosoft.tfcache.metadata.Method;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -33,25 +34,34 @@ public class TFCache {
         return this.methods.stream();
     }
 
-    private void recommendCacheableMethods(boolean changeable) {
-        if (!changeable) {
+    private void recommendCacheableMethods() {
+        if (Configuration.getChangeability().equals("deny")) {
             LOGGER.log(Level.INFO, "Removing changeable from {0} methods", this.methods.size());
             this.methods.removeIf(Method::isChangeable);
         }
         LOGGER.log(Level.INFO, "Removing not reusable from {0} methods", this.methods.size());
         this.methods.removeIf(Method::isNotReusable);
         LOGGER.log(Level.INFO, "Recommending TTL to {0} methods", this.methods.size());
-        this.methods.forEach(Method::recommendTTL);
+        this.methods.stream().parallel().forEach(Method::recommendTTL);
+        LOGGER.log(Level.INFO, "Removing not recommended from {0}", this.methods.size());
+        this.methods.removeIf(method -> method.getSavedTime() == 0);
         LOGGER.log(Level.INFO, "Ranking {0} methods according saved time", this.methods.size());
         this.methods.sort((method1, method2) -> Long.compare(method2.getSavedTime(), method1.getSavedTime()));
         LOGGER.log(Level.INFO, "Printing recommendations for {0} methods", this.methods.size());
         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());
+            System.out.println(method.getName()
+                    + " Occurrences " + method.getOccurrencesSize()
+                    + " Inputs " + method.groupsOfOccurrences().count()
+                    + " TTL " + method.getTtl()
+                    + " STpTiC " + method.getSavedTimePerTimeInCache()
+                    + " Saves " + method.getSavedTime()
+                    + " Hits " + method.getBestMetrics().getHits()
+                    + " Stales " + method.getBestMetrics().getStales());
         });
     }
 
-    private void recommendCacheableInputs(boolean changeable, String outputPath) {
-        if (!changeable) {
+    private void recommendCacheableInputs() {
+        if (Configuration.getChangeability().equals("deny")) {
             LOGGER.log(Level.INFO, "Removing changeable inputs from {0} methods", this.methods.size());
             this.methods.forEach(Method::removeChangeableInputs);
         }
@@ -60,18 +70,35 @@ public class TFCache {
         LOGGER.log(Level.INFO, "Removing not reusable methods from {0} methods", this.methods.size());
         this.methods.removeIf(method -> method.groupsOfOccurrences().count() < 1);
         LOGGER.log(Level.INFO, "Recommending TTL to {0} methods", this.methods.size());
-        this.methods.forEach(Method::recommendTTLPerInput);
+        this.methods.stream().parallel().forEach(Method::recommendTTLPerInput);
+        LOGGER.log(Level.INFO, "Removing not recommended inputs from {0}", this.methods.size());
+        this.methods.forEach(Method::removeNotRecommededInputs);
+        LOGGER.log(Level.INFO, "Removing not recommended methods from {0}", this.methods.size());
+        this.methods.removeIf(method -> method.groupsOfOccurrences().count() < 1);
         LOGGER.log(Level.INFO, "Ranking {0} methods and inputs according saved time", this.methods.size());
         this.methods.forEach(Method::rankRecommendations);
         this.methods.sort((method1, method2) -> Long.compare(method2.getSavedTime(), method1.getSavedTime()));
         LOGGER.log(Level.INFO, "Printing recommendations for {0} methods", this.methods.size());
         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());
+            System.out.println(method.getName()
+                    + " Occurrences " + method.getOccurrencesSize()
+                    + " Inputs " + method.groupsOfOccurrences().count()
+                    + " TTL " + method.getTtl()
+                    + " STpTiC " + method.getSavedTimePerTimeInCache()
+                    + " Saves " + method.getSavedTime()
+                    + " Hits " + method.getBestMetrics().getHits()
+                    + " Stales " + method.getBestMetrics().getStales());
             method.groupsOfOccurrences().forEach(group -> {
-                System.out.println("\t" + group.getParameters().hashCode() + " Occurrences " + group.getOccurrencesSize() + " -> TTL " + group.getTtl() + " HpTiC " + group.getHitsPerTimeInCache() + " Saves " + group.getSavedTime());
+                System.out.println("\t" + group.getParameters().hashCode()
+                        + " Occurrences " + group.getOccurrencesSize()
+                        + " -> TTL " + group.getTtl()
+                        + " STpTiC " + group.getSavedTimePerTimeInCache()
+                        + " Saves " + group.getSavedTime()
+                        + " Hits " + group.getBestMetrics().getHits()
+                        + " Stales " + group.getBestMetrics().getHits());
             });
         });
-        try (FileWriter fileWriter = new FileWriter(outputPath)) {
+        try (FileWriter fileWriter = new FileWriter(Configuration.getOutput())) {
             JsonObject jsonCacheableParameters = new JsonObject();
             this.methods.forEach(method -> {
                 JsonObject cacheableParameters = new JsonObject();
@@ -88,22 +115,10 @@ public class TFCache {
     }
 
     public void recommend() {
-        recommend(false);
-    }
-
-    public void recommend(String outputPath) {
-        recommend(false, outputPath);
-    }
-
-    public void recommend(boolean changeable) {
-        recommendCacheableMethods(changeable);
-    }
-
-    public void recommend(boolean changeable, String outputPath) {
-        if (outputPath == null) {
-            recommend(changeable);
+        if (Configuration.getLevel().equals("method")) {
+            recommendCacheableMethods();
         } else {
-            recommendCacheableInputs(changeable, outputPath);
+            recommendCacheableInputs();
         }
     }