Method.java
Home
/
src /
main /
java /
br /
ufrgs /
inf /
prosoft /
tfcache /
metadata /
Method.java
/*
* 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.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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*
* @author romulo
*/
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 Integer countChangeableGroups;
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 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 int getOccurrencesSize() {
if (this.occurrences == null) {
if (this.groupsOfOccurrences == null) {
throw new RuntimeException("groupsOfOccurrences is null");
}
return this.groupsOfOccurrences.stream().map(group -> group.getOccurrencesSize()).reduce(Integer::sum).orElse(0);
}
return this.occurrences.size();
}
public Stream<GroupOfOccurrences> groupsOfOccurrences() {
if (this.groupsOfOccurrences == null) {
groupByInput();
}
return this.groupsOfOccurrences.stream();
}
private void groupByInput() {
if (this.occurrences == null) {
throw new RuntimeException("Occurrences already consumed");
}
Map<String, List<Occurrence>> inputHasOccurrences = new ConcurrentHashMap<>();
this.occurrences.stream().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 removeChangeableInputs() {
if (this.countChangeableGroups != null) {
throw new RuntimeException("Changeable already filtered");
}
if (this.groupsOfOccurrences == null) {
groupByInput();
}
int initialCountOfInputs = this.groupsOfOccurrences.size();
this.groupsOfOccurrences.removeIf(GroupOfOccurrences::isChangeable);
this.countChangeableGroups = initialCountOfInputs - this.groupsOfOccurrences.size();
if (this.countChangeableGroups > 0) {
LOGGER.log(Level.INFO, "\tRemoved {0} of {1} changeable inputs from method {2}", new Object[]{this.countChangeableGroups, initialCountOfInputs, this.name});
}
}
public boolean isChangeable() {
if (this.countChangeableGroups != null) {
return this.countChangeableGroups > 0;
}
if (this.groupsOfOccurrences != null) {
return this.groupsOfOccurrences.stream().anyMatch(GroupOfOccurrences::isChangeable);
}
Map<String, Object> inputHasOutput = new HashMap<>();
for (Occurrence occurrence : this.occurrences) {
String parameters = occurrence.getParametersSerialised();
if (!inputHasOutput.containsKey(parameters)) {
inputHasOutput.put(parameters, occurrence.getReturnValue());
} else if (!Objects.deepEquals(inputHasOutput.get(parameters), occurrence.getReturnValue())) {
return true;
}
}
return false;
}
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 boolean isReusable() {
if (this.groupsOfOccurrences != null) {
return this.groupsOfOccurrences.stream().anyMatch(groupOfOccurrences -> groupOfOccurrences.getOccurrencesSize() > 1);
}
Map<String, Long> inputHasFrequency = occurrences()
.map(Occurrence::getParametersSerialised)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
return inputHasFrequency.values().stream().anyMatch(frequency -> frequency > 1);
}
public boolean isNotReusable() {
return !isReusable();
}
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 recommendTTLPerInput() {
if (this.groupsOfOccurrences == null) {
groupByInput();
removeSingleOccurrences();
}
groupsOfOccurrences().forEach(GroupOfOccurrences::calculateHitsPerTimeInCache);
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 rankRecommendations() {
if (this.groupsOfOccurrences == null) {
throw new RuntimeException("groupsOfOccurrences is null");
}
this.groupsOfOccurrences.sort((group1, group2) -> Long.compare(group2.getSavedTime(), group1.getSavedTime()));
}
@Override
public String toString() {
return this.name;
}
}