aplcache

removed n*n comparation, grouped by methods. separated thresholds.

12/5/2018 10:21:53 AM

Changes

pom.xml 5(+0 -5)

src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/CacheDecider.java 10(+0 -10)

src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodEntry.java 60(+0 -60)

src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/LogTrace.java 95(+0 -95)

src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/MethodInfoConcrete.java 24(+0 -24)

Details

pom.xml 5(+0 -5)

diff --git a/pom.xml b/pom.xml
index bbae100..ab23205 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,11 +16,6 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <version>1.7.21</version>
-        </dependency>
-        <dependency>
             <groupId>org.ehcache</groupId>
             <artifactId>sizeof</artifactId>
             <version>0.3.0</version>
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java
index c796197..497dc70 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java
@@ -1,362 +1,75 @@
 package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart;
 
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.CacheDecider;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodEntry;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.CacheabilityMetrics;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.CacheabilityPatternDecider;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.Thresholds;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.Method;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.logging.Level;
-import java.util.stream.Collectors;
-
-public class FlowchartWorkFlow {
-
-    Logger logger = LoggerFactory.getLogger(FlowchartWorkFlow.class);
-
-    private final long population;
-    protected HashMap<MethodInfo, MethodStats> methodsInfoMap;
-
-    private CacheDecider decider;
-
-    private double sumMissRatio;
-    private double sumHitRatio;
-    private double sumExecutionTime;
-    private double sumShareability;
-    private double sumFrequency;
-    private List<Double> hitRatios = new ArrayList<>();
-    private List<Double> missRatios = new ArrayList<>();
-    private List<Long> executionTimes = new ArrayList<>();
-    private List<Double> shareabilities = new ArrayList<>();
-    private List<Long> frequencies = new ArrayList<>();
-
-    public FlowchartWorkFlow(List<LogTrace> logList) {
-        this.decider = new CacheabilityPatternDecider(logList.size(), this);
-        this.methodsInfoMap = countOccurrences(logList);
-        this.population = logList.size();
-
-        logger.debug(methodsInfoMap.size() + " unique method calls identified from " + logList.size() + " original traces");
-
-        logger.debug("Average ExecutionTime: " + getAverageExecutionTime());
-        logger.debug("Average HitRatio: " + getAverageHitRatio());
-        logger.debug("Average MissRatio: " + getAverageMissRatio());
-        logger.debug("Average shareability: " + getAverageShareability());
-        logger.debug("StdDv ExecutionTime: " + getStdDevExecutionTimeRatio());
-        logger.debug("StdDv HitRatio: " + getStdDevHitRatio());
-        logger.debug("StdDv MissRatio: " + getStdDevMissRatio());
-        logger.debug("StdDv shareability: " + getStdDevShareability());
-        logger.debug("StdDv frequency: " + getStdDevFrequency());
-
-        int k = 0;
-        logger.debug("Using " + k + " stdDev to calculate thresholds...");
-        logger.debug("Threshold ExecutionTime: " + expensivenessThreshold(k));
-        logger.debug("Threshold HitRatio: " + hitThreshold(k));
-        logger.debug("Threshold MissRatio: " + missThreshold(k));
-        logger.debug("Threshold Shareability: " + shareabilityThreshold(k));
-        logger.debug("Threshold frequency: " + frequencyThreshold(k));
-    }
-
-    public Set<MethodEntry> filterCacheableMethods(long expiryTime) {
-        logger.debug("Deciding if methods are cacheable...");
-
-        Set<MethodEntry> cacheableMethods = getMethodsInfoMap().keySet().stream()
-                .filter(mi -> decider.isCacheable(mi, getMethodsInfoMap().get(mi)))
-                .map(mi -> new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime))
-                .collect(Collectors.toSet());
-
-        logger.info(cacheableMethods.size() + " cacheable methods detected. Printing files...");
-
-        //TODO remove: print all unique methods and metrics to csv file
-        try (PrintWriter pw = new PrintWriter(new File("allmethods.csv"))) {
-            pw.write("isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive,signature,numberOfSameOccurrences,numberOfDifferentReturnOccurrences,totalOccurrences,sameOccurrencesAverageExecutionTime,sameOccurrencesTotalExecutionTime,hitRatio,missRatio\n");
-            getMethodsInfoMap().keySet().stream().forEach(mi -> pw.write(CacheabilityMetrics.allMetricsToString(mi, getMethodsInfoMap().get(mi), this, getMethodsInfoMap().size()) + "," + new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime).getMethodInfo().getSignature() + "," + new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime).getMethodStats().toCSV() + '\n'));
-        } catch (FileNotFoundException ex) {
-        }
-
-        //TODO remove: print cacheable methods to csv file
-        try (PrintWriter pw = new PrintWriter(new File("cacheablemethods.csv"))) {
-            pw.write("isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive,signature,numberOfSameOccurrences,numberOfDifferentReturnOccurrences,totalOccurrences,sameOccurrencesAverageExecutionTime,sameOccurrencesTotalExecutionTime,hitRatio,missRatio\n");
-            cacheableMethods.stream().forEach(ma -> pw.write(CacheabilityMetrics.allMetricsToString(ma.getMethodInfo(), ma.getMethodStats(), this, getMethodsInfoMap().size()) + "," + ma.getMethodInfo().getSignature() + "," + ma.getMethodStats().toCSV() + '\n'));
-        } catch (FileNotFoundException ex) {
-        }
-        return cacheableMethods;
-    }
-
-    public HashMap<MethodInfo, MethodStats> countOccurrences(List<LogTrace> logs) {
-        logger.info("Counting {0} occurrences", logs.size());
-        sumExecutionTime = 0;
-        sumHitRatio = 0;
-        sumMissRatio = 0;
-        sumShareability = 0;
-        sumFrequency = 0;
-
-        HashMap<MethodInfo, MethodStats> methodInfoMap = new HashMap<>();
-
-        for (int i = 0; i < logs.size(); i++) {
-            LogTrace logTrace = logs.get(i);
-            System.out.print(".");
-            System.out.flush();
-            if (i != 0 && i % 100 == 0) {
-                System.out.println();
-            }
-            if (methodInfoMap.containsKey(logTrace.getMethodInfo())) {
-                continue;
-            }
-
-            MethodStats methodStats = new MethodStats(logTrace);
-
-            for (int j = 0; j < logs.size(); j++) {
-                LogTrace traceCompare = logs.get(j);
-
-                if (i == j) {
-                    continue;
-                }
-
-                //if similar methods: same signature and params, different return
-                if (traceCompare.getMethodInfo().equalsWithoutReturnedValue(logTrace.getMethodInfo())) {
-                    //if identical methods
-                    if (EqualsBuilder.reflectionEquals(traceCompare.getMethodInfo().getReturnedValue(), logTrace.getMethodInfo().getReturnedValue())) {
-                        methodStats.addSameOccurrence(traceCompare);
-                    } else {
-                        methodStats.addDifferentReturnOccurrence();
-                    }
-                }
-            }
-
-            methodInfoMap.put(logTrace.getMethodInfo(), methodStats);
-
-            sumExecutionTime += methodStats.getSameOccurrencesTotalExecutionTime();
-            executionTimes.add(methodStats.getSameOccurrencesTotalExecutionTime());
-
-            sumHitRatio += methodStats.hitRatio();
-            hitRatios.add(methodStats.hitRatio());
-
-            sumMissRatio += methodStats.missRatio();
-            missRatios.add(methodStats.missRatio());
-
-            sumShareability += methodStats.shareability();
-            shareabilities.add(methodStats.shareability());
-
-            sumFrequency += methodStats.getNumberOfSameOccurrences();
-            frequencies.add(methodStats.getNumberOfSameOccurrences());
-        }
-
-        return methodInfoMap;
-    }
-
-    /**
-     * General mean hit ratio of all calls
-     *
-     * @return
-     */
-    public double getAverageHitRatio() {
-        if (population == 0) {
-            return 0;
-        }
-        return new BigDecimal(sumHitRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
-    }
-
-    public double getAverageMissRatio() {
-        if (population == 0) {
-            return 0;
-        }
-        return new BigDecimal(sumMissRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
-    }
-
-    public double getAverageExecutionTime() {
-        if (population == 0) {
-            return 0;
-        }
-        return new BigDecimal(sumExecutionTime).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
-    }
-
-    public double getAverageShareability() {
-        if (population == 0) {
-            return 0;
-        }
-        return new BigDecimal(sumShareability).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
-    }
-
-    private Double StdDevHitRatio;
-
-    public double getStdDevHitRatio() {
-        if (population == 0) {
-            return 0;
-        }
-        if (StdDevHitRatio != null) {
-            return StdDevHitRatio;
-        }
+import java.util.logging.Logger;
 
-        double mean = getAverageHitRatio();
-        double temp = 0;
-        for (double a : hitRatios) {
-            temp += (a - mean) * (a - mean);
-        }
-        StdDevHitRatio = Math.sqrt(temp / population);
-        return StdDevHitRatio;
-    }
+public final class FlowchartWorkFlow {
 
-    private Double StdDevMissRatio;
+    private static final Logger logger = Logger.getLogger(FlowchartWorkFlow.class.getName());
 
-    public double getStdDevMissRatio() {
-        if (population == 0) {
-            return 0;
-        }
-        if (StdDevMissRatio != null) {
-            return StdDevMissRatio;
-        }
+    private Collection<Method> methods;
 
-        double mean = getAverageMissRatio();
-        double temp = 0;
-        for (double a : missRatios) {
-            temp += (a - mean) * (a - mean);
-        }
-        StdDevMissRatio = Math.sqrt(temp / population);
-        return StdDevMissRatio;
+    public FlowchartWorkFlow setMethods(Collection<Method> methods) {
+        this.methods = methods;
+        return this;
     }
 
-    private Double StdDevExecutionTimeRatio;
-
-    public 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).get();
-        StdDevExecutionTimeRatio = Math.sqrt(temp / population);
-        return StdDevExecutionTimeRatio;
+    private void countStats() {
+        logger.log(Level.INFO, "Counting stats of {0} methods", this.methods.size());
+        this.methods.stream().parallel().forEach(Method::countStats);
     }
 
-    private Double StdDevFrequency;
-
-    public double getStdDevFrequency() {
-        if (population == 0) {
-            return 0;
-        }
-        if (StdDevFrequency != null) {
-            return StdDevFrequency;
-        }
-
-        double mean = getAverageFrequency();
-        double temp = frequencies.stream()
-                .map(frequency -> (frequency - mean) * (frequency - mean))
-                .reduce(Double::sum).get();
-        StdDevFrequency = Math.sqrt(temp / population);
-        return StdDevFrequency;
-    }
+    private void calculateThresholds() {
+        logger.log(Level.INFO, "Calculating thresholds");
+        Thresholds.reset();
+        Thresholds.population = getPopulation();
+        this.methods.stream().forEach(Method::calculateThresholds);
 
-    private Double StdDevShareability;
+        logger.log(Level.INFO, "Average ExecutionTime: {0}", Thresholds.getAverageExecutionTime());
+        logger.log(Level.INFO, "Average HitRatio: {0}", Thresholds.getAverageHitRatio());
+        logger.log(Level.INFO, "Average MissRatio: {0}", Thresholds.getAverageMissRatio());
+        logger.log(Level.INFO, "Average shareability: {0}", Thresholds.getAverageShareability());
+        logger.log(Level.INFO, "StdDv ExecutionTime: {0}", Thresholds.getStdDevExecutionTimeRatio());
+        logger.log(Level.INFO, "StdDv HitRatio: {0}", Thresholds.getStdDevHitRatio());
+        logger.log(Level.INFO, "StdDv MissRatio: {0}", Thresholds.getStdDevMissRatio());
+        logger.log(Level.INFO, "StdDv shareability: {0}", Thresholds.getStdDevShareability());
+        logger.log(Level.INFO, "StdDv frequency: {0}", Thresholds.getStdDevFrequency());
 
-    public 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).get();
-        StdDevShareability = Math.sqrt(temp / population);
-        return StdDevShareability;
-    }
-
-    public HashMap<MethodInfo, MethodStats> getMethodsInfoMap() {
-        return methodsInfoMap;
-    }
-
-    //getting X% with most hits
-    public double hitThreshold(int kStdDev) {
-        return getAverageHitRatio() + (kStdDev * getStdDevHitRatio());
-    }
-
-    //getting X% with most misses
-    public double missThreshold(int kStdDev) {
-        return getAverageMissRatio() + (kStdDev * getStdDevMissRatio());
-    }
-
-    //getting X% most expensive methods
-    public double expensivenessThreshold(int kStdDev) {
-        return getAverageExecutionTime() + (kStdDev * getStdDevExecutionTimeRatio());
-    }
-
-    public double shareabilityThreshold(int kStdDev) {
-        return getAverageShareability() + (kStdDev * getStdDevShareability());
-    }
-
-    //getting X% most frenquent
-    public double frequencyThreshold(int kStdDev) {
-        return getAverageFrequency() + (kStdDev * getStdDevFrequency());
-    }
-
-    /**
-     * General miss ratio from a signature
-     *
-     * @param signature
-     * @return
-     */
-    public double getMissRatio(String signature) {
-        long occurrences = 0;
-        long methods = 0;
-        for (MethodInfo mi : methodsInfoMap.keySet()) {
-            if (mi.getSignature().equals(signature)) {
-                occurrences += methodsInfoMap.get(mi).getNumberOfSameOccurrences();
-                methods++;
+        int k = 0;
+        logger.log(Level.INFO, "Using {0} stdDev to calculate thresholds...", k);
+        logger.log(Level.INFO, "Threshold ExecutionTime: {0}", Thresholds.expensivenessThreshold(k));
+        logger.log(Level.INFO, "Threshold HitRatio: {0}", Thresholds.hitThreshold(k));
+        logger.log(Level.INFO, "Threshold MissRatio: {0}", Thresholds.missThreshold(k));
+        logger.log(Level.INFO, "Threshold Shareability: {0}", Thresholds.shareabilityThreshold(k));
+        logger.log(Level.INFO, "Threshold frequency: {0}", Thresholds.frequencyThreshold(k));
+    }
+
+    public void filterCacheableInputs() {
+        countStats();
+        calculateThresholds();
+
+        logger.log(Level.INFO, "Deciding if methods are cacheable...");
+
+        Iterator<Method> iterator = this.methods.iterator();
+        while (iterator.hasNext()) {
+            Method method = iterator.next();
+            method.filterCacheableInputs();
+            if (method.getGroupsOfOccurrences().isEmpty()) {
+                iterator.remove();
             }
         }
-        return occurrences / methods;
-    }
 
-    /**
-     * General hit ratio from a signature
-     *
-     * @param signature
-     * @return
-     */
-    public double getHitRatio(String signature) {
-        long occurrences = 0;
-        long methods = 0;
-        for (MethodInfo mi : methodsInfoMap.keySet()) {
-            if (mi.getSignature().equals(signature)) {
-                occurrences += methodsInfoMap.get(mi).getNumberOfSameOccurrences();
-                methods++;
-            }
-        }
-        return occurrences / methods;
+        logger.log(Level.INFO, "{0} cacheable methods detected", this.methods.size());
     }
 
-    private Double AverageFrequency;
-
-    public double getAverageFrequency() {
-        if (frequencies.isEmpty()) {
-            return 0;
-        }
-        if (AverageFrequency != null) {
-            return AverageFrequency;
-        }
-
-        AverageFrequency = new BigDecimal(sumFrequency).divide(new BigDecimal(frequencies.size()), 5, RoundingMode.HALF_UP).doubleValue();
-        return AverageFrequency;
+    private long getPopulation() {
+        return this.methods.stream().parallel()
+                .map(Method::getOccurrencesSize)
+                .reduce(Integer::sum)
+                .get();
     }
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java
index c90f88b..47822bf 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java
@@ -1,124 +1,57 @@
 package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats;
 
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.GroupOfOccurrences;
 import java.util.Optional;
 
 public class CacheabilityMetrics {
 
-    //total da população
-    public static long sampleSize(long N, double Z, double e) {
-        //Nível de confiança 90% -> Z=1.645
-        //Nível de confiança 95% -> Z=1.96
-        //Nível de confiança 99% -> Z=2.575
-        //Qual o nível de certeza que precisa ter de que a amostra retrata com precisão a sua população?
-        //double Z = 2.575;
-
-        //e = É a margem de erro máximo que eu quero admitir (p.e. 5%)
-        //Qual o nível de certeza que precisa ter de que os traces refletem as execuções da sua aplicação?
-        //double e = 0.03;
-        //p = É a proporção que esperamos encontrar. Este parâmetro tende confundir bastante à primeira vista:
-        double p = 0.5;
-
-        long n = (long) ((N * Math.pow(Z, 2) * p * (1 - p))
-                / ((N - 1) * Math.pow(e, 2) + Math.pow(Z, 2) * p * (1 - p)));
-
-        //simplified without the population
-        //int nn = (int) ((Math.pow(Z,2) * p * (1-p)) / Math.pow(e,2));
-        return n;
-    }
-
-    public static Optional<Boolean> isStaticData(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
-
-        //executions of a method should represent a good portion of total logs in order to avoid 1 occur == 100% hit
-//        if (!(methodStats.getNumberOfSameOccurrences() >= sampleSize(methodStats.getNumberOfOccurrences(), 1.645, 0.05)
-//                && methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05))) {
-//            return Optional.empty();
-//        }
-        if (methodStats.getNumberOfSameOccurrences() < workflow.frequencyThreshold(0)) {
+    public static Optional<Boolean> isStaticData(GroupOfOccurrences groupOfOccurrences) {
+        if (groupOfOccurrences.getStats().getNumberOfSameOccurrences() < Thresholds.frequencyThreshold(0)) {
             return Optional.empty();
         }
-        if (methodStats.hitRatio() == 100.0) {
+        if (groupOfOccurrences.getStats().getHitRatio() == 100.0) {
             return Optional.of(true);
         } else {
             return Optional.of(false);
         }
     }
 
-    public static Optional<Boolean> changeMoreThanUsed(MethodStats methodStats, FlowchartWorkFlow workflow) {
+    public static Optional<Boolean> changeMoreThanUsed(GroupOfOccurrences groupOfOccurrences) {
         //+/- k sds
-        if (methodStats.missRatio() > workflow.missThreshold(0)) {
+        if (groupOfOccurrences.getStats().getMissRatio() > Thresholds.missThreshold(0)) {
             return Optional.of(true);
         } else {
             return Optional.of(false);
         }
     }
 
-    public static Optional<Boolean> usedByManyRequests(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
-
-        //same executions of a method should represent a good portion of total execution of such method
-        if (methodStats.getNumberOfSameOccurrences() >= workflow.frequencyThreshold(0)) {
-//        if (methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05)) {
+    public static Optional<Boolean> usedByManyRequests(GroupOfOccurrences groupOfOccurrences) {
+        if (groupOfOccurrences.getStats().getNumberOfSameOccurrences() >= Thresholds.frequencyThreshold(0)) {
             return Optional.of(true);
         } else {
             return Optional.of(false);
         }
     }
 
-    public static Optional<Boolean> isUserSpecific(MethodStats methodStats, FlowchartWorkFlow workflow) {
-
-        if (methodStats.getAmountOfIdentifiedSameOccurences() == 0) {
+    public static Optional<Boolean> isUserSpecific(GroupOfOccurrences groupOfOccurrences) {
+        if (groupOfOccurrences.getStats().getAmountOfIdentifiedSameOccurences() == 0) {
             return Optional.empty();
         }
 
         //the less shareable, the more user specific
-        if (methodStats.shareability() < workflow.shareabilityThreshold(0)) {
+        if (groupOfOccurrences.getStats().getShareability() < Thresholds.shareabilityThreshold(0)) {
             return Optional.of(true);
         } else {
             return Optional.of(false);
         }
     }
 
-    public static Optional<Boolean> isDataSizeLarge(MethodInfo methodInfo) {
-
-        //TODO concept considered while caching
-        if (true) {
-            return Optional.of(false);
-        }
-
-        long shallowSize = methodInfo.getSizeOfReturnedValue(); // modified to interface
-
-        BigDecimal bd = new BigDecimal(shallowSize)
-                .multiply(new BigDecimal(100))
-                .divide(new BigDecimal(Long.MAX_VALUE), 5, RoundingMode.HALF_UP); //cache free space replaced to infinity
-        double dataSizePercent = bd.doubleValue();
-
-        if (dataSizePercent <= 2.0) {
-            return Optional.of(false);
-        } else {
-            return Optional.of(true);
-        }
-    }
-
-    public static Optional<Boolean> isExpensive(MethodStats methodStats, FlowchartWorkFlow workflow) {
-        if (methodStats.getSameOccurrencesAverageExecutionTime() >= workflow.expensivenessThreshold(0)) {
+    public static Optional<Boolean> isExpensive(GroupOfOccurrences groupOfOccurrences) {
+        if (groupOfOccurrences.getStats().getSameOccurrencesAverageExecutionTime() >= Thresholds.expensivenessThreshold(0)) {
             return Optional.of(true);
         } else {
             return Optional.of(false);
         }
     }
 
-    public static String allMetricsToString(MethodInfo methodInfo, MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
-        //isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive
-        return isStaticData(methodStats, workflow, population)
-                + "," + changeMoreThanUsed(methodStats, workflow)
-                + "," + usedByManyRequests(methodStats, workflow, population)
-                + "," + isUserSpecific(methodStats, workflow)
-                + "," + isDataSizeLarge(methodInfo)
-                + "," + isExpensive(methodStats, workflow);
-    }
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java
index ff9644f..5d5c2a5 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java
@@ -1,93 +1,38 @@
 package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats;
 
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.CacheDecider;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.GroupOfOccurrences;
 
 import java.util.Optional;
 
-/**
- * TODO flowchart paper link
- *
- * @see
- */
-public class CacheabilityPatternDecider implements CacheDecider {
-
-    private final int population;
-    Logger logger = LoggerFactory.getLogger(CacheabilityPatternDecider.class);
-
-    FlowchartWorkFlow workflow;
-
-    public CacheabilityPatternDecider(int size, FlowchartWorkFlow workflow) {
-        this.workflow = workflow;
-        this.population = size;
-    }
+public class CacheabilityPatternDecider {
 
     /**
      * Flowchart definition
      *
-     * @param methodStats
+     * @param groupOfOccurrences
      * @return
      */
-    @Override
-    public boolean isCacheable(MethodInfo methodInfo, MethodStats methodStats) {
-
-        //used more than once, in many parts of the reasoning
-        Optional<Boolean> isCacheSizeLarge = Optional.of(true); // the cache is always large enough
-
+    public static boolean isCacheable(GroupOfOccurrences groupOfOccurrences) {
         //Is the data completely static?
-        Optional<Boolean> isStaticData = CacheabilityMetrics.isStaticData(methodStats, workflow, population);
+        Optional<Boolean> isStaticData = CacheabilityMetrics.isStaticData(groupOfOccurrences);
         if (isStaticData.isPresent() && isStaticData.get()) { // staticity yes
-
-            Optional<Boolean> isDataSizeLarge = CacheabilityMetrics.isDataSizeLarge(methodInfo);
-            if (isDataSizeLarge.isPresent() && !isDataSizeLarge.get()) {
-                return true;
-            } else {
-                if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        } else { // staticity no/not sure
-
-            Optional<Boolean> changeMoreThanUsed = CacheabilityMetrics.changeMoreThanUsed(methodStats, workflow);
-            if (changeMoreThanUsed.isPresent() && changeMoreThanUsed.get()) { //changeMoreThanUsed true
-                return false;
-            } else { //changeMoreThanUsed not/not sure
-                Optional<Boolean> usedByManyRequests = CacheabilityMetrics.usedByManyRequests(methodStats, workflow, population);
-                if (usedByManyRequests.isPresent() && !usedByManyRequests.get()) { //useByManyRequests no
-                    return false;
-                } else {
-                    Optional<Boolean> isUserSpecific = CacheabilityMetrics.isUserSpecific(methodStats, workflow);
-                    if (isUserSpecific.isPresent() && isUserSpecific.get()) {
-                        if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
-                            return true;
-                        } else {
-                            return false;
-                        }
-                    } else {
-                        Optional<Boolean> isExpensive = CacheabilityMetrics.isExpensive(methodStats, workflow);
-                        if (!isExpensive.isPresent()) {
-
-                            if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
-                                return true;
-                            } else {
-                                return false;
-                            }
-                        } else {
-                            if (isExpensive.get()) {
-                                return true;
-                            } else {
-                                return false;
-                            }
-                        }
-                    }
-                }
-            }
+            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;
     }
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/Thresholds.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/Thresholds.java
new file mode 100644
index 0000000..d53bfe4
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/Thresholds.java
@@ -0,0 +1,217 @@
+/*
+ * 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.adaptivecaching.analysis.decision.flowchart.stats;
+
+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 sumFrequency;
+
+    public static Double averageFrequency;
+
+    public static Double stdDevHitRatio;
+    public static Double stdDevMissRatio;
+    public static Double stdDevExecutionTimeRatio;
+    public static Double stdDevFrequency;
+    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<Long> frequencies = new ArrayList<>();
+
+    public static void reset() {
+        Thresholds.sumMissRatio = 0;
+        Thresholds.sumHitRatio = 0;
+        Thresholds.sumExecutionTime = 0;
+        Thresholds.sumShareability = 0;
+        Thresholds.sumFrequency = 0;
+
+        Thresholds.averageFrequency = 0D;
+
+        Thresholds.stdDevHitRatio = 0D;
+        Thresholds.stdDevMissRatio = 0D;
+        Thresholds.stdDevExecutionTimeRatio = 0D;
+        Thresholds.stdDevFrequency = 0D;
+        Thresholds.stdDevShareability = 0D;
+
+        Thresholds.population = 0;
+
+        Thresholds.hitRatios.clear();
+        Thresholds.missRatios.clear();
+        Thresholds.executionTimes.clear();
+        Thresholds.shareabilities.clear();
+        Thresholds.frequencies.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().parallel()
+                .map(hitRate -> (hitRate - mean) * (hitRate - mean))
+                .reduce(Double::sum)
+                .get();
+        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 = 0;
+        for (double a : missRatios) {
+            temp += (a - mean) * (a - mean);
+        }
+        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).get();
+        stdDevExecutionTimeRatio = Math.sqrt(temp / population);
+        return stdDevExecutionTimeRatio;
+    }
+
+    public static double getStdDevFrequency() {
+        if (population == 0) {
+            return 0;
+        }
+        if (stdDevFrequency != null) {
+            return stdDevFrequency;
+        }
+
+        double mean = getAverageFrequency();
+        double temp = frequencies.stream()
+                .map(frequency -> (frequency - mean) * (frequency - mean))
+                .reduce(Double::sum).get();
+        stdDevFrequency = Math.sqrt(temp / population);
+        return stdDevFrequency;
+    }
+
+    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)
+                .get();
+        stdDevShareability = Math.sqrt(temp / population);
+        return stdDevShareability;
+    }
+
+    public static double getAverageFrequency() {
+        if (frequencies.isEmpty()) {
+            return 0;
+        }
+        if (averageFrequency != null) {
+            return averageFrequency;
+        }
+
+        averageFrequency = new BigDecimal(sumFrequency).divide(new BigDecimal(frequencies.size()), 5, RoundingMode.HALF_UP).doubleValue();
+        return averageFrequency;
+    }
+
+//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 frequencyThreshold(int kStdDev) {
+        return getAverageFrequency() + (kStdDev * getStdDevFrequency());
+    }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/GroupOfOccurrences.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/GroupOfOccurrences.java
new file mode 100644
index 0000000..8682038
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/GroupOfOccurrences.java
@@ -0,0 +1,73 @@
+/*
+ * 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.adaptivecaching.monitoring.application.metadata;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.Stats;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.Thresholds;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+
+/**
+ *
+ * @author romulo
+ */
+public class GroupOfOccurrences {
+
+    private final Stats stats;
+    private final List<Occurrence> occurrences;
+
+    public GroupOfOccurrences() {
+        this.stats = new Stats();
+        this.occurrences = new ArrayList<>();
+    }
+
+    public Stats getStats() {
+        return stats;
+    }
+
+    public void addOccurrence(Occurrence occurrence) {
+        this.occurrences.add(occurrence);
+    }
+
+    protected void countStats() {
+        if (this.occurrences.size() == 1) {
+            this.stats.addSameOccurrence(this.occurrences.get(0));
+            return;
+        }
+        for (int i = 0; i < this.occurrences.size(); i++) {
+            Occurrence occurrence = this.occurrences.get(i);
+//            load concrete
+            for (int j = i + 1; j < this.occurrences.size(); j++) {
+                Occurrence other = this.occurrences.get(j);
+                if (EqualsBuilder.reflectionEquals(occurrence.getReturnValue(), other.getReturnValue())) {
+                    this.stats.addSameOccurrence(occurrence);
+                    continue;
+                }
+                this.stats.addDifferentReturnOccurrence();
+            }
+//            remove
+        }
+    }
+
+    protected void calculateThresholds() {
+        Thresholds.sumExecutionTime += this.stats.getSameOccurrencesTotalExecutionTime();
+        Thresholds.executionTimes.add(this.stats.getSameOccurrencesTotalExecutionTime());
+
+        Thresholds.sumHitRatio += this.stats.getHitRatio();
+        Thresholds.hitRatios.add(this.stats.getHitRatio());
+
+        Thresholds.sumMissRatio += this.stats.getMissRatio();
+        Thresholds.missRatios.add(this.stats.getMissRatio());
+
+        Thresholds.sumShareability += this.stats.getShareability();
+        Thresholds.shareabilities.add(this.stats.getShareability());
+
+        Thresholds.sumFrequency += this.stats.getNumberOfSameOccurrences();
+        Thresholds.frequencies.add(this.stats.getNumberOfSameOccurrences());
+    }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/Method.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/Method.java
new file mode 100644
index 0000000..e6fe5cb
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/Method.java
@@ -0,0 +1,103 @@
+/*
+ * 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.adaptivecaching.monitoring.application.metadata;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.CacheabilityPatternDecider;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author romulo
+ */
+public class Method {
+
+    private static final Logger logger = Logger.getLogger(Method.class.getName());
+
+    private final String name;
+    private final List<Occurrence> occurrences;
+    private Collection<GroupOfOccurrences> groupsOfOccurrences;
+
+    public Method(String name) {
+        this.name = name;
+        this.occurrences = new ArrayList<>();
+    }
+
+    public Method addOccurrence(Occurrence occurrence) {
+        this.occurrences.add(occurrence);
+        return this;
+    }
+
+    public Method clearTraces() {
+        this.occurrences.clear();
+        return this;
+    }
+
+    public int getOccurrencesSize() {
+        return this.occurrences.size();
+    }
+
+    public Collection<GroupOfOccurrences> getGroupsOfOccurrences() {
+        return groupsOfOccurrences;
+    }
+
+    public void groupByParameter() {
+        logger.log(Level.INFO, "Grouping by parameters {0} occurrences of {1}", new Object[]{this.name, this.occurrences.size()});
+        Map<String, GroupOfOccurrences> groupByParameter = new HashMap<>();
+        this.occurrences.stream().parallel().forEach(new Consumer<Occurrence>() {
+            int i = 0;
+
+            @Override
+            public void accept(Occurrence occurrence) {
+                System.out.print(".");
+                System.out.flush();
+                if (++i % 100 == 0) {
+                    System.out.println();
+                }
+                String parameters = Arrays.toString(occurrence.getParameters());
+                synchronized (groupByParameter) {
+                    try {
+                        groupByParameter.get(parameters).addOccurrence(occurrence);
+                    } catch (Exception e) {
+                        GroupOfOccurrences groupOfOccurrences = new GroupOfOccurrences();
+                        groupOfOccurrences.addOccurrence(occurrence);
+                        groupByParameter.put(parameters, groupOfOccurrences);
+                    }
+                }
+            }
+        });
+        this.groupsOfOccurrences = groupByParameter.values();
+        groupByParameter.clear();
+        this.occurrences.clear();
+    }
+
+    public void countStats() {
+        groupByParameter();
+        this.groupsOfOccurrences.stream().parallel().forEach(GroupOfOccurrences::countStats);
+    }
+
+    public void calculateThresholds() {
+        this.groupsOfOccurrences.stream().forEach(GroupOfOccurrences::calculateThresholds);
+    }
+
+    public void filterCacheableInputs() {
+        Iterator<GroupOfOccurrences> iterator = this.groupsOfOccurrences.iterator();
+        while (iterator.hasNext()) {
+            GroupOfOccurrences groupOfOccurrences = iterator.next();
+            if (!CacheabilityPatternDecider.isCacheable(groupOfOccurrences)) {
+                iterator.remove();
+            }
+        }
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/OccurrenceConcrete.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/OccurrenceConcrete.java
new file mode 100644
index 0000000..086d319
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/OccurrenceConcrete.java
@@ -0,0 +1,32 @@
+/*
+ * 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.adaptivecaching.monitoring.application.metadata;
+
+/**
+ *
+ * @author romulo
+ */
+public class OccurrenceConcrete extends Occurrence {
+
+    private final Object[] parameters;
+    private final Object returnValue;
+
+    public OccurrenceConcrete(Object[] arguments, Object returnedValue, long startTime, long endTime, String userId) {
+        super(startTime, endTime, userId);
+        this.parameters = arguments;
+        this.returnValue = returnedValue;
+    }
+
+    @Override
+    public Object[] getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public Object getReturnValue() {
+        return this.returnValue;
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java
index fff69c4..8d9e7d9 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java
@@ -6,11 +6,10 @@
 package br.ufrgs.inf.prosoft.approachescomparison.adapter;
 
 import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
-import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodEntry;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.Method;
 import br.ufrgs.inf.prosoft.trace.Trace;
+import java.util.Collection;
 import java.util.List;
-import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -34,9 +33,11 @@ public class Main {
         }
         logger.log(Level.INFO, "Reading traces");
         List<Trace> traces = TraceReader.partiallyParseFile(path);
-        List<LogTrace> logList = TraceReader.getLogTraces(traces);
-        FlowchartWorkFlow flowchartWorkFlow = new FlowchartWorkFlow(logList);
-        Set<MethodEntry> process = flowchartWorkFlow.filterCacheableMethods(300000); // default value provided by the framework
-        logger.log(Level.INFO, "{0} cacheable methods identified", process.size());
+        logger.log(Level.INFO, "Grouping by methods");
+        Collection<Method> methods = TraceReader.groupByMethods(traces);
+        FlowchartWorkFlow flowchartWorkFlow = new FlowchartWorkFlow();
+        flowchartWorkFlow.setMethods(methods);
+        logger.log(Level.INFO, "Filtering cacheable methods");
+        flowchartWorkFlow.filterCacheableInputs();
     }
 }
diff --git a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java
index 6ad3e04..4a018cd 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java
@@ -5,14 +5,17 @@
  */
 package br.ufrgs.inf.prosoft.approachescomparison.adapter;
 
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfoConcrete;
-import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfoReference;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.Occurrence;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.OccurrenceConcrete;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.OccurrenceReference;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.Method;
 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.Collection;
+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;
@@ -21,39 +24,42 @@ import java.util.stream.Collectors;
  *
  * @author romulo
  */
-public class TraceReader extends br.ufrgs.inf.prosoft.trace.TraceReader {
+public class TraceReader extends br.ufrgs.inf.prosoft.trace.reader.TraceReader {
 
     private static final Logger logger = Logger.getLogger(TraceReader.class.getName());
 
-    public static List<LogTrace> getLogTraces(List<Trace> traces) {
-        logger.log(Level.INFO, "Converting {0} traces", traces.size());
-        List<LogTrace> logTraces = new ArrayList<>();
-        int index = 1;
+    public static Collection<Method> groupByMethods(List<Trace> traces) {
+        Map<String, Method> methodNameHasOccurrences = new HashMap<>();
         while (!traces.isEmpty()) {
             Trace trace = traces.remove(0);
             try {
-                System.out.print(".");
-                System.out.flush();
-                if (index++ % 100 == 0) {
-                    System.out.println();
-                }
-                LogTrace logTrace = new LogTrace().setStartTime(trace.getStartTime())
-                        .setEndTime(trace.getEndTime())
-                        .setUserId(trace.getUserSession());
+                Occurrence occurrence;
                 if (trace instanceof TraceReference) {
                     TraceReference traceReference = (TraceReference) trace;
-                    logTrace.setMethodInfo(new MethodInfoReference(traceReference.getName(), traceReference.getIndex()));
+                    occurrence = new OccurrenceReference(traceReference.getIndex(),
+                            trace.getStartTime(), trace.getEndTime(), trace.getUserSession());
                 } else {
-                    logTrace.setMethodInfo(new MethodInfoConcrete(trace.getName(),
-                            trace.getParameters().stream().map(Parameter::getData)
+                    occurrence = new OccurrenceConcrete(
+                            trace.getParameters().stream()
+                                    .map(Parameter::getData)
                                     .collect(Collectors.toList()).toArray(),
-                            trace.getReturn().getData()));
+                            trace.getReturn().getData(),
+                            trace.getStartTime(),
+                            trace.getEndTime(),
+                            trace.getUserSession()
+                    );
+                }
+                try {
+                    methodNameHasOccurrences.get(trace.getName()).addOccurrence(occurrence);
+                } catch (Exception ex) {
+                    Method method = new Method(trace.getName());
+                    method.addOccurrence(occurrence);
+                    methodNameHasOccurrences.put(trace.getName(), method);
                 }
-                logTraces.add(logTrace);
             } catch (Exception e) {
                 logger.log(Level.INFO, "Trace discarted: {0}", trace);
             }
         }
-        return logTraces;
+        return methodNameHasOccurrences.values();
     }
 }