Details
diff --git a/src/main/java/br/ufrgs/inf/prosoft/cache/tools/CSV.java b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/CSV.java
new file mode 100644
index 0000000..1aee4fd
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/CSV.java
@@ -0,0 +1,281 @@
+package br.ufrgs.inf.prosoft.cache.tools;
+
+import br.ufrgs.inf.prosoft.cache.tools.CSV.Row;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+public class CSV implements Iterable<Row> {
+
+ private class Reference {
+
+ String value;
+
+ public Reference() {
+ }
+
+ public Reference(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 11 * hash + Objects.hashCode(this.value);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this.value == null) {
+ return obj == null;
+ }
+ if (obj instanceof Reference) {
+ return this.value.equals(((Reference) obj).value);
+ }
+ if (obj instanceof String) {
+ return this.value.equals((String) obj);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ private class Header extends Reference {
+
+ public Header(String value) {
+ super(value);
+ }
+
+ }
+
+ private class Value {
+
+ private final Header header;
+ private Reference reference;
+ private final Row row;
+ private final Column column;
+
+ public Value(Header header, Row row, Column column) {
+ this(header, null, row, column);
+ }
+
+ public Value(Header header, Reference reference, Row row, Column column) {
+ this.header = header;
+ this.reference = reference;
+ this.row = row;
+ this.column = column;
+ }
+
+ public String getProperty() {
+ return header.value;
+ }
+
+ public String getValue() {
+ return reference.value;
+ }
+
+ private void setValue(Reference reference) {
+ this.reference = reference;
+ }
+
+ public Row getRow() {
+ return row;
+ }
+
+ public Column getColumn() {
+ return column;
+ }
+
+ @Override
+ public String toString() {
+ return getProperty() + ": " + getValue();
+ }
+
+ }
+
+ protected class Row implements Iterable<Value> {
+
+ private final List<Value> values;
+
+ public Row(List<Value> values) {
+ if (values == null) {
+ throw new RuntimeException("Tried to create a null row");
+ }
+ this.values = values;
+ }
+
+ public String get(String property) {
+ for (Value value : this) {
+ if (value.getProperty().equals(property)) {
+ return value.getValue();
+ }
+ }
+ throw new RuntimeException("data does not contain property " + property);
+ }
+
+ @Override
+ public Iterator<Value> iterator() {
+ return this.values.iterator();
+ }
+
+ @Override
+ public String toString() {
+ return values.toString();
+ }
+
+ }
+
+ private class Column extends Row {
+
+ public Column() {
+ this(new ArrayList<>());
+ }
+
+ public Column(List<Value> values) {
+ super(values);
+ }
+ }
+
+ private List<Header> headers;
+ private final List<Row> rows;
+ private final List<Column> columns;
+ private final Map<Header, Map<Reference, Value>> indexes;
+
+ public CSV(String... headers) {
+ this(headers, new String[]{});
+ }
+
+ public CSV(String[] headers, String[] indexes) {
+ this.headers = new ArrayList<>();
+ this.indexes = new HashMap<>();
+ this.columns = new ArrayList<>();
+ this.rows = new ArrayList<>();
+
+ ArrayList<String> indexesList = new ArrayList(Arrays.asList(indexes));
+
+ for (String strHeader : headers) {
+ Header header = new Header(strHeader);
+ this.headers.add(header);
+ if (indexesList.contains(header.toString())) {
+ indexesList.remove(header.toString());
+ this.indexes.put(header, new HashMap<>());
+ }
+ this.columns.add(new Column());
+ }
+ if (!indexesList.isEmpty()) {
+ throw new RuntimeException("indexes not present on headers: " + indexesList);
+ }
+ }
+
+ public CSV append(String... values) {
+ if (values.length != this.headers.size()) {
+ throw new RuntimeException("Values not matching headers length");
+ }
+ List<Value> list = new ArrayList<>();
+ Row row = new Row(list);
+ Iterator<Column> columnsIterator = this.columns.iterator();
+ Iterator<Header> headersIterator = this.headers.iterator();
+ for (String strValue : values) {
+ Header header = headersIterator.next();
+ Reference reference = new Reference(strValue);
+ Value value = new Value(header, reference, row, columnsIterator.next());
+ list.add(value);
+ if (this.indexes.containsKey(header)) {
+ if (this.indexes.get(header).get(reference) != null) {
+ throw new RuntimeException("duplicate index " + reference + " for " + header);
+ }
+ this.indexes.get(header).put(reference, value);
+ }
+ }
+ this.rows.add(row);
+ return this;
+ }
+
+ private static String[] process(String line) {
+ if (line.contains("'") || line.contains("\"")) {
+ throw new UnsupportedOperationException("Scaped values not supported yet");
+ }
+ return line.split(",");
+ }
+
+ public static CSV read(String path) {
+ return read(path, new String[]{});
+ }
+
+ public static CSV read(String path, String... indexes) {
+ try {
+ List<String> lines = Files.readAllLines(Paths.get(path));
+ if (lines.isEmpty()) {
+ throw new RuntimeException("Empty file");
+ }
+ String headers = lines.remove(0);
+ CSV csv = new CSV(process(headers), indexes);
+ lines.forEach(line -> {
+ csv.append(process(line));
+ });
+ return csv;
+ } catch (IOException ex) {
+ throw new RuntimeException("Cannot read file");
+ }
+ }
+
+ public Optional<Row> selectFirst(String property, String value) {
+ Header mockProperty = new Header(property);
+ Reference mockValue = new Reference(value);
+ if (!this.headers.contains(mockProperty)) {
+ throw new RuntimeException("header not present");
+ }
+ Map<Reference, Value> values = this.indexes.get(mockProperty);
+ if (values != null) {
+ return Optional.ofNullable(values.get(mockValue).getRow());
+ }
+
+ for (Row row : this) {
+ if (row.get(property).equals(value)) {
+ return Optional.ofNullable(row);
+ }
+ }
+ return Optional.empty();
+ }
+
+ public List<Row> select(String property, String value) {
+ Header mockProperty = new Header(property);
+ Reference mockValue = new Reference(value);
+ if (!this.headers.contains(mockProperty)) {
+ throw new RuntimeException("header not present");
+ }
+ List<Row> select = new ArrayList<>();
+
+ Map<Reference, Value> values = this.indexes.get(mockProperty);
+ if (values != null) {
+ select.add(values.get(mockValue).getRow());
+ return select;
+ }
+
+ for (Row row : this) {
+ if (row.get(property).equals(value)) {
+ select.add(row);
+ }
+ }
+ return select;
+ }
+
+ @Override
+ public Iterator<Row> iterator() {
+ return this.rows.iterator();
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Main.java b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Main.java
index 7516af9..cd2ae9d 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Main.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Main.java
@@ -22,6 +22,7 @@ public class Main {
System.err.println("--events=<EventsPath> --reduce=<ReducePath> [--prefix=<prefix>]");
System.err.println("--uncached=<UncachedPath> --reduce=<ReducePath> [--prefix=<prefix> --hash]");
System.err.println("--events=<EventsPath> --reduce=<ReducePath> --size=true [--prefix=<prefix>]");
+ System.err.println("--events=<EventsPath> --reduce=<ReducePath> --ttls=<TTLsPath> --executions=<ExecutionsPath> [--prefix=<prefix>]");
System.exit(1);
}
@@ -40,6 +41,8 @@ public class Main {
String eventsPath = arguments.get("events");
String reducePath = arguments.get("reduce");
+ String ttlsPath = arguments.get("ttls");
+ String executionsPath = arguments.get("executions");
String prefix = arguments.get("prefix");
String uncachedPath = arguments.get("uncached");
String size = arguments.get("size");
@@ -50,7 +53,9 @@ public class Main {
if (prefix == null) {
prefix = "";
}
- if (eventsPath != null && size != null) {
+ if (ttlsPath != null && executionsPath != null) {
+ Reducer.savedTimeAndTimeInCache(eventsPath, ttlsPath, executionsPath, reducePath, prefix);
+ } else if (eventsPath != null && size != null) {
Reducer.size(eventsPath, reducePath, prefix);
} else if (eventsPath != null) {
Reducer.reduce(eventsPath, reducePath, prefix);
diff --git a/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Reducer.java b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Reducer.java
index 549e7da..2ee1713 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Reducer.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/cache/tools/Reducer.java
@@ -14,6 +14,7 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -82,4 +83,85 @@ public class Reducer {
}
}
+ private static class Method {
+
+ String name;
+ String reference;
+ long ttl;
+ double avgexecution;
+ int hits;
+ int additions;
+
+ double getSavedTime() {
+ return avgexecution * hits;
+ }
+
+ long getTimeInCache() {
+ return ttl * additions;
+ }
+
+ }
+
+ public static void savedTimeAndTimeInCache(String eventsPath, String recommendedTTLsPath, String averageExecutionsPath, String reducePath, String prefix) {
+ CSV recommendedTTLs = CSV.read(recommendedTTLsPath);
+ CSV averageExecutions = CSV.read(averageExecutionsPath);
+
+ Map<String, Method> methods = new HashMap<>();
+
+ recommendedTTLs.forEach(row -> {
+ String reference = row.get("reference");
+ Optional<CSV.Row> selectFirst = averageExecutions.selectFirst("reference", reference);
+ if (selectFirst.isPresent()) {
+ Method method = new Method();
+ method.reference = reference;
+ method.ttl = Long.parseLong(row.get("ttl"));
+ method.avgexecution = Double.parseDouble(selectFirst.get().get("avgexecution"));
+ if (methods.containsKey(reference)) {
+ throw new RuntimeException("Trying to overwrite reference " + reference);
+ }
+ String uuid = row.get("application") + "," + row.get("version") + "," + row.get("group") + "," + row.get("method");
+ methods.put(uuid, method);
+ }
+ });
+
+ String[] splitPrefix = prefix.split(",");
+ String application = splitPrefix[0];
+ String version = splitPrefix[1];
+ String group = splitPrefix[2];
+
+ String uuid = application + "," + version + "," + group + ",";
+
+ try (Stream<String> lines = Files.lines(Paths.get(eventsPath))) {
+ Gson gson = new Gson();
+ lines.forEach(line -> {
+ CacheEvent event = gson.fromJson(line, CacheEvent.class);
+ if (event.getType().equals(EventType.HIT) || event.getType().equals(EventType.ADDITION)) {
+ Method method = methods.get(uuid + event.getName());
+ if (method == null) {
+ throw new RuntimeException("Method in events was not recommended " + uuid + event.getName());
+ }
+ if (event.getType().equals(EventType.HIT)) {
+ method.hits++;
+ } else {
+ method.additions++;
+ }
+ }
+ });
+
+ try (FileWriter fileWriter = new FileWriter(reducePath, true)) {
+ methods.values().forEach(method -> {
+ try {
+ if (method.hits + method.additions > 0) {
+ fileWriter.write(prefix + method.reference + "," + method.avgexecution + "," + method.ttl + "," + method.additions + "," + method.hits + "," + method.getSavedTime() + "," + method.getTimeInCache() + "\n");
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.SEVERE, "IOException {0}", ex);
+ }
+ });
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.SEVERE, "file not found {0}", eventsPath);
+ }
+ }
+
}