package org.jgroups.tests;
import org.jgroups.Address;
import org.jgroups.Message;
import org.jgroups.PhysicalAddress;
import org.jgroups.protocols.*;
import org.jgroups.util.AsciiString;
import org.jgroups.util.AverageMinMax;
import org.jgroups.util.DefaultThreadFactory;
import org.jgroups.util.Util;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
/**
* Tests bundler performance
* @author Bela Ban
* @since 4.0
*/
public class BundlerStressTest {
protected String bundler_type;
protected Bundler bundler;
protected int num_msgs=50000, num_senders=20, msg_size=1000;
protected boolean details;
protected static final Address[] ADDRESSES;
protected final TP transport=new MockTransport();
protected static final int BUFSIZE=50000;
static {
ADDRESSES=new Address[]{null, Util.createRandomAddress("A"), Util.createRandomAddress("B"),
Util.createRandomAddress("C"), Util.createRandomAddress("D"), Util.createRandomAddress("E"),
Util.createRandomAddress("F"), Util.createRandomAddress("G"), Util.createRandomAddress("H")};
}
public BundlerStressTest(String bundler_type) {
this.bundler_type=bundler_type;
}
protected void start() {
this.bundler=createBundler(bundler_type);
this.bundler.init(transport);
this.bundler.start();
loop();
}
protected void loop() {
boolean looping=true;
while(looping) {
int c=Util.keyPress(String.format("[1] send [2] num_msgs (%d) [3] senders (%d) [4] msg size (%d bytes)\n" +
"[b] change bundler (%s) [d] details (%b) [x] exit\nbundler: %s\n",
num_msgs, num_senders, msg_size, bundler.getClass().getSimpleName(),
details, bundler.toString()));
try {
switch(c) {
case '1':
sendMessages();
break;
case '2':
num_msgs=Util.readIntFromStdin("num_msgs: ");
break;
case '3':
num_senders=Util.readIntFromStdin("num_senders: ");
break;
case '4':
msg_size=Util.readIntFromStdin("msg_size: ");
break;
case 'b':
String type=null;
try {
type=Util.readStringFromStdin("new bundler type: ");
Bundler old=this.bundler;
this.bundler=createBundler(type);
this.bundler.init(transport);
this.bundler.start();
if(old != null)
old.stop();
}
catch(Throwable t) {
System.err.printf("failed changing bundler to %s: %s\n", type, t);
}
break;
case 'd':
details=!details;
break;
case 'x':
case -1:
looping=false;
break;
}
}
catch(Throwable t) {
t.printStackTrace();
}
}
if(this.bundler != null)
this.bundler.stop();
}
protected void sendMessages() throws Exception {
Message[] msgs=generateMessages(num_msgs);
CountDownLatch latch=new CountDownLatch(1);
AtomicInteger index=new AtomicInteger(0);
Sender[] senders=new Sender[num_senders];
for(int i=0; i < senders.length; i++) {
senders[i]=new Sender(latch, msgs, index);
senders[i].start();
}
long start=Util.micros();
latch.countDown(); // starts all sender threads
for(Sender sender: senders)
sender.join();
// wait until the bundler has no pending msgs left
long park_time=1;
for(int i=0; i < 1_000_000; i++) {
int pending_msgs=bundler.size();
if(pending_msgs == 0)
break;
LockSupport.parkNanos(park_time);
if(i % 10000 == 0) {
park_time=Math.min(park_time*2, 1_000_000); // 1 ms max park time
}
}
if(bundler.size() > 0)
throw new Exception(String.format("bundler still has %d pending messages", bundler.size()));
long time_us=Util.micros()-start;
AverageMinMax send_avg=null;
for(Sender sender: senders) {
if(details)
System.out.printf("[%d] count=%d, send-time = %s\n", sender.getId(), sender.send.count(), sender.send);
if(send_avg == null)
send_avg=sender.send;
else
send_avg.merge(sender.send);
}
double msgs_sec=num_msgs / (time_us / 1_000.0);
System.out.printf(Util.bold("\n\nreqs/ms = %.2f (time: %d us)" +
"\nsend-time = min/avg/max: %d / %.2f / %d ns\n"),
msgs_sec, time_us, send_avg.min(), send_avg.average(), send_avg.max());
}
protected Bundler createBundler(String bundler) {
if(bundler == null)
throw new IllegalArgumentException("bundler type has to be non-null");
if(bundler.equals("stq"))
return new SimplifiedTransferQueueBundler(BUFSIZE);
if(bundler.equals("tq"))
return new TransferQueueBundler(BUFSIZE);
if(bundler.startsWith("sender-sends") || bundler.equals("ss"))
return new SenderSendsBundler();
if(bundler.endsWith("ring-buffer") || bundler.equals("rb"))
return new RingBufferBundler(BUFSIZE);
if(bundler.equals("ring-buffer-lockless") || bundler.equals("rbl"))
return new RingBufferBundlerLockless(BUFSIZE);
if(bundler.equals("ring-buffer-lockless2") || bundler.equals("rbl2"))
return new RingBufferBundlerLockless2(BUFSIZE);
if(bundler.startsWith("no-bundler") || bundler.equals("nb"))
return new NoBundler();
try {
Class<Bundler> clazz=Util.loadClass(bundler, getClass());
return clazz.newInstance();
}
catch(Throwable t) {
throw new IllegalArgumentException(String.format("failed creating instance of bundler %s: %s", bundler, t));
}
}
protected Message[] generateMessages(int num) {
Message[] msgs=new Message[num];
for(int i=0; i < msgs.length; i++)
msgs[i]=new Message(pickAddress(), new byte[msg_size]);
return msgs;
}
protected static Address pickAddress() {
return Util.pickRandomElement(ADDRESSES);
}
public static void main(String[] args) {
String bundler="ring-buffer-lockless2";
for(int i=0; i < args.length; i++) {
if(args[i].equals("-bundler")) {
bundler=args[++i];
continue;
}
System.out.printf("BundlerStressTest [-bundler bundler-type]\n");
return;
}
new BundlerStressTest(bundler).start();
}
protected class Sender extends Thread {
protected final CountDownLatch latch;
protected final Message[] msgs;
protected final AtomicInteger index;
protected final AverageMinMax send=new AverageMinMax(); // ns
public Sender(CountDownLatch latch, Message[] msgs, AtomicInteger index) {
this.latch=latch;
this.msgs=msgs;
this.index=index;
}
public void run() {
try {
latch.await();
}
catch(InterruptedException e) {
}
while(true) {
int idx=index.getAndIncrement();
if(idx >= msgs.length)
break;
try {
long start=System.nanoTime();
bundler.send(msgs[idx]);
long time_ns=System.nanoTime()-start;
send.add(time_ns);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
protected static class MockTransport extends TP {
public MockTransport() {
this.cluster_name=new AsciiString("mock");
thread_factory=new DefaultThreadFactory("", false);
}
public boolean supportsMulticasting() {
return false;
}
public void doSend(byte[] buf, int offset, int length, Address dest) throws Exception {
}
public void sendMulticast(byte[] data, int offset, int length) throws Exception {
}
public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception {
}
public String getInfo() {
return null;
}
protected PhysicalAddress getPhysicalAddress() {
return null;
}
}
}