APLCache.java

121 lines | 5.96 kB Blame History Raw Download
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufrgs.inf.prosoft.aplcachetf.extension;

import br.ufrgs.inf.prosoft.aplcachetf.extension.metadata.Method;
import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.CacheabilityMetrics;
import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.Thresholds;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author root
 */
public class APLCache {

    private static final Logger LOGGER = Logger.getLogger(APLCache.class.getName());

    private final List<Method> methods;

    public APLCache(List<Method> methods) {
        this.methods = methods;
    }

    private void calculateMetrics() {
        LOGGER.log(Level.INFO, "Calculating metrics for {0} methods...", this.methods.size());
        this.methods.stream().parallel().forEach(Method::calculateMetrics);
    }

    private void calculateThresholds(int kStdDev) {
        LOGGER.log(Level.INFO, "Calculating thresholds for {0} methods with {1} stdDev...", new Object[]{this.methods.size(), kStdDev});

        Thresholds.reset();
        Thresholds.population = getPopulation();
        this.methods.stream().forEach(Method::calculateThresholds);

        LOGGER.log(Level.INFO, "\tAverage   ExecutionTime:      {0}", Thresholds.getAverageExecutionTime());
        LOGGER.log(Level.INFO, "\tStdDv     ExecutionTime:      {0}", Thresholds.getStdDevExecutionTimeRatio());
        LOGGER.log(Level.INFO, "\tThreshold ExecutionTime:      {0}", Thresholds.expensivenessThreshold(kStdDev));
        LOGGER.log(Level.INFO, "\tAverage   HitRatio:           {0}", Thresholds.getAverageHitRatio());
        LOGGER.log(Level.INFO, "\tStdDv     HitRatio:           {0}", Thresholds.getStdDevHitRatio());
        LOGGER.log(Level.INFO, "\tThreshold HitRatio:           {0}", Thresholds.hitThreshold(kStdDev));
        LOGGER.log(Level.INFO, "\tAverage   MissRatio:          {0}", Thresholds.getAverageMissRatio());
        LOGGER.log(Level.INFO, "\tStdDv     MissRatio:          {0}", Thresholds.getStdDevMissRatio());
        LOGGER.log(Level.INFO, "\tThreshold MissRatio:          {0}", Thresholds.missThreshold(kStdDev));
        LOGGER.log(Level.INFO, "\tAverage   Shareability:       {0}", Thresholds.getAverageShareability());
        LOGGER.log(Level.INFO, "\tStdDv     Shareability:       {0}", Thresholds.getStdDevShareability());
        LOGGER.log(Level.INFO, "\tThreshold Shareability:       {0}", Thresholds.shareabilityThreshold(kStdDev));
        LOGGER.log(Level.INFO, "\tAverage   HitsPerTimeInCache: {0}", Thresholds.getAverageHitsPerTimeInCache());
        LOGGER.log(Level.INFO, "\tStdDv     HitsPerTimeInCache: {0}", Thresholds.getStdDevHitsPerTimeInCache());
        LOGGER.log(Level.INFO, "\tThreshold HitsPerTimeInCache: {0}", Thresholds.hitsPerTimeInCacheThreshold(kStdDev));
    }

    private void filterCacheableInputs(int kStdDev) {
        LOGGER.log(Level.INFO, "Filtering inputs of {0} methods under {1} stdDev threshold...", new Object[]{this.methods.size(), kStdDev});
        CacheabilityMetrics.K_STANDARD_DEVIATION = kStdDev;
        this.methods.forEach(Method::filterCacheableInputs);
        this.methods.removeIf(method -> method.groupsOfOccurrences().count() == 0);
    }

    private void removeSingleOccurrences() {
        int initialMethodsSize = this.methods.size();
        LOGGER.log(Level.INFO, "Removing not reusable inputs from {0} methods", this.methods.size());
        this.methods.forEach(Method::removeSingleOccurrences);
        LOGGER.log(Level.INFO, "Removing not reusable methods from {0} methods", this.methods.size());
        this.methods.removeIf(method -> method.groupsOfOccurrences().count() == 0);
        int removedMethods = initialMethodsSize - this.methods.size();
        if (removedMethods > 0) {
            LOGGER.log(Level.INFO, "Removed {0} of {1} not reusable methods", new Object[]{removedMethods, initialMethodsSize});
        }
    }

    public void recommend(String outputPath) {
        recommend(0, outputPath);
    }

    public void recommend(int kStdDev, String outputPath) {
        LOGGER.log(Level.INFO, "Recommending TTL per method for {0} methods", this.methods.size());
        removeSingleOccurrences();
        calculateMetrics();
        calculateThresholds(kStdDev);
        filterCacheableInputs(kStdDev);

        LOGGER.log(Level.INFO, "{0} cacheable methods detected", this.methods.size());
        Collections.sort(this.methods, (m1, m2) -> Long.compare(m2.getSavedTime(), m1.getSavedTime()));

        this.methods.forEach(method -> {
            System.out.println(method.getName() + " Occurrences " + method.getOccurrencesSize() + " Inputs " + method.groupsOfOccurrences().count() + " TTL " + method.getTtl() + " HpTiC " + method.getHitsPerTimeInCache() + " Saves " + method.getSavedTime());
        });
        try (FileWriter fileWriter = new FileWriter(outputPath)) {
            JsonObject jsonCacheableParameters = new JsonObject();
            this.methods.forEach(method -> {
                JsonObject cacheableParameters = new JsonObject();
                method.groupsOfOccurrences().forEach(group -> {
                    cacheableParameters.addProperty(group.getParameters(), group.getTtl());
                });
                jsonCacheableParameters.add(method.getName(), cacheableParameters);
            });
            Gson gson = new GsonBuilder().setPrettyPrinting().create();
            gson.toJson(jsonCacheableParameters, fileWriter);
        } catch (IOException ex) {
            LOGGER.log(Level.SEVERE, "invalid <outputPath>");
        }
    }

    private long getPopulation() {
        return this.methods.stream().map(Method::getOccurrencesSize).reduce(Integer::sum).orElse(0);
    }

}