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

io.scalecube.benchmarks.BenchmarksState Maven / Gradle / Ivy

package io.scalecube.benchmarks;

import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.CsvReporter;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer;
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;

import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;


/**
 * BenchmarksState is the state of the benchmark. it gives you the analogy of the beginning, and ending of the test. It
 * can run both sync or async way using the {@link #runForSync(Function)} and {@link #runForAsync(Function)}
 * respectively.
 * 
 * @param  when extending this class, please add your class as the SELF. ie.
 * 
 *        
 * {@code   
 *  public class ExampleBenchmarksState extends BenchmarksState {   
 *    ...   
 *  }   
 * }
 *        
*/ public class BenchmarksState> { private static final Logger LOGGER = LoggerFactory.getLogger(BenchmarksState.class); protected final BenchmarksSettings settings; protected Scheduler scheduler; protected List schedulers; private ConsoleReporter consoleReporter; private CsvReporter csvReporter; private final AtomicBoolean started = new AtomicBoolean(); public BenchmarksState(BenchmarksSettings settings) { this.settings = settings; } protected void beforeAll() throws Exception { // NOP } protected void afterAll() throws Exception { // NOP } /** * Executes starting of the state, also it includes running of {@link BenchmarksState#beforeAll}. */ public final void start() { if (!started.compareAndSet(false, true)) { throw new IllegalStateException("BenchmarksState is already started"); } LOGGER.info("Benchmarks settings: " + settings); settings.registry().register(settings.taskName() + "-memory", new MemoryUsageGaugeSet()); consoleReporter = ConsoleReporter.forRegistry(settings.registry()) .outputTo(System.out) .convertDurationsTo(settings.durationUnit()) .convertRatesTo(settings.rateUnit()) .build(); csvReporter = CsvReporter.forRegistry(settings.registry()) .convertDurationsTo(settings.durationUnit()) .convertRatesTo(settings.rateUnit()) .build(settings.csvReporterDirectory()); scheduler = Schedulers.fromExecutor(Executors.newFixedThreadPool(settings.nThreads())); schedulers = IntStream.rangeClosed(1, settings.nThreads()) .mapToObj(i -> Schedulers.fromExecutorService(Executors.newSingleThreadScheduledExecutor())) .collect(Collectors.toList()); try { beforeAll(); } catch (Exception ex) { throw new IllegalStateException("BenchmarksState beforeAll() failed: " + ex, ex); } consoleReporter.start(settings.reporterInterval().toMillis(), TimeUnit.MILLISECONDS); csvReporter.start(settings.reporterInterval().toMillis(), TimeUnit.MILLISECONDS); Runtime.getRuntime().addShutdownHook(new Thread(() -> { if (started.get()) { csvReporter.report(); consoleReporter.report(); } })); } /** * Executes shutdown process of the state, also it includes running of {@link BenchmarksState#afterAll}. */ public final void shutdown() { if (!started.compareAndSet(true, false)) { throw new IllegalStateException("BenchmarksState is not started"); } if (consoleReporter != null) { consoleReporter.report(); consoleReporter.stop(); } if (csvReporter != null) { csvReporter.report(); csvReporter.stop(); } if (scheduler != null) { scheduler.dispose(); } if (schedulers != null) { schedulers.forEach(Scheduler::dispose); } try { afterAll(); } catch (Exception ex) { throw new IllegalStateException("BenchmarksState afterAll() failed: " + ex, ex); } } public Scheduler scheduler() { return scheduler; } public List schedulers() { return schedulers; } /** * Returns timer with specified name. * * @param name name * @return timer with specified name */ public Timer timer(String name) { return settings.registry().timer(settings.taskName() + "-" + name); } /** * Returns meter with specified name. * * @param name name * @return meter with specified name */ public Meter meter(String name) { return settings.registry().meter(settings.taskName() + "-" + name); } /** * Returns histogram with specified name. * * @param name name * @return histogram with specified name */ public Histogram histogram(String name) { return settings.registry().histogram(settings.taskName() + "-" + name); } /** * Runs given function in the state. It also executes {@link BenchmarksState#start()} before and * {@link BenchmarksState#shutdown()} after. *

* NOTICE: It's only for synchronous code. *

* * @param func a function that should return the execution to be tested for the given SELF. This execution would run * on all positive values of Long (i.e. the benchmark itself) the return value is ignored. */ public final void runForSync(Function> func) { @SuppressWarnings("unchecked") SELF self = (SELF) this; try { // noinspection unchecked self.start(); Function unitOfWork = func.apply(self); Flux fromStream = Flux.fromStream(LongStream.range(0, settings.numOfIterations()).boxed()); Flux.merge(fromStream .publishOn(scheduler()).map(unitOfWork)) .take(settings.executionTaskDuration()) .blockLast(); } finally { self.shutdown(); } } /** * Runs given function on this state. It also executes {@link BenchmarksState#start()} before and * {@link BenchmarksState#shutdown()} after. *

* NOTICE: It's only for asynchronous code. *

* * @param func a function that should return the execution to be tested for the given SELF. This execution would run * on all positive values of Long (i.e. the benchmark itself) On the return value, as it is a Publisher, The * benchmark test would {@link Publisher#subscribe(Subscriber) subscribe}, And upon all subscriptions - await * for termination. */ public final void runForAsync(Function>> func) { // noinspection unchecked @SuppressWarnings("unchecked") SELF self = (SELF) this; try { self.start(); Function> unitOfWork = func.apply(self); Flux fromStream = Flux.fromStream(LongStream.range(0, settings.numOfIterations()).boxed()); Flux.merge(fromStream .publishOn(scheduler()).map(unitOfWork)) .take(settings.executionTaskDuration()) .blockLast(); } finally { self.shutdown(); } } /** * Runs given function on this state. It also executes {@link BenchmarksState#start()} before and * {@link BenchmarksState#shutdown()} after. *

* NOTICE: It's only for asynchronous code. *

* * @param setUp a function that should return some T type which one will be passed into next to the argument. Also, * this function will be invoked with some ramp-up strategy, and when it will be invoked it will start * executing the unitOfWork, which one specified as the second argument of this method. * @param func a function that should return the execution to be tested for the given SELF. This execution would run * on all positive values of Long (i.e. the benchmark itself) On the return value, as it is a Publisher, The * benchmark test would {@link Publisher#subscribe(Subscriber) subscribe}, And upon all subscriptions - await * for termination. * @param cleanUp a function that should clean up some T's resources. */ public final void runWithRampUp( BiFunction> setUp, Function>> func, BiFunction> cleanUp) { // noinspection unchecked @SuppressWarnings("unchecked") SELF self = (SELF) this; try { self.start(); BiFunction> unitOfWork = func.apply(self); Flux.interval(settings.rampUpInterval()) .take(settings.rampUpDuration()) .flatMap(rampUpIteration -> { // select scheduler and bind tasks to it int schedulerIndex = (int) ((rampUpIteration & Long.MAX_VALUE) % schedulers().size()); Scheduler scheduler = schedulers().get(schedulerIndex); // create tasks on selected scheduler Flux setUpFactory = Flux.create((FluxSink sink) -> { Flux deferSetUp = Flux.defer(() -> setUp.apply(rampUpIteration, self)); deferSetUp.subscribe( sink::next, ex -> { LOGGER.error("Exception occured on setUp at rampUpIteration: {}, " + "cause: {}, task won't start", rampUpIteration, ex); sink.complete(); }, sink::complete); }); return setUpFactory .subscribeOn(scheduler) .map(setUpResult -> new BenchmarksTask<>(self, setUpResult, unitOfWork, cleanUp, scheduler)) .doOnNext(scheduler::schedule) .flatMap(BenchmarksTask::completionMono); }, Integer.MAX_VALUE, Integer.MAX_VALUE) .blockLast(); } finally { self.shutdown(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy