package eu.toolchain.ffwd;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
public class FastForwardPerf {
@Option(name = "-h", aliases = { "--help" }, help = true, usage = "Display help")
private boolean help;
@Option(name = "--attribute-count", usage = "Control the number of attributes to use (default: 8)")
private int attributeCount = 8;
@Option(name = "--threads", usage = "Control number of threads to use (default: 2)")
private int threads = 2;
@Option(name = "--count", usage = "Control number of metrics to send per patch (default: 100000)")
private int count = 10000;
@Option(name = "--batches", usage = "Control how many batches of metrics to send (default: 1000)")
private int batches = 1000;
@Option(name = "--even", usage = "Attempt to send metrics evenly over the given time period in ms (default: disabled)")
private long even = 200;
public static void main(String argv[]) throws Exception {
new FastForwardPerf().run(argv);
}
public void run(String argv[]) throws Exception {
final CmdLineParser parser = new CmdLineParser(this);
parser.parseArgument(argv);
if (help) {
parser.printUsage(System.out);
return;
}
final Map<String, String> attributes = buildBaseAttributes();
final ExecutorService executor = Executors.newFixedThreadPool(threads);
final Metric m = FastForward.metric("test").attributes(attributes);
System.out.println("Sending:");
for (int id = 0; id < batches; id++) {
System.out.print(".");
System.out.flush();
if ((id + 1) % 100 == 0)
System.out.println();
final CountDownLatch latch = new CountDownLatch(threads);
final Batch batch = new Batch(m, latch, System.nanoTime());
for (int i = 0; i < threads; i++)
executor.submit(new BatchRunnable(batch));
latch.await();
for (final Throwable error : batch.errors) {
System.out.println("Error in batch #" + id);
error.printStackTrace(System.out);
}
}
System.out.println();
System.out.println("Shutting down threads");
executor.shutdown();
System.exit(0);
}
private Map<String, String> buildBaseAttributes() {
final Map<String, String> attributes = new HashMap<>();
for (int i = 0; i < attributeCount; i++)
attributes.put("attribute" + i, "attribute-value-" + i);
return attributes;
}
@RequiredArgsConstructor
private final class BatchRunnable implements Runnable {
private final Batch batch;
@Override
public void run() {
try {
once();
} catch (Exception e) {
batch.errors.add(e);
}
batch.latch.countDown();
}
private void once() throws Exception {
final FastForward client = FastForward.setup();
final long period = (even * 1000000) / count;
while (true) {
int i = batch.position.getAndIncrement();
if (i >= count)
break;
final Metric m = batch.m.value(i * 1.0);
if (even == 0) {
client.send(m);
continue;
}
final long now = System.nanoTime();
// what is the expected time that we should run?
final long expected = batch.started + (i * period) / threads;
final long diff = (expected - now) / 1000000;
if (diff > 0)
Thread.sleep(diff);
client.send(m);
}
}
};
@Data
private static class Batch {
final AtomicInteger position = new AtomicInteger();
final ConcurrentLinkedQueue<Throwable> errors = new ConcurrentLinkedQueue<>();
final Metric m;
final CountDownLatch latch;
final long started;
}
}