Method.java

197 lines | 7.152 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.metadata;

import br.ufrgs.inf.prosoft.aplcachetf.extension.metrics.CacheabilityPatternDecider;
import br.ufrgs.inf.prosoft.tfcache.Metrics;
import br.ufrgs.inf.prosoft.tfcache.Simulator;
import br.ufrgs.inf.prosoft.tfcache.metadata.Occurrence;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

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

    private final String name;
    private List<Occurrence> occurrences;
    private List<GroupOfOccurrences> groupsOfOccurrences;
    private Metrics bestMetrics;

    public Method(String name, List<Occurrence> occurrences) {
        this.name = name;
        if (occurrences == null) {
            throw new RuntimeException("Occurrences is null");
        }
        if (occurrences.isEmpty()) {
            throw new RuntimeException("Occurrences is empty");
        }
        this.occurrences = occurrences;
    }

    public String getName() {
        return this.name;
    }

    public Metrics getBestMetrics() {
        if (this.bestMetrics == null) {
            throw new RuntimeException("hitsPerTimeInCache must be calculated");
        }
        return this.bestMetrics;
    }

    public Long getTtl() {
        return getBestMetrics().getTtl();
    }

    public Long getSavedTime() {
        return getBestMetrics().getSavedTime();
    }

    public BigDecimal getHitsPerTimeInCache() {
        return getBestMetrics().getHitsPerTimeInCache();
    }

    public Stream<Occurrence> occurrences() {
        if (this.occurrences == null) {
            throw new RuntimeException("Occurrences already consumed");
        }
        return this.occurrences.stream();
    }

    public Stream<GroupOfOccurrences> groupsOfOccurrences() {
        if (this.groupsOfOccurrences == null) {
            groupByInput();
        }
        return this.groupsOfOccurrences.stream();
    }

    public int getOccurrencesSize() {
        if (this.occurrences == null) {
            if (this.groupsOfOccurrences == null) {
                throw new RuntimeException("groupsOfOccurrences is null");
            }
            return this.groupsOfOccurrences.stream().map(GroupOfOccurrences::getOccurrencesSize).reduce(Integer::sum).orElse(0);
        }
        return this.occurrences.size();
    }

    private void groupByInput() {
        if (this.occurrences == null) {
            throw new RuntimeException("Occurrences already consumed");
        }
        Map<String, List<Occurrence>> inputHasOccurrences = new ConcurrentHashMap<>();
        occurrences().forEach(occurrence -> {
            String parameters = occurrence.getParametersSerialised();
            inputHasOccurrences.compute(parameters, (key, oldValue) -> {
                if (oldValue == null) {
                    oldValue = new ArrayList<>();
                }
                oldValue.add(occurrence);
                return oldValue;
            });
        });
        this.groupsOfOccurrences = inputHasOccurrences.entrySet().stream()
                .map(entry -> new GroupOfOccurrences(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
        this.occurrences = null;
    }

    public void recommendTTL() {
        if (this.bestMetrics != null) {
            LOGGER.log(Level.WARNING, "HitsPerTimeInCache already calculated");
        }
        if (this.occurrences == null) {
            throw new RuntimeException("groupByInputs called. Cannot proceed");
        }
        this.bestMetrics = new Metrics();
        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);
        });
    }

    public void removeSingleOccurrences() {
        if (this.groupsOfOccurrences == null) {
            groupByInput();
        }
        int initialCountofOccurrences = this.groupsOfOccurrences.size();
        this.groupsOfOccurrences.removeIf(groupOfOccurrences -> groupOfOccurrences.getOccurrencesSize() < 2);
        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});
        }
    }

    public void calculateMetrics() {
        if (this.groupsOfOccurrences == null) {
            groupByInput();
        }
        groupsOfOccurrences().forEach(GroupOfOccurrences::calculateMetrics);
        if (this.bestMetrics == null) {
            long savedTime = groupsOfOccurrences().map(GroupOfOccurrences::getSavedTime).reduce(Long::sum).get();
            GroupOfOccurrences max = groupsOfOccurrences().max((group1, group2) -> Long.compare(group1.getSavedTime(), group2.getSavedTime())).get();
            long ttl = max.getTtl();
            BigDecimal hitsPerTimeInCache = max.getHitsPerTimeInCache();
            this.bestMetrics = new Metrics(ttl, savedTime, hitsPerTimeInCache);
        }
    }

    public void calculateThresholds() {
        this.groupsOfOccurrences.stream().forEach(GroupOfOccurrences::calculateThresholds);
    }

    public void filterCacheableInputs() {
        int initialInputsSize = this.groupsOfOccurrences.size();
        this.groupsOfOccurrences.removeIf(CacheabilityPatternDecider::isNotCacheable);
        int removedInputs = initialInputsSize - this.groupsOfOccurrences.size();
        if (removedInputs > 0) {
            LOGGER.log(Level.INFO, "\tRemoved {0} of {1} uncacheable inputs from method {2}", new Object[]{removedInputs, initialInputsSize, this.name});
        }
        initialInputsSize = this.groupsOfOccurrences.size();
        this.groupsOfOccurrences.removeIf(groupOfOccurrence -> groupOfOccurrence.getTtl() == 0);
        removedInputs = initialInputsSize - this.groupsOfOccurrences.size();
        if (removedInputs > 0) {
            LOGGER.log(Level.INFO, "\tRemoved {0} of {1} inputs with 0 TTL from method {2}", new Object[]{removedInputs, initialInputsSize, this.name});
        }
    }

    @Override
    public String toString() {
        return this.name;
    }

}