diff --git a/.nosqlbench/grafana_apikey b/.nosqlbench/grafana_apikey new file mode 100644 index 000000000..11d760aed --- /dev/null +++ b/.nosqlbench/grafana_apikey @@ -0,0 +1 @@ +eyJrIjoibVFjRkV5Z096VHE0MjhXYk1RM2p1cnFuUkhQMXVZNGwiLCJuIjoibm9zcWxiZW5jaC0xMC4xMC4xMDAuNTItMTYwNzMxMDE2MDM0OCIsImlkIjoxfQ== \ No newline at end of file diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/InlineTokenPool.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/InlineTokenPool.java new file mode 100644 index 000000000..dfbfc8f36 --- /dev/null +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/ratelimits/InlineTokenPool.java @@ -0,0 +1,376 @@ +/* + * + * Copyright 2016 jshook + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package io.nosqlbench.engine.api.activityapi.ratelimits; + +import com.codahale.metrics.Timer; +import io.nosqlbench.engine.api.activityimpl.ActivityDef; +import io.nosqlbench.engine.api.metrics.ActivityMetrics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.ReentrantLock; + +import static io.nosqlbench.engine.api.util.Colors.*; + +/** + *

Synopsis

+ * + * This TokenPool represents a finite quantity which can be + * replenished with regular refills. Extra tokens that do not fit + * within the active token pool are saved in a waiting token pool and + * used to backfill when allowed according to the backfill rate. + * + * A detailed explanation for how this works will be included + * at @link "http://docs.nosqlbench.io/" under dev notes. + * + *

This is the basis for the token-based rate limiters in + * NB. This mechanism is easily adaptable to bursting + * capability as well as a degree of stricter timing at speed. + * Various methods for doing this in a lock free way were + * investigated, but the intrinsic locks provided by synchronized + * method won out for now. This may be revisited when EB is + * retrofitted for J11. + *

+ */ +public class InlineTokenPool { + + private final static Logger logger = LogManager.getLogger(InlineTokenPool.class); + + public static final double MIN_CONCURRENT_OPS = 5; + + // Size limit of active pool + private long maxActivePoolSize; + // Size limit of burst pool incremental above active pool + private long maxBurstPoolSize; + // Size limit of total active tokens which can be waiting in active pool, considering burst + private long maxActiveAndBurstSize; + + // Ratio of speed relative to base speed at which bursting is allowed + private double burstRatio; + + // TODO Consider removing volatile after investigating + + // The active number of tokens (ns) available for consumers + private volatile long activePool; + // The tokens which were not claimed on time, and were moved into the waitime (reserve) pool + private volatile long waitingPool; + // How many tokens (ns) represent passage of time for a single op, given the op rate + private long nanosPerOp; + + // The nanotime of the last refill + private volatile long lastRefillAt; + // metrics for refill + private final Timer refillTimer; + // update rate for refiller + private final long interval = (long) 1E6; + + + private RateSpec rateSpec; +// private long debugTrigger=0L; +// private long debugRate=1000000000; + + // Total number of thread blocks that occured since this token pool was started + private long blocks = 0L; + + private final Lock lock = new ReentrantLock(); + private final Condition lockheld = lock.newCondition(); + + /** + * This constructor tries to pick reasonable defaults for the token pool for + * a given rate spec. The active pool must be large enough to contain one + * op worth of time, and the burst ratio + * + * @param rateSpec a {@link RateSpec} + */ + public InlineTokenPool(RateSpec rateSpec, ActivityDef def) { + ByteBuffer logbuf = getBuffer(); + apply(rateSpec); + logger.debug("initialized token pool: " + this.toString() + " for rate:" + rateSpec.toString()); + this.refillTimer = ActivityMetrics.timer(def, "tokenfiller"); + } + + public InlineTokenPool(long poolsize, double burstRatio, ActivityDef def) { + ByteBuffer logbuf = getBuffer(); + this.maxActivePoolSize = poolsize; + this.burstRatio = burstRatio; + this.maxActiveAndBurstSize = (long) (maxActivePoolSize * burstRatio); + this.maxBurstPoolSize = maxActiveAndBurstSize - maxActivePoolSize; + this.refillTimer = ActivityMetrics.timer(def, "tokenfiller"); + } + + /** + * Change the settings of this token pool, and wake any blocked callers + * just in case it allows them to proceed. + * + * @param rateSpec The rate specifier. + */ + public synchronized void apply(RateSpec rateSpec) { + this.rateSpec = rateSpec; + // maxActivePool is set to the higher of 1M or however many nanos are needed for 2 ops to be buffered + this.maxActivePoolSize = Math.max((long) 1E6, (long) ((double) rateSpec.getNanosPerOp() * MIN_CONCURRENT_OPS)); + this.maxActiveAndBurstSize = (long) (maxActivePoolSize * rateSpec.getBurstRatio()); + this.burstRatio = rateSpec.getBurstRatio(); + + this.maxBurstPoolSize = maxActiveAndBurstSize - maxActivePoolSize; + this.nanosPerOp = rateSpec.getNanosPerOp(); + notifyAll(); + } + + + public double getBurstRatio() { + return burstRatio; + } + + /** + * Take tokens up to amt tokens form the pool and report + * the amount of token removed. + * + * @param amt tokens requested + * @return actual number of tokens removed, greater to or equal to zero + */ + public synchronized long takeUpTo(long amt) { + long take = Math.min(amt, activePool); + activePool -= take; + return take; + } + + /** + * wait for the given number of tokens to be available, and then remove + * them from the pool. + * + * @return the total number of tokens untaken, including wait tokens + */ + public long blockAndTake() { + synchronized (this) { + if (activePool >= nanosPerOp) { + activePool -= nanosPerOp; + return waitingPool + activePool; + } + } + while (true) { + if (lock.tryLock()) { + try { + while (activePool < nanosPerOp) { + dorefill(); + } + lockheld.signal(); + lockheld.signal(); + } finally { + lock.unlock(); + } + } else { + try { + lockheld.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } +// while (activePool < nanosPerOp) { +// blocks++; +// //System.out.println(ANSI_BrightRed + "waiting for " + amt + "/" + activePool + " of max " + maxActivePool + ANSI_Reset); +// try { +// wait(); +//// wait(maxActivePoolSize / 1000000, (int) maxActivePoolSize % 1000000); +// } catch (InterruptedException ignored) { +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// //System.out.println("waited for " + amt + "/" + activePool + " tokens"); +// } +// //System.out.println(ANSI_BrightYellow + "taking " + amt + "/" + activePool + ANSI_Reset); +// +// activePool -= nanosPerOp; +// return waitingPool + activePool; + } + + public synchronized long blockAndTakeOps(long ops) { + long totalNanosNeeded = ops * nanosPerOp; + while (activePool < totalNanosNeeded) { + blocks++; + //System.out.println(ANSI_BrightRed + "waiting for " + amt + "/" + activePool + " of max " + maxActivePool + ANSI_Reset); + try { + wait(); +// wait(maxActivePoolSize / 1000000, (int) maxActivePoolSize % 1000000); + } catch (InterruptedException ignored) { + } catch (Exception e) { + throw new RuntimeException(e); + } + //System.out.println("waited for " + amt + "/" + activePool + " tokens"); + } + //System.out.println(ANSI_BrightYellow + "taking " + amt + "/" + activePool + ANSI_Reset); + + activePool -= totalNanosNeeded; + return waitingPool + activePool; + } + + public synchronized long blockAndTake(long tokens) { + while (activePool < tokens) { + //System.out.println(ANSI_BrightRed + "waiting for " + amt + "/" + activePool + " of max " + maxActivePool + ANSI_Reset); + try { + wait(); +// wait(maxActivePoolSize / 1000000, (int) maxActivePoolSize % 1000000); + } catch (InterruptedException ignored) { + } catch (Exception e) { + throw new RuntimeException(e); + } + //System.out.println("waited for " + amt + "/" + activePool + " tokens"); + } + //System.out.println(ANSI_BrightYellow + "taking " + amt + "/" + activePool + ANSI_Reset); + + activePool -= tokens; + return waitingPool + activePool; + } + + public long getWaitTime() { + return activePool + waitingPool; + } + + public long getWaitPool() { + return waitingPool; + } + + public long getActivePool() { + return activePool; + } + + /** + * Add the given number of new tokens to the pool, forcing any amount + * that would spill over the current pool size into the wait token pool, but + * moving up to the configured burst tokens back from the wait token pool + * otherwise. + * + * The amount of backfilling that occurs is controlled by the backfill ratio, + * based on the number of tokens submitted. This causes normalizes the + * backfilling rate to the fill rate, so that it is not sensitive to refill + * scheduling. + * + * @param newTokens The number of new tokens to add to the token pools + * @return the total number of tokens in all pools + */ + public synchronized long refill(long newTokens) { + boolean debugthis = false; +// long debugAt = System.nanoTime(); +// if (debugAt>debugTrigger+debugRate) { +// debugTrigger=debugAt; +// debugthis=true; +// } + + long needed = Math.max(maxActivePoolSize - activePool, 0L); + long allocatedToActivePool = Math.min(newTokens, needed); + activePool += allocatedToActivePool; + + + // overflow logic + long allocatedToOverflowPool = newTokens - allocatedToActivePool; + waitingPool += allocatedToOverflowPool; + + // backfill logic + double refillFactor = Math.min((double) newTokens / maxActivePoolSize, 1.0D); + long burstFillAllowed = (long) (refillFactor * maxBurstPoolSize); + + burstFillAllowed = Math.min(maxActiveAndBurstSize - activePool, burstFillAllowed); + long burstFill = Math.min(burstFillAllowed, waitingPool); + + waitingPool -= burstFill; + activePool += burstFill; + + if (debugthis) { + System.out.print(this); + System.out.print(ANSI_BrightBlue + " adding=" + allocatedToActivePool); + if (allocatedToOverflowPool > 0) { + System.out.print(ANSI_Red + " OVERFLOW:" + allocatedToOverflowPool + ANSI_Reset); + } + if (burstFill > 0) { + System.out.print(ANSI_BrightGreen + " BACKFILL:" + burstFill + ANSI_Reset); + } + System.out.println(); + } + + //System.out.println(this); + notifyAll(); + + return activePool + waitingPool; + } + + @Override + public String toString() { + return "Tokens: active=" + activePool + "/" + maxActivePoolSize + + String.format( + " (%3.1f%%)A (%3.1f%%)B ", + (((double) activePool / (double) maxActivePoolSize) * 100.0), + (((double) activePool / (double) maxActiveAndBurstSize) * 100.0)) + " waiting=" + waitingPool + + " blocks=" + blocks + + " rateSpec:" + ((rateSpec != null) ? rateSpec.toString() : "NULL"); + } + + public RateSpec getRateSpec() { + return rateSpec; + } + + public synchronized long restart() { + long wait = activePool + waitingPool; + activePool = 0L; + waitingPool = 0L; + return wait; + } + + private ByteBuffer getBuffer() { + RandomAccessFile image = null; + try { + image = new RandomAccessFile("tokenbucket.binlog", "rw"); + ByteBuffer mbb = image.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, image.length()); + return mbb; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public synchronized void dorefill() { + lastRefillAt = System.nanoTime(); + long nextRefillTime = lastRefillAt + interval; + long thisRefillTime = System.nanoTime(); + while (thisRefillTime < nextRefillTime) { +// while (thisRefillTime < lastRefillAt + interval) { + long parkfor = Math.max(nextRefillTime - thisRefillTime, 0L); + //System.out.println(ANSI_Blue + "parking for " + parkfor + "ns" + ANSI_Reset); + LockSupport.parkNanos(parkfor); + thisRefillTime = System.nanoTime(); + } + +// this.times[iteration]=thisRefillTime; + long delta = thisRefillTime - lastRefillAt; +// this.amounts[iteration]=delta; + lastRefillAt = thisRefillTime; + + //System.out.println(this); + refill(delta); + refillTimer.update(delta, TimeUnit.NANOSECONDS); +// iteration++; + + } + + +} diff --git a/sort_docs/design/opstate.puml b/sort_docs/design/opstate.puml new file mode 100644 index 000000000..eb6be73d4 --- /dev/null +++ b/sort_docs/design/opstate.puml @@ -0,0 +1,61 @@ +@startuml +scale 600 width +[*] --> TrackedOp : track() + + TrackedOp: setCycle(cycle) + TrackedOp: setWaitTime(delay) + TrackedOp: start() + TrackedOp: + TrackedOp: setData(data) + TrackedOp: getData() + TrackedOp: skip(reason) + +State InProtocol { + + TrackedOp --> StartedOp : start() + + StartedOp: getCycle() + StartedOp: setData(data) + StartedOp: getData() + StartedOp: succeed(status) + StartedOp: + StartedOp: retry() + StartedOp: fail(status) + StartedOp: getStartedAtNanos() + StartedOp: getCurrentServiceTimeNanos() + StartedOp: getCurrentResponseTimeNanos() + + StartedOp -> StartedOp : retry() + + StartedOp --> SucceededOp : succeed() + SucceededOp: getCycle() + SucceededOp: getResult() + SucceededOp: getTries() + SucceededOp: getStartedAtNanos() + SucceededOp: getServiceTimeNanos() + SucceededOp: getResponseTimeNanos() + + StartedOp --> FailedOp: fail() + FailedOp: getCycle() + FailedOp: getResult() + FailedOp: getTries() + FailedOp: getStartedAtNanos() + FailedOp: getServiceTimeNanos() + FailedOp: getResponseTimeNanos() +} + +TrackedOp --> SkippedOp : skip() +SkippedOp: getSkippedReason() +SkippedOp: +SkippedOp: getCycle() +SkippedOp: getResult() +SkippedOp: getStartedAtNanos() +SkippedOp: getData() +SkippedOp: setData(data) + + +SucceededOp --> [*] +FailedOp --> [*] +SkippedOp --> [*] + +@enduml \ No newline at end of file diff --git a/sort_docs/eb_iterates_cycles.puml b/sort_docs/eb_iterates_cycles.puml new file mode 100644 index 000000000..15d22bfca --- /dev/null +++ b/sort_docs/eb_iterates_cycles.puml @@ -0,0 +1,29 @@ +@startuml + +Participant Input as i +Participant Thread as t +Participant Action as a + +== acquire input == + +group TIMER read-input + t -> i : get segment(stride) + activate i + t <- i : [stride] + deactivate i +end + +group TIMER strides + + loop over cycle values in segment + group TIMER cycle & phase + t -> a : runCycle(cycle) + activate a + t <- a : result + deactivate a + end +end + +end # strides + +@enduml \ No newline at end of file diff --git a/sort_docs/eb_iterates_cycles.svg b/sort_docs/eb_iterates_cycles.svg new file mode 100644 index 000000000..d7e447ca2 --- /dev/null +++ b/sort_docs/eb_iterates_cycles.svg @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + Input + + + Input + + + Thread + + + Thread + + + Action + + + Action + + + + + + + + acquire input + + + + TIMER read-input + + + + get segment(stride) + + + + <cycle segment>[stride] + + + + TIMER strides + + + + loop + + [over cycle + values in segment] + + + + TIMER cycle + & phase + + + + runCycle(cycle) + + + + result + + + \ No newline at end of file diff --git a/sort_docs/eb_iterates_phases.puml b/sort_docs/eb_iterates_phases.puml new file mode 100644 index 000000000..ff3d1c82a --- /dev/null +++ b/sort_docs/eb_iterates_phases.puml @@ -0,0 +1,42 @@ +@startuml + +Participant Input as i +Participant Thread as t +Participant Action as a + +== acquire input data == + +group TIMER read-input + t -> i : get segment(stride) + activate i + t <- i : [stride] + deactivate i +end + +group TIMER strides + + loop over cycle values in segment + group TIMER cycle + group TIMER phase + t -> a : runCycle(cycle) + activate a + t <- a : result + deactivate a + end + +== additional phases == + +group TIMER phase + loop until phases complete + t -> a : runPhase(cycle) + activate a + t <- a : result + deactivate a +end +end +end +end + +end # strides + +@enduml \ No newline at end of file diff --git a/sort_docs/eb_iterates_phases.svg b/sort_docs/eb_iterates_phases.svg new file mode 100644 index 000000000..577addfd1 --- /dev/null +++ b/sort_docs/eb_iterates_phases.svg @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Input + + + Input + + + Thread + + + Thread + + + Action + + + Action + + + + + + + + + acquire input + data + + + + TIMER read-input + + + + get segment(stride) + + + + <cycle segment>[stride] + + + + TIMER strides + + + + loop + + [over cycle + values in segment] + + + + TIMER cycle + + + + TIMER phase + + + + runCycle(cycle) + + + + result + + + + + + additional + phases + + + + TIMER phase + + + + loop + + [until phases + complete] + + + + runPhase(cycle) + + + + result + + + \ No newline at end of file diff --git a/sort_docs/eb_latency_details.puml b/sort_docs/eb_latency_details.puml new file mode 100644 index 000000000..9081a4928 --- /dev/null +++ b/sort_docs/eb_latency_details.puml @@ -0,0 +1,38 @@ +@startuml +Participant user as u +Participant client as c +Participant resource as cr +Participant transport as t +Participant server as s + +group responsetime + u -> c: request + activate c #Black + group waittime + c -> cr: wait + activate cr #Yellow + note left of cr: client\nwaits\nfor\nresource + cr -> c: + deactivate cr + end + + group servicetime + c ->> t: request + activate t #Red + group servertime + t ->> s: request + deactivate t + activate s #Blue + note right of s: server\nprocesses\nrequest + s ->> t: response + deactivate s + activate t #Red + end + t ->> c: response + deactivate t + end + c -> u: response + deactivate c +end + +@enduml \ No newline at end of file diff --git a/sort_docs/eb_latency_terms.puml b/sort_docs/eb_latency_terms.puml new file mode 100644 index 000000000..c7f6d336a --- /dev/null +++ b/sort_docs/eb_latency_terms.puml @@ -0,0 +1,30 @@ +@startuml +Participant user as u +Participant client as c +Participant resource as cr +Participant server as s + +group responsetime + u -> c: request + activate c #Black +' note left of c: user\nwaits\nfor\nresponse + group waittime + c -> cr: wait + activate cr #Yellow + note right of cr: client\nwaits\nfor\nresource + cr -> c: + deactivate cr + end + + group servicetime + c ->> s: request + activate s #Blue + note right of s: server\nprocesses\nrequest + s ->> c: response + deactivate s + end + c -> u: response + deactivate c +end + +@enduml \ No newline at end of file diff --git a/sort_docs/eb_latency_terms.svg b/sort_docs/eb_latency_terms.svg new file mode 100644 index 000000000..ccfe22776 --- /dev/null +++ b/sort_docs/eb_latency_terms.svg @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + user + + + user + + + client + + + client + + + resource + + + resource + + + server + + + server + + + + + + + responsetime + + + + request + + + + waittime + + + + wait + + + + client + + waits + + for + + resource + + + + + + servicetime + + + + + request + + + + server + + processes + + request + + + + + response + + + + response + + + \ No newline at end of file diff --git a/sort_docs/op_state_nomnoml.png b/sort_docs/op_state_nomnoml.png new file mode 100644 index 000000000..fbeea86f5 Binary files /dev/null and b/sort_docs/op_state_nomnoml.png differ diff --git a/sort_docs/opstate_nomnoml.svg b/sort_docs/opstate_nomnoml.svg new file mode 100644 index 000000000..b9dfb411f --- /dev/null +++ b/sort_docs/opstate_nomnoml.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + track + start + skip + retry + succeed + fail + + tracked + + started + + succeeded + + failed + + skipped + \ No newline at end of file diff --git a/sort_docs/package_layout.md b/sort_docs/package_layout.md new file mode 100644 index 000000000..2705d7f51 --- /dev/null +++ b/sort_docs/package_layout.md @@ -0,0 +1,7 @@ +- io.nosqlbench.engine. +- io.nosqlbench.extensions. +- io.nosqlbench.activitytypes. + +- io.nosqlbench.virtdata. +- io.nosqlbench.virtdata.library. +- io.nosqlbench.docs. ... \ No newline at end of file diff --git a/sort_docs/parts.puml b/sort_docs/parts.puml new file mode 100644 index 000000000..e69de29bb diff --git a/sort_docs/rate_limiter_design.puml b/sort_docs/rate_limiter_design.puml new file mode 100644 index 000000000..8a97ff202 --- /dev/null +++ b/sort_docs/rate_limiter_design.puml @@ -0,0 +1,82 @@ +@startuml + +Participant "Calling\nThread" as t +Participant "Limiter\nlogic" as l +Participant "Allocated\nnanos" as a +Participant "Elapsed\nnanos" as e +Participant "Clock\nSource" as c + +t -> l : acquire(nanos) + +group allocate start time +l -> a : getAndIncrement(nanos) +activate a #black +note over l,a + **allocated** is an atomic accumulator + which represents scheduled time. Each + op causes it to be atomically incremented + by a time slice of nanos. +end note +a -> l : +deactivate a +end + +group calculate delay (cached) +l -> e : get() +activate e +note over e + **elapsed** is an + atomic register + which caches + system time. +end note +e -> l : +deactivate e +l -> l : delay = \nelapsed - scheduled_at + +note right + **delay** measures external delay + that causes an op to fire after + the ideal time. **positive delay** + thus means the rate limiter doesn't + need to impose its own blocking delay + in order to ensure delay>=0. +end note + +end + +group if delay<0 (cached) +note over l,c + If delay<0, then this operation is too soon according + to the cached clock value. Since this could be stale + and cause us to block needlessly, we update the cached + clock value and recompute delay. +end note +l -> c : get() (~25ns) +activate c #orange +c -> l : +deactivate c + +l -> e : store() +activate e #black +e -> l +deactivate e +l -> l : delay = \nelapsed - scheduled_at + +group if delay<0 (updated) + l->l: sleep(-delay);\ndelay=0 + note right + If delay is negative, we sleep + in the calling thread and + set delay=0 + end note + activate l + deactivate l +end + +end + +l->t: + + +@enduml \ No newline at end of file diff --git a/sort_docs/ratelimiter_timelines.puml b/sort_docs/ratelimiter_timelines.puml new file mode 100644 index 000000000..649d74a43 --- /dev/null +++ b/sort_docs/ratelimiter_timelines.puml @@ -0,0 +1,47 @@ +@startuml +scale 100 as 100 pixels + +Title Rate Limiter - **Timelines** + +robust "typical" as W1 +@W1 +0 is past +100 is allocated #yellow +200 is scheduled #lightgreen +632 is now #lightblue +W1@100 <-> @200: schedule\ndelay +W1@200 <-> @632: + +robust "no waittime" as W2 +@W2 +0 is past +200 is allocated #yellow +200.000001 is scheduled #lightgreen +632 is now #lightblue + +robust "caughtup" as W3 +@W3 +0 is past +100 is allocated #yellow +200.000001 is scheduled #lightgreen +232 is now #lightblue + +robust "ahead" as W4 +@W4 +0 is past +100 is allocated #yellow +200.000001 is scheduled #lightgreen +232 is now #lightblue + +concise "perfect ops" as O +@O +0 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op +@enduml diff --git a/sort_docs/research/multivariate.svg b/sort_docs/research/multivariate.svg new file mode 100644 index 000000000..a7f619f7a --- /dev/null +++ b/sort_docs/research/multivariate.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + SCORE + value + throughput + latency + PARAMS + + + + + + + + + + + + + RESULT + ccph + async + threads + + + PARAMS + + + + + VALUE + + + + + + + + result + function + R(P) + value + function + V(R) + + V(R(P)) + + diff --git a/sort_docs/research/op_tracking.svg b/sort_docs/research/op_tracking.svg new file mode 100644 index 000000000..d46906623 --- /dev/null +++ b/sort_docs/research/op_tracking.svg @@ -0,0 +1,611 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + op + tracker + + + + + + + + + + scenario + + + + + + + activity + + + + input + + + + async + count + + + + stride + tracker + + + + cycle + segment + + + + + + + thread + + + diff --git a/sort_docs/testseq_early.png b/sort_docs/testseq_early.png new file mode 100644 index 000000000..df14d6361 Binary files /dev/null and b/sort_docs/testseq_early.png differ diff --git a/sort_docs/testseq_early.puml b/sort_docs/testseq_early.puml new file mode 100644 index 000000000..a3bc77d93 --- /dev/null +++ b/sort_docs/testseq_early.puml @@ -0,0 +1,45 @@ +@startuml +scale 100 as 100 pixels + +Title Rate Limit - **EARLY** +concise "clock" as C +concise "elapsed" as E +concise "scheduled" as S +concise "allocated" as A + +C is the_past #red +E is elapsed #lightblue +S is scheduled #orange +A is allocated #yellow + +@C +732 is future #white +E->C + +@E +500 is unseen #white +@500 <-> @732: **error** = 232 +A -> C + +@S +0 is idle #grey +100 is scheduled #orange +600 is unscheduled #white +@500 <-> @600: **scheduling_delay** =\nelapsed - scheduled = -100 + +@A +300 is unallocated #white +@300 <-> @500: **wait_time** =\nelapsed - allocated = 200 + +concise "Ops" as O +@O +0 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op +@enduml diff --git a/sort_docs/testseq_late.png b/sort_docs/testseq_late.png new file mode 100644 index 000000000..95c894b08 Binary files /dev/null and b/sort_docs/testseq_late.png differ diff --git a/sort_docs/testseq_late.puml b/sort_docs/testseq_late.puml new file mode 100644 index 000000000..f6f17e5a4 --- /dev/null +++ b/sort_docs/testseq_late.puml @@ -0,0 +1,51 @@ +@startuml +scale 100 as 100 pixels + +Title Rate Limit **LATE** +concise "clock" as C +concise "elapsed" as E +concise "scheduled" as S +concise "allocated" as A + +C is the_past #red +E is NOWTIME #lightblue +S is scheduled #orange +A is allocated #yellow + +@0 +S is idle #grey + +@100 +A is unallocated #white +S is scheduled #orange + +@200 +S is unscheduled #white + +@500 +E is unseen #white +A -> C + +@632 +C is future #white +E->C + +@A +@100 <-> @500: **wait_time** =\nelapsed - allocated = 400 +@E +@500 <-> @632: **error** = 132 +@S +@200 <-> @500: **scheduling_delay** =\nelapsed - scheduled = 300 + +concise "Ops" as O +@O +0 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op ++100 is op +@enduml diff --git a/sort_docs/todo.md b/sort_docs/todo.md new file mode 100644 index 000000000..a173147ef --- /dev/null +++ b/sort_docs/todo.md @@ -0,0 +1,16 @@ +- convert core input to be equivalent + of `input=type:interval,cycles:N[..M]` +- Add doc support for input, input filters, outputs, output filters +- Build metadata scaffolding for parameters, so unused parameters may be + warned about. + - parameters should be part of the activity API + - parameters should not re-trigger def observers for non-change + evhandler + - parameters across all subsystems should be discoverable or + enumerable +- make stride auto-sizing uniformly apply after sequencing +- reimplement core activity and scenario logic as async/reactor with + monitors +- convert to Java 9 +- add activity library commands +- add --list-input-filters and --list-output-filters \ No newline at end of file