All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.tests.perf.UPerf Maven / Gradle / Ivy

There is a newer version: 5.3.13.Final
Show newest version
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.jmx.JmxConfigurator;
import org.jgroups.protocols.TP;
import org.jgroups.protocols.relay.RELAY;
import org.jgroups.stack.AddressGenerator;
import org.jgroups.tests.perf.PerfUtil.Config;
import org.jgroups.tests.perf.PerfUtil.GetCall;
import org.jgroups.tests.perf.PerfUtil.PutCall;
import org.jgroups.tests.perf.PerfUtil.Results;
import org.jgroups.util.*;

import javax.management.MBeanServer;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAdder;


/**
 * Tests the UNICAST by invoking unicast RPCs between a sender and a receiver. Mimicks the DIST mode in Infinispan
 *
 * @author Bela Ban
 */
public class UPerf implements Receiver {
    private JChannel                           channel;
    private Address                            local_addr;
    private RpcDispatcher                      disp;
    static final String                        groupname="uperf";
    protected final List
members=new ArrayList<>(); protected volatile View view; protected volatile boolean looping=true; protected final LongAdder num_reads=new LongAdder(); protected final LongAdder num_writes=new LongAdder(); protected ThreadFactory thread_factory; protected final ResponseCollector results_coll=new ResponseCollector<>(); // ============ configurable properties ================== @Property protected boolean oob; @Property protected int num_threads=100; @Property protected int time=60; // in seconds @Property protected int msg_size=1000; @Property protected int anycast_count=2; @Property protected double read_percentage=0.8; // 80% reads, 20% writes @Property protected boolean print_invokers; @Property protected boolean print_details; @Property protected long rpc_timeout=0; // ... add your own here, just don't forget to annotate them with @Property // ======================================================= private static final Method[] METHODS=new Method[7]; private static final short START_TEST = 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 = 4; private static final short QUIT_ALL = 5; private static final short ACCEPT_RESULTS = 6; protected static final Field OOB, NUM_THREADS, TIME, RPC_TIMEOUT, MSG_SIZE, ANYCAST_COUNT, READ_PERCENTAGE, PRINT_INVOKERS, PRINT_DETAILS; 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[o] OOB (%b) [t] RPC timeout (%,dms) [a] Anycast count (%d) [r] Read percentage (%.2f) " + "\n[d] print details (%b) [i] print invokers (%b)" + "\n[v] Version [x] Exit [X] Exit all\n"; static { try { METHODS[START_TEST] = UPerf.class.getMethod("startTest", Address.class); METHODS[GET] = UPerf.class.getMethod("get", long.class); METHODS[PUT] = UPerf.class.getMethod("put", long.class, byte[].class); METHODS[GET_CONFIG] = UPerf.class.getMethod("getConfig"); METHODS[SET] = UPerf.class.getMethod("set", String.class, Object.class); METHODS[QUIT_ALL] = UPerf.class.getMethod("quitAll"); METHODS[ACCEPT_RESULTS] = UPerf.class.getMethod("acceptResults", Address.class, Results.class); OOB=Util.getField(UPerf.class, "oob", true); NUM_THREADS=Util.getField(UPerf.class, "num_threads", true); TIME=Util.getField(UPerf.class, "time", true); RPC_TIMEOUT=Util.getField(UPerf.class, "rpc_timeout", true); MSG_SIZE=Util.getField(UPerf.class, "msg_size", true); ANYCAST_COUNT=Util.getField(UPerf.class, "anycast_count", true); READ_PERCENTAGE=Util.getField(UPerf.class, "read_percentage", true); PRINT_INVOKERS=Util.getField(UPerf.class, "print_invokers", true); PRINT_DETAILS=Util.getField(UPerf.class, "print_details", true); PerfUtil.init(); } catch(Exception e) { throw new RuntimeException(e); } } public void init(String props, String name, AddressGenerator generator, int bind_port, boolean use_virtual_threads, boolean async_rsp_handling) throws Throwable { thread_factory=new DefaultThreadFactory("invoker", false, true) .useVirtualThreads(use_virtual_threads); if(use_virtual_threads && Util.virtualThreadsAvailable()) System.out.println("-- using virtual threads instead of threads"); channel=new JChannel(props).addAddressGenerator(generator).setName(name); if(bind_port > 0) { TP transport=channel.getProtocolStack().getTransport(); transport.setBindPort(bind_port); } disp=(RpcDispatcher)new RpcDispatcher(channel,this).setReceiver(this) .setMethodLookup(id -> METHODS[id]) .asyncRspHandling(async_rsp_handling); channel.connect(groupname); local_addr=channel.getAddress(); try { MBeanServer server=Util.getMBeanServer(); JmxConfigurator.registerChannel(channel, server, "jgroups", channel.getClusterName(), true); } catch(Throwable ex) { System.err.println("registering the channel in JMX failed: " + ex); } if(members.size() < 2) return; Address coord=members.get(0); Config config=disp.callRemoteMethod(coord, new MethodCall(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); } void stop() { Util.close(disp, channel); } protected void stopEventLoop() { looping=false; Util.close(channel); } public void viewAccepted(View new_view) { this.view=new_view; System.out.println("** view: " + new_view); members.clear(); members.addAll(new_view.getMembers()); results_coll.retainAll(members); } // =================================== callbacks ====================================== public void startTest(Address sender) throws Throwable { CompletableFuture.supplyAsync(() -> { try { return startTest(); } catch(Throwable ex) { System.err.printf("failure running the test: %s", ex); return null; } }).thenAccept(r -> sendResults(sender, r)); } /** Called by worker when done; response to {@link #startTest()} */ public void acceptResults(Address sender, Results results) { results_coll.add(sender, results); } public void quitAll() { System.out.println("-- received quitAll(): shutting down"); stopEventLoop(); System.exit(0); } 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 void set(String field_name, Object value) { Field field=Util.getField(this.getClass(),field_name); if(field == null) System.err.println("Field " + field_name + " not found"); else { Util.setField(field, this, value); System.out.println(field.getName() + "=" + value); } } public byte[] get(@SuppressWarnings("UnusedParameters") long key) { return BUFFER; } @SuppressWarnings("UnusedParameters") public void put(long key, byte[] val) { } public Config getConfig() { Config config=new Config(); for(Field field: Util.getAllDeclaredFieldsWithAnnotations(UPerf.class, Property.class)) { if(field.isAnnotationPresent(Property.class)) { config.add(field.getName(), Util.getField(field, this)); } } return config; } protected Results startTest() throws Throwable { 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]; Thread[] threads=new Thread[num_threads]; for(int i=0; i < threads.length; i++) { invokers[i]=new Invoker(members, latch); threads[i]=thread_factory.newThread(invokers[i]); threads[i].setName("invoker-" + (i+1)); threads[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.stop(); for(Thread t: threads) t.join(); long total_time=System.currentTimeMillis() - start; System.out.println(); AverageMinMax avg_gets=null, avg_puts=null; for(int i=0; i < invokers.length; i++) { Invoker invoker=invokers[i]; if(print_invokers) System.out.printf("invoker %s: gets %s puts %s\n", threads[i].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); } protected void sendResults(Address dest, Results results) { RequestOptions options=new RequestOptions(ResponseMode.GET_NONE, 0) .flags(Message.Flag.OOB, Message.Flag.DONT_BUNDLE, Message.Flag.NO_FC); try { disp.callRemoteMethod(dest, new MethodCall(ACCEPT_RESULTS, local_addr, results), options); } catch(Exception ex) { System.err.printf("failed sending results to %s: %s", dest, ex); } } protected void applyConfig(Config config) { for(Map.Entry entry: config.values.entrySet()) { Field field=Util.getField(getClass(), entry.getKey()); Util.setField(field, this, entry.getValue()); } } // ================================= end of callbacks ===================================== public void eventLoop() { while(looping) { try { int c=Util.keyPress(String.format(format, num_threads, time, Util.printBytes(msg_size), oob, rpc_timeout, anycast_count, read_percentage, print_details, print_invokers)); switch(c) { case '1': startBenchmark(); break; case '2': printView(); break; case '4': changeFieldAcrossCluster(NUM_THREADS, Util.readIntFromStdin("Number of sender threads: ")); break; case '6': changeFieldAcrossCluster(TIME, Util.readIntFromStdin("Time (secs): ")); break; case '7': changeFieldAcrossCluster(MSG_SIZE, Util.readIntFromStdin("Message size: ")); break; case 'a': int tmp=getAnycastCount(); if(tmp >= 0) changeFieldAcrossCluster(ANYCAST_COUNT, tmp); break; case 'o': changeFieldAcrossCluster(OOB, !oob); break; case 'r': double percentage=getReadPercentage(); if(percentage >= 0) changeFieldAcrossCluster(READ_PERCENTAGE, percentage); break; case 'd': changeFieldAcrossCluster(PRINT_DETAILS, !print_details); break; case 'i': changeFieldAcrossCluster(PRINT_INVOKERS, !print_invokers); break; case 't': changeFieldAcrossCluster(RPC_TIMEOUT, Util.readIntFromStdin("RPC timeout (millisecs): ")); break; case 'v': System.out.printf("Version: %s, Java version: %s\n", Version.printVersion(), System.getProperty("java.vm.version", "n/a")); 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) .transientFlags(Message.TransientFlag.DONT_LOOPBACK); disp.callRemoteMethods(null, new MethodCall(QUIT_ALL), options); quitAll(); break; } catch(Throwable t) { System.err.println("Calling quitAll() failed: " + t); } break; default: break; } } catch(Throwable t) { t.printStackTrace(); } } stop(); } /** Kicks off the benchmark on all cluster nodes */ protected void startBenchmark() { results_coll.reset(members); 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 MethodCall(START_TEST, local_addr), options); } catch(Throwable t) { System.err.println("starting the benchmark failed: " + t); return; } long total_reqs=0, total_time=0; AverageMinMax avg_gets=null, avg_puts=null; long time_to_wait=(long)(time * 1000 * 1.2); // add 20% more results_coll.waitForAllResponses(time_to_wait); Map results=results_coll.getResults(); System.out.println("\n======================= Results: ==========================="); for(Map.Entry entry: results.entrySet()) { Address mbr=entry.getKey(); Results result=entry.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 getReadPercentage() 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; } int getAnycastCount() 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 void changeFieldAcrossCluster(Field field, Object value) throws Exception { disp.callRemoteMethods(null, new MethodCall(SET, field.getName(), value), RequestOptions.SYNC()); } protected void printView() { System.out.printf("\n-- local: %s, view: %s\n", local_addr, view); try { System.in.skip(System.in.available()); } catch(Exception e) { } } 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); } protected static List getSites(JChannel channel) { RELAY relay=channel.getProtocolStack().findProtocol(RELAY.class); return relay != null? relay.siteNames() : new ArrayList<>(0); } /** Picks the next member in the view */ private Address getReceiver() { try { List
mbrs=channel.getView().getMembers(); int index=mbrs.indexOf(local_addr); int new_index=index + 1 % mbrs.size(); return mbrs.get(new_index); } catch(Exception e) { System.err.println("UPerf.getReceiver(): " + e); return null; } } private class Invoker implements Runnable { 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); } public AverageMinMax avgGets() {return avg_gets;} public AverageMinMax avgPuts() {return avg_puts;} public void stop() {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, rpc_timeout, false, null); RequestOptions put_options=new RequestOptions(ResponseMode.GET_ALL, rpc_timeout, 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(); 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 t) { if(running) t.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) throws IOException, ClassNotFoundException { String props=null, name=null; boolean run_event_loop=true, use_virtual_threads=true, async_rsp_handling=!Util.virtualThreadsAvailable(); // good with convential threads, bad with vthreads! AddressGenerator addr_generator=null; int port=0; for(int i=0; i < args.length; i++) { if("-props".equals(args[i])) { props=args[++i]; continue; } if("-name".equals(args[i])) { name=args[++i]; continue; } if("-nohup".equals(args[i])) { run_event_loop=false; continue; } if("-uuid".equals(args[i])) { addr_generator=new OneTimeAddressGenerator(Long.parseLong(args[++i])); continue; } if("-port".equals(args[i])) { port=Integer.parseInt(args[++i]); continue; } if("-use_virtual_threads".equals(args[i])) { use_virtual_threads=Boolean.parseBoolean(args[++i]); continue; } if("-async_rsp_handling".equals(args[i])) { async_rsp_handling=Boolean.parseBoolean(args[++i]); continue; } help(); return; } UPerf test=null; try { test=new UPerf(); test.init(props, name, addr_generator, port, use_virtual_threads, async_rsp_handling); if(run_event_loop) test.eventLoop(); else { for(;;) Util.sleep(60000); } } catch(Throwable ex) { ex.printStackTrace(); if(test != null) test.stop(); } } static void help() { System.out.println("UPerf [-props ] [-name name] [-nohup] [-uuid ] [-port ] " + "[-use_virtual_threads ] [-async_rsp_handling ]"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy