package org.jgroups.tests;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.protocols.TP;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.stack.ProtocolStack;
import org.jgroups.util.Average;
import org.jgroups.util.Promise;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* Measure the latency between messages with message bundling enabled at the transport level.
* Update April 2016: this test is more or less useless, as System.nanoTime() can yield different values, depending
* on which core it is run. E.g. the sender (main) thread might run on core-0, but the receiver thread on core-1,
* and since the cores can have different counters for nanoTime(), subtracting the values is meaningless.
* System.nanoTime() only really works when invoked by the same thread, e.g. as in {@link #testSimple()}.</p>
* This was changed by making the sender block on a promise which is signalled by the receiver thread when a message
* has been received. After that, System.nanoTime() is called by the sender, so start and stop times are called by the
* same thread. Of course, (uncontended) lock acquisition, thread context switching etc amount to some overhead...
*
* @author Bela Ban
*/
@Test(groups=Global.STACK_DEPENDENT,singleThreaded=true)
public class MessageBundlingTest extends ChannelTestBase {
private JChannel a, b;
private final Promise<Boolean> promise=new Promise<>();
private SimpleReceiver r2;
private final static long LATENCY=1500L;
private final static long LATENCY_NS=LATENCY * 1_000_000_000L;
private final static long SLEEP=5000L; // ms
private final static long SLEEP_NS=SLEEP * 1_000_000_000;
private static final int MAX_BYTES=62000;
@BeforeMethod
protected void createChannels() throws Exception {
a=createChannel(true, 2, "A");
setBundling(a,MAX_BYTES);
a.connect("MessageBundlingTest");
b=createChannel(a, "B");
r2=new SimpleReceiver(promise);
b.setReceiver(r2);
b.connect("MessageBundlingTest");
Util.waitUntilAllChannelsHaveSameView(10000, 1000, a, b);
}
protected JChannel create(String name) throws Exception {
return new JChannel(Util.getTestStack()).name(name);
}
@AfterMethod void tearDown() throws Exception {promise.reset(false); Util.close(b,a);}
protected boolean useBlocking() {return false;}
public void testSimple() throws Exception {
long start=System.nanoTime();
a.send(new Message());
promise.getResult(5000);
long diff=System.nanoTime() - start;
System.out.printf("took %s to send and receive a multicast message\n", print(diff));
assert diff < SLEEP_NS /2;
}
public void testLatencyWithoutMessageBundling() throws Exception {
_testLatencyWithoutMessageBundling(true);
}
public void testLatencyWithMessageBundling() throws Exception {
_testLatencyWithoutMessageBundling(false);
}
public void testLatencyWithMessageBundlingAndMaxBytes() throws Exception {
final int num=500;
final Average avg=new Average();
long min=Long.MAX_VALUE, max=0;
System.out.printf(">>> sending %s messages\n", num);
long[] times=new long[num];
for(int i=0; i < num; i++) {
long start=System.nanoTime();
a.send(new Message(null, new byte[4000]));
promise.getResult(SLEEP);
long time=System.nanoTime()-start;
times[i]=time;
avg.add(time);
min=Math.min(min, time);
max=Math.max(max, time);
promise.reset(false);
}
for(int i=0; i < times.length; i++)
System.out.printf("latency for %d: %s\n", i, print(times[i]));
System.out.printf("\nmin/max/avg (us): %.2f / %.2f / %.2f\n", min / 1000.0, max / 1000.0, avg.getAverage() / 1000.0);
assert avg.getAverage() < LATENCY_NS;
}
protected void _testLatencyWithoutMessageBundling(boolean use_bundling) throws Exception {
Message tmp=new Message();
if(use_bundling) {
tmp.setFlag(Message.Flag.DONT_BUNDLE);
setBundling(a, MAX_BYTES);
}
long time=System.nanoTime();
a.send(tmp);
System.out.println(">>> sent message");
promise.getResult(SLEEP);
long diff=System.nanoTime() - time;
System.out.printf("latency: %s\n", print(diff));
assertTrue(String.format("latency (%s) should be less than %d ms", print(diff), LATENCY), diff <= LATENCY_NS);
}
private static String print(long time_ns) {
double us=time_ns / 1_000.0;
return String.format("%d ns (%.2f us)", time_ns, us);
}
private static void setBundling(JChannel ch, int max_bytes) {
ProtocolStack stack=ch.getProtocolStack();
TP transport=stack.getTransport();
transport.setMaxBundleSize(max_bytes);
GMS gms=stack.findProtocol(GMS.class);
gms.setViewAckCollectionTimeout(LATENCY * 2);
gms.setJoinTimeout(LATENCY * 2);
}
protected static class SimpleReceiver extends ReceiverAdapter {
protected final Promise<Boolean> promise;
public SimpleReceiver(Promise<Boolean> promise) {
this.promise=promise;
}
public void receive(Message msg) {
promise.setResult(true);
}
}
}