= Move benchmark core to fit existing standard

This commit is contained in:
Matthew Cech
2019-09-11 14:03:02 -07:00
parent d1dfab411e
commit 28377cf78e
12 changed files with 25 additions and 21 deletions
@@ -2,11 +2,11 @@ package commands.benchmark;
import java.util.List;
import core.benchmark.BenchmarkCommand;
import core.benchmark.BenchmarkEntry;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkInput;
import core.benchmark.BenchmarkManager;
import commands.benchmark.core.BenchmarkCommand;
import commands.benchmark.core.BenchmarkEntry;
import commands.benchmark.core.BenchmarkFormattable;
import commands.benchmark.core.BenchmarkInput;
import commands.benchmark.core.BenchmarkManager;
public class BenchmarkCommandCompare extends BenchmarkCommand
{
@@ -2,7 +2,11 @@ package commands.benchmark;
import java.util.List;
import core.benchmark.*;
import commands.benchmark.core.BenchmarkCommand;
import commands.benchmark.core.BenchmarkEntry;
import commands.benchmark.core.BenchmarkFormattable;
import commands.benchmark.core.BenchmarkInput;
import commands.benchmark.core.BenchmarkManager;
public class BenchmarkCommandFind extends BenchmarkCommand
{
@@ -2,11 +2,11 @@ package commands.benchmark;
import java.util.List;
import core.benchmark.BenchmarkCommand;
import core.benchmark.BenchmarkEntry;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkInput;
import core.benchmark.BenchmarkManager;
import commands.benchmark.core.BenchmarkCommand;
import commands.benchmark.core.BenchmarkEntry;
import commands.benchmark.core.BenchmarkFormattable;
import commands.benchmark.core.BenchmarkInput;
import commands.benchmark.core.BenchmarkManager;
import dataStructures.KittyEmbed;
import java.awt.Color;
+2 -2
View File
@@ -1,9 +1,9 @@
package commands.benchmark;
import commands.benchmark.core.BenchmarkFormattable;
import commands.benchmark.core.BenchmarkFramework;
import core.Command;
import core.LocStrings;
import core.benchmark.BenchmarkFormattable;
import core.benchmark.BenchmarkFramework;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -0,0 +1,10 @@
package commands.benchmark.core;
public abstract class BenchmarkCommand
{
public BenchmarkCommand()
{ }
// OVERRIDE ME
public abstract BenchmarkFormattable onRun(BenchmarkManager manager, BenchmarkInput input);
}
@@ -0,0 +1,27 @@
package commands.benchmark.core;
public class BenchmarkEntry
{
public final BenchmarkType type;
public final String partNumber;
public final String brand;
public final String model;
public final int rank;
public final float benchmark;
public final int samples;
public final String url;
BenchmarkEntry(String input)
{
String[] row = input.split(",");
type = BenchmarkType.valueOf(row[0]);
partNumber = row[1];
brand = row[2];
model = row[3];
rank = Integer.parseInt(row[4]);
benchmark = Float.parseFloat(row[5]);
samples = Integer.parseInt(row[6]);
url = row[7];
}
}
@@ -0,0 +1,40 @@
package commands.benchmark.core;
import dataStructures.KittyEmbed;
import dataStructures.Response;
// Only one isn't null!
public class BenchmarkFormattable
{
public final KittyEmbed resEmbed;
public final String resString;
// Default constructor is hidden and disabled.
// If somehow it is called, defaults to an empty string and no embed.
@SuppressWarnings("unused")
private BenchmarkFormattable()
{
this.resEmbed = null;
this.resString = "";
}
public BenchmarkFormattable(String res)
{
this.resEmbed = null;
this.resString = res;
}
public BenchmarkFormattable(KittyEmbed embed)
{
this.resEmbed = embed;
this.resString = null;
}
public void call(Response res)
{
if(resEmbed == null)
res.send(resString);
else
res.send(resEmbed);
}
}
@@ -0,0 +1,59 @@
package commands.benchmark.core;
import java.util.HashMap;
import commands.benchmark.*;
// Based on general core structure, this registers sub-commands and keeps tabs on them.
public class BenchmarkFramework
{
// Variables
public HashMap<String, BenchmarkCommand> benchmarkCommand;
public BenchmarkManager benchmarkManager;
// Constructor and command registration
public BenchmarkFramework()
{
this.benchmarkManager = new BenchmarkManager();
this.benchmarkCommand = new HashMap<String, BenchmarkCommand>();
registerCommand("find", new BenchmarkCommandFind());
registerCommand("compare", new BenchmarkCommandCompare());
registerCommand("info", new BenchmarkCommandInfo());
}
// Runs a command if possible.
public BenchmarkFormattable run(String args)
{
BenchmarkInput input = new BenchmarkInput(args);
return executeCommand(input.key, input);
}
public void update()
{
synchronized(benchmarkManager)
{
benchmarkManager.update();
}
}
// Registers a command
private void registerCommand(String commandName, BenchmarkCommand command)
{
commandName = commandName.toLowerCase();
if(benchmarkCommand.put(commandName, command) != null)
BenchmarkLog.log("Multiple registration of a command with name '" + commandName + "'!");
BenchmarkLog.log("Registered " + commandName);
}
// Executes a command with the specified name, and provides it with some extra input data.
private BenchmarkFormattable executeCommand(String name, BenchmarkInput input)
{
BenchmarkCommand command = benchmarkCommand.get(name.toLowerCase());
if(command != null && benchmarkManager != null)
return command.onRun(benchmarkManager, input);
return null;
}
}
@@ -0,0 +1,34 @@
package commands.benchmark.core;
import utils.StringUtils;
// Lifted from an early iteration of RPGInput
public class BenchmarkInput
{
public String raw;
public String key;
public String value;
public BenchmarkInput(String raw)
{
this.raw = raw;
this.key = "";
this.value = "";
if(raw == null || raw.length() == 0)
return;
raw = raw.trim();
int whitespacePos = StringUtils.findFirstWhitespace(raw);
if(whitespacePos == -1)
{
key = raw;
return;
}
key = raw.substring(0, whitespacePos).trim();
value = raw.substring(whitespacePos).trim();
}
}
@@ -0,0 +1,11 @@
package commands.benchmark.core;
import utils.GlobalLog;
//Logging shim
public class BenchmarkLog
{
public static void log(String str) { GlobalLog.log("[Benchmark] " + str); }
public static void warn(String str) { GlobalLog.warn("[Benchmark] " + str); }
public static void error(String str) { GlobalLog.error(" [Benchmark] " + str); }
}
@@ -0,0 +1,189 @@
package commands.benchmark.core;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import core.Constants;
import dataStructures.Pair;
import utils.io.DirectoryMonitor;
import utils.io.FileUtils;
import utils.io.MonitoredFile;
// All things considered, this doesn't need to be particularly efficient since anything
// less than 10ms of search time won't be noticable to an end user really, not for
// a networked application that's not performance bound.
public class BenchmarkManager
{
// Variables
public final String directory = Constants.AssetDirectory + "userbench/";
public final String extension = ".csv";
public final String lineDelimiter = "\n";
private List<BenchmarkEntry> raw;
private DirectoryMonitor directoryMonitor;
private boolean needsUpdate;
// Read in all extensions
public BenchmarkManager()
{
long start = Instant.now().toEpochMilli();
raw = new ArrayList<BenchmarkEntry>();
directoryMonitor = new DirectoryMonitor(directory);
needsUpdate = false;
rebuildLookup();
BenchmarkLog.log("Took " + (Instant.now().toEpochMilli() - start) + " ms to load " + raw.size() + " entries from " + directoryMonitor.getCurrentFiles().size() + " file" + (raw.size() > 1 ? "s" : ""));
}
// Rebuilds the files being monitored
public void rebuildLookup()
{
long start = Instant.now().toEpochMilli();
synchronized(raw)
{
raw.clear();
List<MonitoredFile> files;
synchronized(directoryMonitor)
{
files = directoryMonitor.getCurrentFiles();
}
if(files != null)
{
for(MonitoredFile mf : files)
{
String contents = FileUtils.readContent(mf.path);
String[] lines = contents.split(lineDelimiter);
for(int i = 1; i < lines.length; ++i)
raw.add(new BenchmarkEntry(lines[i]));
}
}
else
{
BenchmarkLog.warn("No " + extension + " files where found in " + directory);
}
}
long end = Instant.now().toEpochMilli();
BenchmarkLog.log("Rebuilt data in " + (end - start) + "ms");
}
// Re-evaluates and re-orders the input list based on the Levenshtein distance of the
// original search and the contents of the provided list of benchmark entries.
public List<BenchmarkEntry> evaluateLevenshteinDistance(List<BenchmarkEntry> entries, String search)
{
// Populate cost list with heuristic results
List<Pair<BenchmarkEntry, Integer>> cost = new ArrayList<Pair<BenchmarkEntry, Integer>>();
int bestSoFar = Integer.MAX_VALUE;
for(int i = 0; i < entries.size(); ++i)
{
BenchmarkEntry entry = entries.get(i);
String entryString = entry.brand + " " + entry.model;
int heuristic = levenshteinHeuristic(entryString, search, bestSoFar);
if(heuristic < bestSoFar)
bestSoFar = heuristic;
cost.add(new Pair<BenchmarkEntry, Integer>(entry, heuristic));
}
// Sort list
Collections.sort(cost, (i1, i2) -> i1.Second.compareTo(i2.Second));
// Build new output list
List<BenchmarkEntry> sortedEntries = new ArrayList<BenchmarkEntry>();
for(int i = 0; i < cost.size(); ++i)
sortedEntries.add(cost.get(i).First);
return sortedEntries;
}
// Finds the minimum integer in an array of ints and returns it.
private int min(int[] arr)
{
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; ++i)
{
if(arr[i] < min)
min = arr[i];
}
return min;
}
// Returns cost based on distance of characters from string. This is a kinda sloppy way
// to do it, but because I keep tabs on the best result so far, it could be much worse.
// Still chucks a lot - a non-recursive result w/ memoization would be best but this will do.
private int levenshteinHeuristic(String str1, String str2, int bestSoFar)
{
int cost;
if(str1.length() <= 0)
return str2.length();
if(str2.length() <= 0)
return str1.length();
if(str1.charAt(0) == str2.charAt(0))
cost = 0;
else
cost = 1;
int distance = Math.abs(str1.length() - str2.length());
if(distance > bestSoFar)
return distance;
int s1 = levenshteinHeuristic(str1.substring(1), str2, bestSoFar) + 1;
int s2 = levenshteinHeuristic(str1, str2.substring(1), bestSoFar) + 1;
int s3 = levenshteinHeuristic(str1.substring(1), str2.substring(1), bestSoFar) + cost;
return min(new int[] {s1, s2, s3 });
}
// Search for a substring in the model name
public List<BenchmarkEntry> findModel(String modelSubstr)
{
long start = Instant.now().toEpochMilli();
List<BenchmarkEntry> matching = new ArrayList<BenchmarkEntry>();
String searchSubstr = modelSubstr.toLowerCase();
for(BenchmarkEntry e : raw)
{
String model = e.model.toLowerCase().trim();
if(model.contains(searchSubstr))
matching.add(e);
}
BenchmarkLog.log("Searched for '" + searchSubstr + "' for "+ (Instant.now().toEpochMilli() - start) + "ms and found " + matching.size() + " entries.");
return matching;
}
// Keeps tabs on any changes of the files.
public void update()
{
needsUpdate = false;
directoryMonitor.update(this::onRescan, this::onRescan, this::onRescan);
if(needsUpdate)
rebuildLookup();
}
// When a file is changed, handle it.
private void onRescan(MonitoredFile file)
{
// For now, all we need is to note that something was adjusted.
if(file.path.toString().contains(extension))
{
BenchmarkLog.log("File status changed: " + file.path);
needsUpdate = true;
}
}
}
@@ -0,0 +1,17 @@
package commands.benchmark.core;
public enum BenchmarkType
{
CPU(0), GPU(1), SSD(2), HDD(4), RAM(8), USB(16);
private final int value;
private BenchmarkType(int value)
{
this.value = value;
}
public int getValue()
{
return value;
}
}