org.jgroups.tests.perf.ProgrammaticUPerf2 Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.tests.perf;
import org.jgroups.*;
import org.jgroups.annotations.Property;
import org.jgroups.blocks.MethodCall;
import org.jgroups.blocks.RequestOptions;
import org.jgroups.blocks.ResponseMode;
import org.jgroups.blocks.RpcDispatcher;
import org.jgroups.protocols.*;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.stack.DiagnosticsHandler;
import org.jgroups.stack.NonReflectiveProbeHandler;
import org.jgroups.stack.Protocol;
import org.jgroups.util.AverageMinMax;
import org.jgroups.util.Rsp;
import org.jgroups.util.RspList;
import org.jgroups.util.Util;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Stream;
import static org.jgroups.tests.perf.PerfUtil.*;
/**
* Tests the UNICAST by invoking unicast RPCs between a sender and a receiver. Mimicks the DIST mode in Infinispan
* @author Bela Ban
*/
public class ProgrammaticUPerf2 implements Receiver {
private static final String groupname="uperf";
private static final JChannel channel;
private static final RpcDispatcher disp;
private static final String BIND_ADDR="site_local";
private static final String MCAST_ADDR="239.5.5.5";
private static Address local_addr=null;
protected static final List members=new ArrayList<>();
protected static volatile View view=null;
protected static NonReflectiveProbeHandler h;
protected volatile boolean looping=true;
protected Thread event_loop_thread;
protected final LongAdder num_reads=new LongAdder();
protected final LongAdder num_writes=new LongAdder();
// ============ configurable properties ==================
@Property static protected boolean sync=true, oob=true;
@Property static protected int num_threads=100;
@Property static protected int time=60; // in seconds
@Property static protected int msg_size=1000;
@Property static protected int anycast_count=2;
@Property static protected double read_percentage=0.8; // 80% reads, 20% writes
@Property static protected boolean allow_local_gets=true;
@Property static protected boolean print_invokers=false;
@Property static protected boolean print_details=false;
// ... add your own here, just don't forget to annotate them with @Property
// =======================================================
private static final short START = 0;
private static final short GET = 1;
private static final short PUT = 2;
private static final short GET_CONFIG = 3;
private static final short SET_SYNC = 4;
private static final short SET_OOB = 5;
private static final short SET_NUM_THREADS = 6;
private static final short SET_TIME = 7;
private static final short SET_MSG_SIZE = 8;
private static final short SET_ANYCAST_COUNT = 9;
private static final short SET_READ_PERCENTAGE = 10;
private static final short ALLOW_LOCAL_GETS = 11;
private static final short PRINT_INVOKERS = 12;
private static final short PRINT_DETAILS = 13;
private static final short QUIT_ALL = 14;
private final AtomicInteger COUNTER=new AtomicInteger(1);
private byte[] BUFFER=new byte[msg_size];
protected static final String format=
"[1] Start test [2] View [4] Threads (%d) [6] Time (%,ds) [7] Msg size (%s)" +
"\n[s] Sync (%b) [o] OOB (%b)" +
"\n[a] Anycast count (%d) [r] Read percentage (%.2f) " +
"\n[l] local gets (%b) [d] print details (%b) [i] print invokers (%b)" +
"\n[v] Version [x] Exit [X] Exit all\n";
static {
try {
Receiver r=new Receiver() {
public void viewAccepted(View new_view) {
view=new_view;
System.out.println("** view: " + new_view);
members.clear();
members.addAll(new_view.getMembers());
}
};
InetAddress bind_address=Util.getAddress(BIND_ADDR, Util.getIpStackType());
TCP tcp=new TCP().setBindAddress(bind_address).setBindAddress(bind_address).setBindPort(7800);
Protocol[] prot_stack={
// uncomment if UDP should be used, compile, then native-image
/*new UDP().setMulticastAddress(mcast_addr).setMulticastPort(7888)
.setBindAddress(bind_address).setBindPort(7800)
.setDiagnosticsEnabled(true).diagEnableUdp(true).diagEnableTcp(false),
new PING(),*/
// uncomment if TCP should be used, compile, then native-image
tcp,
new TCPPING().initialHosts(Collections.singletonList(new InetSocketAddress(bind_address, 7800))),
// new MPING().mcastAddress(mcast_addr).mcastPort(7550),
new MERGE3(),
new FD_SOCK(),
new FD_ALL3(),
new VERIFY_SUSPECT(),
new NAKACK2(),
new UNICAST3().setXmitTableNumRows(10).setXmitTableMsgsPerRow(50000)
.setAckThreshold(1000),
new STABLE(),
new GMS().setJoinTimeout(1000),
new UFC(),
new MFC(),
new FRAG2()};
channel=new JChannel(prot_stack);
channel.getProtocolStack().getTransport().getDiagnosticsHandler().enableUdp(false).enableTcp(true);
disp=new RpcDispatcher(channel, null).setReceiver(r)
.setMethodInvoker(ProgrammaticUPerf2::invoke);
h=new NonReflectiveProbeHandler(channel).initialize(channel.getProtocolStack().getProtocols());
PerfUtil.init();
}
catch(Exception e) {
throw new RuntimeException(e);
}
}
public static boolean getSync() {return sync;}
public static void setSync(boolean s) {sync=s;}
public static boolean getOOB() {return oob;}
public static void setOOB(boolean o) {oob=o;}
public static int getNumThreads() {return num_threads;}
public static void setNumThreads(int t) {num_threads=t;}
public static int getTime() {return time;}
public static void setTime(int t) {time=t;}
public static int getMsgSize() {return msg_size;}
public static void setMsgSize(int t) {msg_size=t;}
public static int getAnycastCount() {return anycast_count;}
public static void setAnycastCount(int t) {anycast_count=t;}
public static double getReadPercentage() {return read_percentage;}
public static void setReadPercentage(double r) {read_percentage=r;}
public static boolean allowLocalGets() {return allow_local_gets;}
public static void allowLocalGets(boolean a) {allow_local_gets=a;}
public static boolean printInvokers() {return print_invokers;}
public static void printInvokers(boolean p) {print_invokers=p;}
public static boolean printDetails() {return print_details;}
public static void printDetails(boolean p) {print_details=p;}
public void init(String name, String bind_addr, int bind_port) throws Exception {
TP transport=channel.getProtocolStack().getTransport();
disp.setServerObject(this);
channel.setName(name);
if(bind_port > 0)
transport.setBindPort(bind_port);
if(bind_addr != null)
transport.setBindAddress(InetAddress.getByName(bind_addr));
channel.connect(groupname);
DiagnosticsHandler diag_handler=transport.getDiagnosticsHandler();
if(diag_handler != null) {
Set probe_handlers=diag_handler.getProbeHandlers();
probe_handlers.removeIf(probe_handler -> {
String[] keys=probe_handler.supportedKeys();
return keys != null && Stream.of(keys).anyMatch(s -> s.startsWith("jmx"));
});
}
transport.registerProbeHandler(h);
local_addr=channel.getAddress();
if(members.size() < 2)
return;
Address coord=members.get(0);
Config config=disp.callRemoteMethod(coord, new CustomCall(GET_CONFIG), new RequestOptions(ResponseMode.GET_ALL, 5000));
if(config != null) {
applyConfig(config);
System.out.println("Fetched config from " + coord + ": " + config + "\n");
}
else
System.err.println("failed to fetch config from " + coord);
}
static void stop() {
Util.close(disp, channel);
}
protected void startEventThread() {
event_loop_thread=new Thread(ProgrammaticUPerf2.this::eventLoop, "EventLoop");
event_loop_thread.setDaemon(true);
event_loop_thread.start();
}
protected void stopEventThread() {
Thread tmp=event_loop_thread;
looping=false;
if(tmp != null)
tmp.interrupt();
Util.close(channel);
}
public void viewAccepted(View v) {
view=v;
System.out.println("** view: " + v);
members.clear();
members.addAll(v.getMembers());
}
public static Object invoke(Object target, short method_id, Object[] args) throws Exception {
ProgrammaticUPerf2 uperf=(ProgrammaticUPerf2)target;
Boolean bool_val;
switch(method_id) {
case START:
return uperf.startTest();
case GET:
Integer key=(Integer)args[0];
return uperf.get(key);
case PUT:
key=(Integer)args[0];
byte[] val=(byte[])args[1];
uperf.put(key, val);
return null;
case GET_CONFIG:
return ProgrammaticUPerf2.getConfig();
case SET_SYNC:
ProgrammaticUPerf2.setSync((Boolean)args[0]);
return null;
case SET_OOB:
bool_val=(Boolean)args[0];
ProgrammaticUPerf2.setOOB(bool_val);
return null;
case SET_NUM_THREADS:
ProgrammaticUPerf2.setNumThreads((Integer)args[0]);
return null;
case SET_TIME:
ProgrammaticUPerf2.setTime((Integer)args[0]);
return null;
case SET_MSG_SIZE:
ProgrammaticUPerf2.setMsgSize((Integer)args[0]);
return null;
case SET_ANYCAST_COUNT:
ProgrammaticUPerf2.setAnycastCount((Integer)args[0]);
return null;
case SET_READ_PERCENTAGE:
ProgrammaticUPerf2.setReadPercentage((Double)args[0]);
return null;
case ALLOW_LOCAL_GETS:
ProgrammaticUPerf2.allowLocalGets((Boolean)args[0]);
return null;
case PRINT_INVOKERS:
ProgrammaticUPerf2.printInvokers((Boolean)args[0]);
return null;
case PRINT_DETAILS:
ProgrammaticUPerf2.printDetails((Boolean)args[0]);
return null;
case QUIT_ALL:
uperf.quitAll();
return null;
default:
throw new IllegalArgumentException("method with id=" + method_id + " not found");
}
}
// =================================== callbacks ======================================
public Results startTest() throws Exception {
BUFFER=new byte[msg_size];
System.out.printf("running for %d seconds\n", time);
final CountDownLatch latch=new CountDownLatch(1);
num_reads.reset(); num_writes.reset();
Invoker[] invokers=new Invoker[num_threads];
for(int i=0; i < invokers.length; i++) {
invokers[i]=new Invoker(members, latch);
invokers[i].start(); // waits on latch
}
long start=System.currentTimeMillis();
latch.countDown();
long interval=(long)((time * 1000.0) / 10.0);
for(int i=1; i <= 10; i++) {
Util.sleep(interval);
System.out.printf("%d: %s\n", i, printAverage(start));
}
for(Invoker invoker: invokers)
invoker.cancel();
for(Invoker invoker: invokers)
invoker.join();
long total_time=System.currentTimeMillis() - start;
System.out.println();
AverageMinMax avg_gets=null, avg_puts=null;
for(Invoker invoker: invokers) {
if(print_invokers)
System.out.printf("invoker %s: gets %s puts %s\n", invoker.getId(),
print(invoker.avgGets(), print_details), print(invoker.avgPuts(), print_details));
if(avg_gets == null)
avg_gets=invoker.avgGets();
else
avg_gets.merge(invoker.avgGets());
if(avg_puts == null)
avg_puts=invoker.avgPuts();
else
avg_puts.merge(invoker.avgPuts());
}
if(print_invokers)
System.out.printf("\navg over all invokers: gets %s puts %s\n",
print(avg_gets, print_details), print(avg_puts, print_details));
System.out.printf("\ndone (in %s ms)\n", total_time);
return new Results((int)num_reads.sum(), (int)num_writes.sum(), total_time, avg_gets, avg_puts);
}
public void quitAll() {
System.out.println("-- received quitAll(): shutting down");
stopEventThread();
}
protected String printAverage(long start_time) {
long tmp_time=System.currentTimeMillis() - start_time;
long reads=num_reads.sum(), writes=num_writes.sum();
double reqs_sec=(reads+writes) / (tmp_time / 1000.0);
return String.format("%,.0f reqs/sec (%,d reads %,d writes)", reqs_sec, reads, writes);
}
public byte[] get(@SuppressWarnings("UnusedParameters")int key) {
return BUFFER;
}
@SuppressWarnings("UnusedParameters")
public void put(int key, byte[] val) {
}
public static Config getConfig() {
return new Config().add("sync", sync).add("oob", oob).add("num_threads", num_threads).add("time", time)
.add("msg_size", msg_size).add("anycast_count", anycast_count).add("read_percentage", read_percentage)
.add("allow_local_gets", allow_local_gets).add("print_invokers", print_invokers).add("print_details", print_details);
}
protected static void applyConfig(Config config) {
for(Map.Entry e: config.values.entrySet()) {
String name=e.getKey();
Object value=e.getValue();
switch(name) {
case "sync":
setSync((Boolean)value);
break;
case "oob":
setOOB((Boolean)value);
break;
case "num_threads":
setNumThreads((Integer)value);
break;
case "time":
setTime((Integer)value);
break;
case "msg_size":
setMsgSize((Integer)value);
break;
case "anycast_count":
setAnycastCount((Integer)value);
break;
case "read_percentage":
setReadPercentage((Double)value);
break;
case "allow_local_gets":
allowLocalGets((Boolean)value);
break;
case "print_invokers":
printInvokers((Boolean)value);
break;
case "print_details":
printDetails((Boolean)value);
break;
default:
throw new IllegalArgumentException("field with name " + name + " not known");
}
}
}
// ================================= end of callbacks =====================================
public void eventLoop() {
while(looping) {
try {
int c=Util.keyPress(String.format(format, num_threads, time, Util.printBytes(msg_size),
sync, oob, anycast_count, read_percentage,
allow_local_gets, print_details, print_invokers));
switch(c) {
case '1':
startBenchmark();
break;
case '2':
printView();
break;
case '4':
invoke(SET_NUM_THREADS, Util.readIntFromStdin("Number of sender threads: "));
break;
case '6':
invoke(SET_TIME, Util.readIntFromStdin("Time (secs): "));
break;
case '7':
invoke(SET_MSG_SIZE, Util.readIntFromStdin("Message size: "));
break;
case 'a':
int tmp=parseAnycastCount();
if(tmp >= 0)
invoke(SET_ANYCAST_COUNT, tmp);
break;
case 'o':
invoke(SET_OOB, !oob);
break;
case 's':
invoke(SET_SYNC, !sync);
break;
case 'r':
double percentage=parseReadPercentage();
if(percentage >= 0)
invoke(SET_READ_PERCENTAGE, percentage);
break;
case 'd':
invoke(PRINT_DETAILS, !print_details);
break;
case 'i':
invoke(PRINT_INVOKERS, !print_invokers);
break;
case 'l':
invoke(ALLOW_LOCAL_GETS, !allow_local_gets);
break;
case 'v':
System.out.printf("Version: %s\n", Version.printVersion());
break;
case 'x':
case -1:
looping=false;
break;
case 'X':
try {
RequestOptions options=new RequestOptions(ResponseMode.GET_NONE, 0)
.flags(Message.Flag.OOB, Message.Flag.DONT_BUNDLE, Message.Flag.NO_FC);
disp.callRemoteMethods(null, new CustomCall(QUIT_ALL), options);
break;
}
catch(Throwable t) {
System.err.println("Calling quitAll() failed: " + t);
}
break;
default:
break;
}
}
catch(Throwable t) {
t.printStackTrace();
}
}
stop();
}
protected static void invoke(short method_id, Object... args) throws Exception {
MethodCall call=new CustomCall(method_id, args);
disp.callRemoteMethods(null, call, RequestOptions.SYNC());
}
/** Kicks off the benchmark on all cluster nodes */
protected void startBenchmark() {
RspList responses=null;
try {
RequestOptions options=new RequestOptions(ResponseMode.GET_ALL, 0);
options.flags(Message.Flag.OOB, Message.Flag.DONT_BUNDLE, Message.Flag.NO_FC);
responses=disp.callRemoteMethods(null, new CustomCall(START), options);
}
catch(Throwable t) {
System.err.println("starting the benchmark failed: " + t);
return;
}
long total_reqs=0;
long total_time=0;
AverageMinMax avg_gets=null, avg_puts=null;
System.out.println("\n======================= Results: ===========================");
for(Map.Entry> entry: responses.entrySet()) {
Address mbr=entry.getKey();
Rsp rsp=entry.getValue();
Results result=rsp.getValue();
if(result != null) {
total_reqs+=result.num_gets + result.num_puts;
total_time+=result.total_time;
if(avg_gets == null)
avg_gets=result.avg_gets;
else
avg_gets.merge(result.avg_gets);
if(avg_puts == null)
avg_puts=result.avg_puts;
else
avg_puts.merge(result.avg_puts);
}
System.out.println(mbr + ": " + result);
}
double total_reqs_sec=total_reqs / ( total_time/ 1000.0);
double throughput=total_reqs_sec * BUFFER.length;
System.out.println("\n");
System.out.println(Util.bold(String.format("Throughput: %,.2f reqs/sec/node (%s/sec)\n" +
"Roundtrip: gets %s, puts %s\n",
total_reqs_sec, Util.printBytes(throughput),
print(avg_gets, print_details), print(avg_puts, print_details))));
System.out.println("\n\n");
}
static double parseReadPercentage() throws Exception {
double tmp=Util.readDoubleFromStdin("Read percentage: ");
if(tmp < 0 || tmp > 1.0) {
System.err.println("read percentage must be >= 0 or <= 1.0");
return -1;
}
return tmp;
}
protected static int parseAnycastCount() throws Exception {
int tmp=Util.readIntFromStdin("Anycast count: ");
View tmp_view=channel.getView();
if(tmp > tmp_view.size()) {
System.err.println("anycast count must be smaller or equal to the view size (" + tmp_view + ")\n");
return -1;
}
return tmp;
}
protected static void printView() {
System.out.printf("\n-- local: %s, view: %s\n", local_addr, view);
try {
System.in.skip(System.in.available());
}
catch(Exception ignored) {
}
}
protected static String print(AverageMinMax avg, boolean details) {
return details? String.format("min/avg/max = %,.2f/%,.2f/%,.2f us",
avg.min() / 1000.0, avg.average() / 1000.0, avg.max() / 1000.0) :
String.format("avg = %,.2f us", avg.average() / 1000.0);
}
private class Invoker extends Thread {
private final List dests=new ArrayList<>();
private final CountDownLatch latch;
private final AverageMinMax avg_gets=new AverageMinMax(), avg_puts=new AverageMinMax(); // in ns
private final List targets=new ArrayList<>(anycast_count);
private volatile boolean running=true;
public Invoker(Collection dests, CountDownLatch latch) {
this.latch=latch;
this.dests.addAll(dests);
setName("Invoker-" + COUNTER.getAndIncrement());
}
public AverageMinMax avgGets() {return avg_gets;}
public AverageMinMax avgPuts() {return avg_puts;}
public void cancel() {running=false;}
public void run() {
Object[] put_args={0, BUFFER};
Object[] get_args={0};
MethodCall get_call=new GetCall(GET, get_args);
MethodCall put_call=new PutCall(PUT, put_args);
RequestOptions get_options=new RequestOptions(ResponseMode.GET_ALL, 40000, false, null);
RequestOptions put_options=new RequestOptions(sync ? ResponseMode.GET_ALL : ResponseMode.GET_NONE, 40000, true, null);
if(oob) {
get_options.flags(Message.Flag.OOB);
put_options.flags(Message.Flag.OOB);
}
try {
latch.await();
}
catch(InterruptedException e) {
e.printStackTrace();
}
while(running) {
boolean get=Util.tossWeightedCoin(read_percentage);
try {
if(get) { // sync GET
Address target=pickTarget();
long start=System.nanoTime();
if(allow_local_gets && Objects.equals(target, local_addr))
get(1);
else {
disp.callRemoteMethod(target, get_call, get_options);
}
long get_time=System.nanoTime()-start;
avg_gets.add(get_time);
num_reads.increment();
}
else { // sync or async (based on value of 'sync') PUT
pickAnycastTargets(targets);
long start=System.nanoTime();
disp.callRemoteMethods(targets, put_call, put_options);
long put_time=System.nanoTime()-start;
targets.clear();
avg_puts.add(put_time);
num_writes.increment();
}
}
catch(Throwable throwable) {
throwable.printStackTrace();
}
}
}
private Address pickTarget() {
return Util.pickRandomElement(dests);
}
private void pickAnycastTargets(List anycast_targets) {
int index=dests.indexOf(local_addr);
for(int i=index + 1; i < index + 1 + anycast_count; i++) {
int new_index=i % dests.size();
Address tmp=dests.get(new_index);
if(!anycast_targets.contains(tmp))
anycast_targets.add(tmp);
}
}
}
public static void main(String[] args) {
String name=null, bind_addr=null;
boolean run_event_loop=true;
int port=0;
for(int i=0; i < args.length; i++) {
if("-name".equals(args[i])) {
name=args[++i];
continue;
}
if("-nohup".equals(args[i])) {
run_event_loop=false;
continue;
}
if("-port".equals(args[i])) {
port=Integer.parseInt(args[++i]);
continue;
}
if("-bind_addr".equals(args[i])) {
bind_addr=args[++i];
continue;
}
help();
return;
}
ProgrammaticUPerf2 test=null;
try {
test=new ProgrammaticUPerf2();
test.init(name, bind_addr, port);
if(run_event_loop)
test.startEventThread();
}
catch(Throwable ex) {
ex.printStackTrace();
if(test != null)
ProgrammaticUPerf2.stop();
}
}
static void help() {
System.out.printf("%s [-name name] [-nohup] [-port ] " +
"[-bind_addr bind-address]\n", ProgrammaticUPerf2.class.getSimpleName());
}
}