package net.i2p.router.transport;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Random;
import org.junit.BeforeClass;
import org.junit.Test;
import static junit.framework.TestCase.*;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
/**
* Stress out the bandwidth limiter by running a series of push and pull tests
* through bandwidth limited streams. This includes pushing data through
* unthrottled streams, through streams throttled at 4KBps, 32KBps, and 256KBps,
* pulling data through those same rates, as well as doing so with 10 concurrent
* threads (and, in turn, 10 concurrent streams all using the same BandwidthLimiter).
*
* Note: this takes a long time to run (~1 hour) since the 4KBps push/pull of 1MB with
* 10 concurrent threads is, well, slow.
*
*/
public class BandwidthLimiterTest {
private static RouterContext _context;
private final static int NUM_KB = 256;
@BeforeClass
public static void setUp() {
_context = new RouterContext(new Router());
_context.initAll();
}
private void prepareLimiter(int inKBps, int outKBps, int inBurst, int outBurst) {
Properties props = System.getProperties();
props.setProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, ""+inKBps);
props.setProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, ""+outKBps);
props.setProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH_PEAK, ""+inBurst);
props.setProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH_PEAK, ""+outBurst);
//props.setProperty(TrivialBandwidthLimiter.PROP_REPLENISH_FREQUENCY, ""+10*1000);
System.setProperties(props);
_context.bandwidthLimiter().reinitialize();
}
/**
* Using the configured limiter, determine how long it takes to shove
* numBytes through a BandwidthLimitedOutputStream (broken up into numBytesPerWrite)
* chunks.
*
*/
private long testOutboundThrottle(int numBytes, int numBytesPerWrite) {
byte source[] = new byte[numBytesPerWrite];
new Random().nextBytes(source);
NullOutputStream target = new NullOutputStream();
BandwidthLimitedOutputStream out = new BandwidthLimitedOutputStream(_context, target, null);
long before = System.currentTimeMillis();
try {
for (int i = 0; i < numBytes; i += numBytesPerWrite) {
int num = numBytesPerWrite;
if (numBytesPerWrite + i >= numBytes)
num = numBytes - i;
//_log.info("** Writing " + num + " bytes starting at " + i);
out.write(source, 0, num);
}
} catch (IOException ioe) {}
long after = System.currentTimeMillis();
return after-before;
}
/**
* Using the configured limiter, determine how long it takes to read
* numBytes through a BandwidthLimitedInputStream (broken up into numBytesPerRead)
* chunks.
*
*/
private long testInboundThrottle(int numBytes, int numBytesPerRead) {
FakeInputStream source = new FakeInputStream(numBytes);
BandwidthLimitedInputStream in = new BandwidthLimitedInputStream(_context, source, null);
long before = System.currentTimeMillis();
try {
byte buf[] = new byte[numBytesPerRead];
int read = 0;
while ( (read = in.read(buf)) != -1) {
//_log.info("** Read " + read + " bytes");
// gobble the data. who cares
}
} catch (IOException ioe) {}
long after = System.currentTimeMillis();
return after-before;
}
/**
* Run a series of tests on outbound throttling (shoving lots of data through pipes
* with various limits) and log the times.
*
*/
@Test
public void testOutbound() {
double error;
double predict;
prepareLimiter(-1, -1, -1, -1);
long ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
/*prepareLimiter(-1, 4, -1, 4*1024);
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/4)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);*/
prepareLimiter(-1, 32, -1, 32*1024);
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/32)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);
prepareLimiter(-1, 256, -1, 256*1024);
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/256)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);
}
/**
* Run a series of tests on inbound throttling (pulling lots of data through pipes
* with various limits) and log the times.
*
*/
@Test
public void testInbound() {
double predict;
double error;
prepareLimiter(-1, -1, -1, -1);
long ms = testInboundThrottle(NUM_KB*1024, 1*1024);
/*prepareLimiter(4, -1, 4*1024, -1);
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/4)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);*/
prepareLimiter(32, -1, 32*1024, -1);
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/32)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);
prepareLimiter(256, -1, 256*1024, -1);
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
predict = (NUM_KB/256)*1000;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);
}
@Test
public void testOutboundContention() {
double predict;
double error;
long ms;
long end;
long start;
prepareLimiter(-1, -1, -1, -1);
start = System.currentTimeMillis();
//long runningTimes[] = testOutboundContention(10, NUM_KB*1024);
end = System.currentTimeMillis();
//prepareLimiter(-1, 4, -1, 5*1024*1024);
//start = System.currentTimeMillis();
//runningTimes = testOutboundContention(10, NUM_KB*1024);
//end = System.currentTimeMillis();
//prepareLimiter(-1, 32, -1, 32*1024);
//start = System.currentTimeMillis();
//runningTimes = testOutboundContention(10, NUM_KB*1024);
//end = System.currentTimeMillis();
prepareLimiter(-1, 256, -1, 256*1024);
start = System.currentTimeMillis();
testOutboundContention(10, NUM_KB*1024);
end = System.currentTimeMillis();
ms = end-start;
predict = (NUM_KB/256)*1000*10;
error = predict/ms;
//assertTrue(error>.89);
assertTrue(error<1.05);
}
private long[] testOutboundContention(int numConcurrent, int numBytes) {
OutboundRunner threads[] = new OutboundRunner[numConcurrent];
for (int i = 0; i < numConcurrent; i++) {
threads[i] = new OutboundRunner(numBytes);
}
for (int i = 0; i < numConcurrent; i++)
threads[i].start();
for (int i = 0; i < numConcurrent; i++) {
try {
threads[i].join();
} catch (InterruptedException ie) {}
}
long rv[] = new long[numConcurrent];
for (int i = 0; i < numConcurrent; i++)
rv[i] = threads[i].getRunningTime();
return rv;
}
private static int __runnerNum = 0;
private class OutboundRunner extends Thread {
private int _numBytes;
private int _runnerNum;
private long _runningTime;
public OutboundRunner(int numBytes) {
_numBytes = numBytes;
_runnerNum = ++__runnerNum;
}
public void run() {
Thread.currentThread().setName("Out" + _runnerNum);
_runningTime = testOutboundThrottle(_numBytes, 8*1024);
}
public long getRunningTime() { return _runningTime; }
}
}
class NullOutputStream extends OutputStream {
public void write(int param) {}
}
class FakeInputStream extends InputStream {
private volatile int _numRead;
private int _size;
public FakeInputStream(int size) {
_size = size;
_numRead = 0;
}
public int read() {
int rv = 0;
if (_numRead >= _size)
rv = -1;
else
rv = 42;
_numRead++;
return rv;
}
}