/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2002-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.referencing.factory.epsg;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.awt.geom.Point2D;
import java.util.concurrent.CountDownLatch;
import java.util.Map;
import org.apache.sis.math.Statistics;
import org.apache.sis.util.Classes;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assume.assumeNotNull;
/**
* Stresses the {@link ThreadedEpsgFactory}.
*
* @author Jody Garnett (Refractions)
* @author Martin Desruisseaux (Geomatys)
* @version 3.00
*
* @since 2.4
*/
public final strictfp class StressTest extends EpsgFactoryTestBase {
/**
* Number of thread running concurrently. This number must be greater than the default
* number of workers in {@link ThreadedEpsgFactory}, which is currently {@value #MAX_WORKERS}.
*/
private static final int RUNNER_COUNT = 32;
/**
* The default maximal number of workers. Actually this number is hard coded in
* {@link ThreadedAuthorityFactory}, but we can't access it since we are not in
* the same package and we don't want to make this value public (because it is
* too likely to change as we experiment tradeoff).
*/
private static final int MAX_WORKERS = 16;
/**
* Number of iterations to perform in each thread.
*/
static final int ITERATIONS = 300;
/**
* Creates a test suite for the default database.
*/
public StressTest() {
super();
}
/**
* Tests the execution of many concurrent threads.
*
* @throws Throwable If any kind of error occurred (may be from a client thread).
*/
@Test
@Ignore
public final void testRunners() throws Throwable {
assumeNotNull(factory);
final CountDownLatch lock = new CountDownLatch(1);
final ClientThread runners[] = new ClientThread[RUNNER_COUNT];
for (int i=0; i<RUNNER_COUNT; i++) {
final ClientThread thread = new ClientThread(i+1, factory, lock);
runners[i] = thread;
thread.start(); // Will block on the count down latch.
}
/*
* Lets every threads go and wait for them to complete.
* We measure the time elapsed in this process.
*/
long timeElapsed = System.currentTimeMillis();
lock.countDown();
for (int i=0; i<RUNNER_COUNT; i++) {
runners[i].join();
}
timeElapsed = System.currentTimeMillis() - timeElapsed;
/*
* Gets the metric et reports the first failure found in each thread.
* If a failure has been found, it will cause the test to fail at the
* end of this method.
*/
final Map<Integer, Point2D.Double> result = ClientThread.createEmptyResultMap();
final Statistics statistics = new Statistics(null);
Throwable exception = null;
for (int i=0; i<RUNNER_COUNT; i++) {
final ClientThread thread = runners[i];
final Throwable e = thread.exception;
if (e != null) {
// Remember the first exception (to be throw below).
if (exception == null) {
exception = e;
} else {
exception.addSuppressed(e);
}
System.err.println(Classes.getShortClassName(e) + " in thread " +
thread.id + " for code " + thread.badCode);
}
statistics.combine(thread.statistics);
// Check the consistency between different threads.
for (final Map.Entry<Integer, Point2D.Double> entry : thread.result.entrySet()) {
ClientThread.assertConsistent(result, entry.getKey(), entry.getValue());
}
}
/*
* Reports the metric.
*/
final PrintWriter out = StressTest.out;
if (out != null) {
final int cumulativeIteration = statistics.count();
final double averageTime = statistics.mean() / 1E+6;
final double throughput = 1000 / averageTime;
final double minTime = statistics.minimum() / 1E+6;
final double maxTime = statistics.maximum() / 1E+6;
out.println("Number of clients: " + RUNNER_COUNT);
out.println("Number of workers: " + MAX_WORKERS);
out.println("Iterations/thread: " + ITERATIONS);
out.println("Cumulative Iter.: " + cumulativeIteration);
out.println("Average Time (ms): " + averageTime);
out.println("Overall Time (ms): " + timeElapsed);
out.println("Throughput (kHz): " + throughput);
out.println("Minimum time (ms): " + minTime);
out.println("Maximum time (ms): " + maxTime);
out.println("Number CRS codes: " + result.size() + " / " + ClientThread.CODES.length);
out.flush();
/*
* Append results to file.
*/
if (false) {
final String content =
"" + RUNNER_COUNT +
", " + MAX_WORKERS +
", " + ITERATIONS +
", " + averageTime +
", " + timeElapsed +
", " + cumulativeIteration +
", " + throughput +
", " + minTime +
", " + maxTime;
final File file = new File(System.getProperty("user.home"), "epsg-stress.csv");
final boolean created = file.createNewFile();
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file, true))) {
if (created) {
bw.write("THREADS, MAX_WORKERS, ITERATIONS_PER_THREAD, " +
"AVG_TIME, TOTAL_TIME, TOTAL_RUNS, THROUGHPUT, MIN_TIME, MAX_TIME");
bw.newLine();
}
bw.write(content);
bw.newLine();
}
}
}
/*
* If the test failed, reports the first failure.
*/
if (exception != null) {
throw exception;
}
}
}