/*
* Carrot2 project.
*
* Copyright (C) 2002-2016, Dawid Weiss, Stanisław Osiński.
* All rights reserved.
*
* Refer to the full license file "carrot2.LICENSE"
* in the root folder of the repository checkout or at:
* http://www.carrot2.org/carrot2.LICENSE
*/
package org.carrot2.workbench.core.ui;
import static org.apache.commons.lang.SystemUtils.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import org.apache.commons.lang.mutable.MutableInt;
import org.carrot2.core.*;
import org.carrot2.core.attribute.AttributeNames;
import org.carrot2.util.CloseableUtils;
import org.carrot2.workbench.core.WorkbenchCorePlugin;
import org.carrot2.workbench.core.helpers.Utils;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.carrot2.shaded.guava.common.collect.Maps;
/**
* Actual background benchmarking job.
*/
final class BenchmarkJob extends Job
{
/**
* Search input for this job.
*/
private final SearchInput input;
/**
* A clone of benchmark settings for this job.
*/
final BenchmarkSettings settings;
/**
* Public volatile statistics when the job is in progress.
*/
public volatile BenchmarkStatistics statistics;
/**
* Shared log writer. You must synchronize on this object to write.
*/
public PrintWriter logWriter;
/**
* Log file (if {@link #logWriter} is writing to a file).
*/
public File logFile;
public BenchmarkJob(SearchInput input, BenchmarkSettings settings)
{
super("Benchmarking...");
this.settings = settings;
this.input = input;
this.statistics = new BenchmarkStatistics(settings.warmupRounds, settings.benchmarksRounds);
}
/* */
@Override
protected IStatus run(IProgressMonitor monitor)
{
prepareLogs();
pushLogHeaders();
// Create a pool of executing threads.
final int totalRounds = settings.getTotalRounds();
final CountDownLatch latch = new CountDownLatch(1);
final MutableInt rounds = new MutableInt(totalRounds);
final Callable<Long> benchmarkRunner = createBenchmarkRunner();
final Thread [] pool = createBenchmarkThreads(latch, rounds, monitor, benchmarkRunner);
// Start all benchmark threads at once and wait for all of them to finish.
monitor.beginTask("Running...", totalRounds);
latch.countDown();
for (Thread t : pool)
{
try
{
t.join();
}
catch (InterruptedException e)
{
// If interrupted, fall through, but terminate all threads whenever possible.
monitor.setCanceled(true);
}
}
synchronized (logWriter)
{
if (monitor.isCanceled())
{
logWriter.println("# (cancelled)");
}
else
{
logWriter.println("# Statistics:");
logWriter.println("# " + statistics.toString());
}
}
monitor.done();
CloseableUtils.close(logWriter);
return Status.OK_STATUS;
}
/**
* Return the benchmark runner.
*/
private Callable<Long> createBenchmarkRunner()
{
final WorkbenchCorePlugin core = WorkbenchCorePlugin.getDefault();
final Controller controller = core.getController();
final ProcessingComponentDescriptor source = core.getComponent(input.getSourceId());
final ProcessingComponentDescriptor algorithm = core.getComponent(input.getAlgorithmId());
final Map<String, Object> attributes =
Maps.newHashMap(input.getAttributeValueSet().getAttributeValues());
return new Callable<Long>() {
public Long call() throws Exception
{
final long start = System.currentTimeMillis();
controller.process(attributes, source.getId(), algorithm.getId());
return System.currentTimeMillis() - start;
}
};
}
/**
* Create a set of benchmark threads.
*/
private Thread [] createBenchmarkThreads(
final CountDownLatch latch,
final MutableInt rounds,
final IProgressMonitor monitor,
final Callable<Long> benchmarkRunner)
{
final Thread [] pool = new Thread [settings.threads];
for (int i = 0; i < settings.threads; i++)
{
final int threadID = i + 1;
pool[i] = new Thread() {
public void run()
{
while (!Thread.currentThread().isInterrupted())
{
final int totalRounds = settings.warmupRounds + settings.benchmarksRounds;
final int round;
synchronized (logWriter)
{
round = rounds.intValue();
if (round == 0 || monitor.isCanceled())
{
// Exit if tests finished.
return;
}
rounds.decrement();
}
long time;
try
{
time = benchmarkRunner.call();
}
catch (Exception e)
{
Utils.logError(e, false);
time = 0;
}
synchronized (logWriter)
{
logWriter.format(Locale.ENGLISH,
"%2d %4d %8.03f\n",
threadID, totalRounds - round, time / 1000d);
statistics = statistics.update((int) time);
monitor.worked(1);
}
}
}
};
pool[i].setName("benchmark-" + i);
pool[i].setPriority(settings.priority.threadPriority);
pool[i].start();
}
return pool;
}
/**
* Push log headers.
*/
private void pushLogHeaders()
{
if (logWriter == null) return;
logWriter.println("# Benchmarking log.");
logWriter.println("# ");
logWriter.println("# Source: " + input.getSourceId());
logWriter.println("# Algorithm: " + input.getAlgorithmId());
logWriter.println("# Query: " + input.getAttribute(AttributeNames.QUERY));
logWriter.println("# Results: " + input.getAttribute(AttributeNames.RESULTS));
logWriter.println("# ");
logWriter.println("# JVM: " + JAVA_VM_NAME
+ ", " + JAVA_VM_VERSION
+ ", " + JAVA_VM_INFO
+ ", " + JAVA_VM_VENDOR);
logWriter.println("# OS arch: " + OS_ARCH
+ ", name: " + OS_NAME
+ ", version: " + OS_VERSION);
logWriter.println("# ");
logWriter.println("# Benchmark rounds: " + settings.benchmarksRounds);
logWriter.println("# Warmup rounds: " + settings.warmupRounds);
logWriter.println("# Threads: " + settings.threads + " (available CPUs or cores: "
+ Runtime.getRuntime().availableProcessors() + "), "
+ "running at " + settings.priority + " priority.");
logWriter.println("# ");
logWriter.println("# thread_id round_number time[s]");
}
/**
* Prepare log output.
*/
private void prepareLogs()
{
final String logName =
String.format(Locale.ENGLISH,
"benchmark_%1$tF_%1$tH-%1$tM-%1$tS.txt", new Date());
try
{
if (this.settings.logDirectory != null)
{
this.logFile = new File(settings.logDirectory, logName);
}
else
{
this.logFile = new File(System.getProperty("java.io.tmpdir", "."), logName);
}
this.logWriter = new PrintWriter(logFile, "UTF-8");
}
catch (IOException e)
{
Utils.logError(e, false);
this.logFile = null;
this.logWriter = null;
}
}
}