stability detection in progress

This commit is contained in:
Jonathan Shook 2023-11-01 11:09:16 -05:00
parent 651aaecbf0
commit 1cc2f5352c
36 changed files with 913 additions and 361 deletions

View File

@ -5,7 +5,7 @@
<option name="region" />
<option name="useCurrentConnection" value="false" />
</extension>
<option name="JAR_PATH" value="$PROJECT_DIR$/nb5/target/nb5.jar" />
<option name="JAR_PATH" value="nb5/target/nb5.jar" />
<option name="PROGRAM_PARAMETERS" value="cql_vector2_fvec astra_vectors.testann userfile=auth/userfile passfile=auth/passfile scb=auth/scb.zip --show-stacktraces dimensions=1024 testsize=10000 trainsize=100000 datafile=intfloat_e5-large-v2 filetype=fvec table=e5_large_v2 similarity_function=cosine --add-labels &quot;dimensions:1024,dataset=e5_large_v2&quot;" />
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$/local/jvector" />
<option name="ALTERNATIVE_JRE_PATH" value="jdk21" />

View File

@ -259,8 +259,7 @@ public class SimRate extends NBBaseComponent implements RateLimiter, Thread.Unca
this.blocks.increment();
try {
this.activePool.acquire(ticksPerOp);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (InterruptedException ignored) {
}
return this.waitingPool.get() + this.activePool.availablePermits();
}

View File

@ -56,21 +56,21 @@ public class TestOptimoExperiments {
SimpleBounds bounds = new SimpleBounds(
new double[]{0.0d, 0.0d, 0.0d},
new double[]{1E9,1E9,1E9});
new double[]{0.0d, 0.0d, 0.0d, 0.0d},
new double[]{1E9,1E9,1E9,1E9});
List<OptimizationData> od = List.of(
new ObjectiveFunction(m),
GoalType.MAXIMIZE,
new InitialGuess(new double[]{1.0,1.0,1.0}),
new InitialGuess(new double[]{1.0,1.0,1.0,1.0}),
new MaxEval(1000)
,bounds
);
BOBYQAOptimizer mo = new BOBYQAOptimizer(
9,
12,
1000.0,
1.0
0.25
);
PointValuePair result = mo.optimize(od.toArray(new OptimizationData[0]));

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios;
import java.util.function.DoubleUnaryOperator;
public class MultiplicativeValueFuncs {
/**
* If the value is above the threshold, return the value. If not, return 1.0d;
* @param threshold
* @return
*/
public static DoubleUnaryOperator above(double threshold) {
return (v) -> (v<threshold) ? 1.0d : v;
}
public static DoubleUnaryOperator between(double min, double max) {
return (v) -> (v>=min & v<=max) ? v : 1.0d;
}
public static DoubleUnaryOperator below(double threshold) {
return (v) -> (v>threshold) ? 1.0d : v;
}
public static DoubleUnaryOperator exp_2() {
return (v) -> (v*v)+1;
}
public static DoubleUnaryOperator exp_e() {
return Math::exp;
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios;
import jakarta.validation.Valid;
import java.util.function.DoubleUnaryOperator;
public class ValidAtOrAbove implements DoubleUnaryOperator {
public ValidAtOrAbove(double threshold, double defaultValue) {
this.threshold = threshold;
this.defaultValue = defaultValue;
}
private double threshold;
private double defaultValue;
@Override
public double applyAsDouble(double operand) {
if (operand>=threshold) {
return operand;
} else {
return defaultValue;
}
}
public static ValidAtOrAbove min(double min) {
return new ValidAtOrAbove(min,0.0d);
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios;
import java.util.function.DoubleUnaryOperator;
public class ValidAtOrBelow implements DoubleUnaryOperator {
public ValidAtOrBelow(double threshold, double defaultValue) {
this.threshold = threshold;
this.defaultValue = defaultValue;
}
private double threshold;
private double defaultValue;
@Override
public double applyAsDouble(double operand) {
if (operand<=threshold) {
return operand;
} else {
return defaultValue;
}
}
public static ValidAtOrBelow max(double max) {
return new ValidAtOrBelow(max,0.0d);
}
}

View File

@ -1,98 +0,0 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.findmax;
/**
* A frame sample is responsible for capturing the data associated with a criterion.
*/
public record FrameSample(
Criterion criterion,
int index,
long startAt,
long endAt,
double startval,
double endval,
double basis,
BasisValues vars
) {
public FrameSample {
vars.put(criterion.name(), basis);
}
public double weightedValue() {
if (Double.isNaN(criterion.weight())) {
return 1.0d;
} else {
return basis * criterion().weight();
}
}
private double calculateBasis() {
return switch (criterion.evaltype()) {
case direct -> endval;
case deltaT -> (endval - startval) / seconds();
case remix -> criterion.remix().applyAsDouble(vars);
};
}
private double seconds() {
return ((double) (endAt - startAt)) / 1000d;
}
public static FrameSample init(Criterion criterion, int index, BasisValues vars) {
return new FrameSample(criterion, index, 0, 0, Double.NaN, Double.NaN, Double.NaN, vars);
}
public FrameSample start(long startTime) {
criterion.frameStartCallback().run();
double v = (criterion().evaltype() == EvalType.deltaT) ? criterion().supplier().getAsDouble() : Double.NaN;
return new FrameSample(criterion, index, startTime, 0L, v, Double.NaN, Double.NaN, vars);
}
public FrameSample stop(long stopTime) {
double v2 = (criterion().evaltype() != EvalType.remix) ? criterion().supplier().getAsDouble() : Double.NaN;
FrameSample intermediate = new FrameSample(criterion, index, startAt, stopTime, startval, v2, Double.NaN, vars);
FrameSample complete = intermediate.tally();
return complete;
}
/**
* Take a frame sample which is fully populated with measured values and convert it into one with a computed basis value.
*/
private FrameSample tally() {
return new FrameSample(criterion, index, startAt, endAt, startval, endval, calculateBasis(), vars);
}
@Override
public String toString() {
return String.format(
"%30s % 3d derived=% 12.3f weighted=% 12.5f%s",
// "%30s %03d dt[%4.2fS] dV[% 10.3f] dC[% 10.3f] wV=%010.5f%s",
criterion.name(),
index,
// seconds(),
// deltaV(),
basis,
weightedValue(),
(Double.isNaN(criterion.weight()) ? " [NEUTRAL WEIGHT]" : "")
);
}
// private double deltaV() {
// return endval - startval;
// }
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax.conditioning;
package io.nosqlbench.scenarios.simframe;
public class ZScore {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import java.util.Arrays;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import java.util.function.DoubleSupplier;
import java.util.function.ToDoubleFunction;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
public enum EvalType {
/**

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.capture;
import java.util.Objects;
/**
* A frame sample is responsible for capturing the data associated with a single criterion as a single dependent
* variable.
*/
public final class FrameSample {
private final Criterion criterion;
private final int index;
private long startAt, endAt;
private double startval = Double.NaN;
private double endval = Double.NaN;
private double basis = Double.NaN;
private final BasisValues vars;
private boolean active = false;
public FrameSample(Criterion criterion, int index, BasisValues vars) {
this.criterion = criterion;
this.index = index;
this.vars = vars;
}
public double weightedValue() {
if (active) {
calculateBasis();
}
double result = (Double.isNaN(criterion().weight()) ? 1.0d : criterion().weight()) * basis;
return result;
}
public FrameSample start(long startTime) {
criterion.frameStartCallback().run();
this.startAt = startTime;
this.startval = (criterion().evaltype() == EvalType.deltaT) ? criterion().supplier().getAsDouble() : Double.NaN;
active = true;
return this;
}
public FrameSample stop(long endTime) {
if (active) {
this.endAt = endTime;
this.endval = (criterion().evaltype() != EvalType.remix) ? criterion().supplier().getAsDouble() : Double.NaN;
calculateBasis();
this.active = false;
} else {
throw new RuntimeException("Can't stop an inactive frame.");
}
return this;
}
private void calculateBasis() {
if (!active) {
throw new RuntimeException("Calculations on inactive windows should not be done.");
}
this.endAt = System.currentTimeMillis();
this.endval = (criterion().evaltype() != EvalType.remix) ? criterion().supplier().getAsDouble() : Double.NaN;
double seconds = deltaT();
double basis = switch (criterion.evaltype()) {
case direct -> endval;
case deltaT -> deltaV() / seconds;
case remix -> criterion.remix().applyAsDouble(vars);
};
vars.put(criterion().name(), basis);
this.basis = basis;
}
private double deltaV() {
return (endval - startval);
}
private double deltaT() {
return ((double) (endAt - startAt)) / 1000d;
}
public Criterion criterion() {
return criterion;
}
public int index() {
return index;
}
public long startAt() {
return startAt;
}
public long endAt() {
return endAt;
}
public double startval() {
return startval;
}
public double endval() {
return endval;
}
public double basis() {
return basis;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (FrameSample) obj;
return Objects.equals(this.criterion, that.criterion) &&
this.index == that.index &&
this.startAt == that.startAt &&
this.endAt == that.endAt &&
Double.doubleToLongBits(this.startval) == Double.doubleToLongBits(that.startval) &&
Double.doubleToLongBits(this.endval) == Double.doubleToLongBits(that.endval) &&
Double.doubleToLongBits(this.basis) == Double.doubleToLongBits(that.basis) &&
Objects.equals(this.vars, that.vars);
}
@Override
public int hashCode() {
return Objects.hash(criterion, index, startAt, endAt, startval, endval, basis, vars);
}
@Override
public String toString() {
return switch (criterion().evaltype()) {
case deltaT ->
String.format(
"% 3d %30s derived=% 12.3f ⋅ W[% 3.3f] =% 12.5f ΔV:%12.5f ΔT=%5.3f",
index, criterion.name(),
basis, criterion().weight(), weightedValue(),
deltaV(), deltaT());
case direct,remix ->
String.format(
"% 3d %30s derived=% 12.3f ⋅ W[% 3.3f] =% 12.5f",
index, criterion.name(),
basis, criterion().weight(), weightedValue());
};
}
}

View File

@ -14,7 +14,9 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import io.nosqlbench.scenarios.simframe.capture.FrameSample;
import java.util.ArrayList;
import java.util.List;
@ -40,7 +42,6 @@ public class FrameSampleSet extends ArrayList<FrameSample> {
return product;
}
// https://www.w3.org/TR/xml-entity-names/025.html
@Override
public String toString() {
@ -52,4 +53,5 @@ public class FrameSampleSet extends ArrayList<FrameSample> {
}
return sb.toString();
}
}

View File

@ -14,7 +14,9 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import io.nosqlbench.scenarios.simframe.planning.SimFrame;
import java.util.List;

View File

@ -14,14 +14,14 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import io.nosqlbench.scenarios.simframe.stats.StabilityDetector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.DoubleSupplier;
import java.util.function.LongSupplier;
import java.util.function.ToDoubleFunction;
import java.util.function.*;
/**
* This is a helper class that makes it easy to bundle up a combination of measurable
@ -35,7 +35,29 @@ import java.util.function.ToDoubleFunction;
public class SimFrameCapture implements SimFrameResults {
private final List<Criterion> criteria = new ArrayList<>();
private final FrameSamples allFrames = new FrameSamples();
private FrameSampleSet currentFrame;
private FrameSampleSet activeFrame;
private volatile boolean running = true;
private final StabilityDetector stabilizer;
public SimFrameCapture() {
stabilizer = new StabilityDetector(1.0,100,this::getPartialValue, 1000,100,10);
}
private double getPartialValue() {
if (activeFrame ==null) {
return 0.0d;
} else {
return activeFrame.value();
}
}
public void awaitSteadyState() {
stabilizer.run();
}
/**
@ -139,21 +161,27 @@ public class SimFrameCapture implements SimFrameResults {
public void startWindow() {
startWindow(System.currentTimeMillis());
}
public void restartWindow() {
restartWindow(System.currentTimeMillis());
}
public void startWindow(long now) {
if (currentFrame != null) {
throw new RuntimeException("cant start window twice in a row. Must close window first");
}
public void restartWindow(long now) {
int nextidx = this.allFrames.size();
BasisValues vars = new BasisValues();
List<FrameSample> samples = criteria.stream().map(c -> FrameSample.init(c, nextidx, vars).start(now)).toList();
this.currentFrame = new FrameSampleSet(samples);
// System.out.println("after start:\n"+ frameCaptureSummary(currentFrame));
List<FrameSample> samples = criteria.stream().map(c -> new FrameSample(c, nextidx, vars).start(now)).toList();
this.activeFrame = new FrameSampleSet(samples);
}
public void startWindow(long now) {
if (activeFrame != null) {
throw new RuntimeException("cant start window twice in a row. Must close window first");
}
restartWindow(now);
}
private String frameCaptureSummary(FrameSampleSet currentFrame) {
StringBuilder sb = new StringBuilder();
for (FrameSample fs : this.currentFrame) {
for (FrameSample fs : this.activeFrame) {
sb.append(fs.index()).append(" T:").append(fs.startAt()).append("-").append(fs.endAt()).append(" V:")
.append(fs.startval()).append(",").append(fs.endval()).append("\n");
}
@ -165,12 +193,12 @@ public class SimFrameCapture implements SimFrameResults {
}
public void stopWindow(long now) {
for (int i = 0; i < currentFrame.size(); i++) {
currentFrame.set(i, currentFrame.get(i).stop(now));
for (int i = 0; i < activeFrame.size(); i++) {
activeFrame.set(i, activeFrame.get(i).stop(now));
}
allFrames.add(currentFrame);
allFrames.add(activeFrame);
// System.out.println("after stop:\n"+ frameCaptureSummary(currentFrame));
currentFrame = null;
activeFrame = null;
}
public FrameSampleSet last() {
@ -181,6 +209,10 @@ public class SimFrameCapture implements SimFrameResults {
addRemix(name, remix, 1.0, null);
}
public FrameSampleSet activeSample() {
return activeFrame;
}
public static class FrameSamples extends ArrayList<FrameSampleSet> {
}

View File

@ -14,7 +14,10 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import io.nosqlbench.scenarios.simframe.planning.SimFrame;
import io.nosqlbench.scenarios.simframe.findmax.FindMaxFrameParams;
import java.util.ArrayList;
import java.util.Collections;
@ -25,7 +28,7 @@ import java.util.List;
* Aggregate usage patterns around capturing and using simulation frame data.
*/
public class SimFrameJournal extends ArrayList<SimFrame> implements JournalView {
public void record(SimFrameParams params, FrameSampleSet samples) {
public void record(FindMaxFrameParams params, FrameSampleSet samples) {
add(new SimFrame(params, samples));
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.capture;
import java.util.List;

View File

@ -14,15 +14,16 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.findmax;
/**
* These parameters are calculated by the planner based on previous simulation frame history.
*/
public record SimFrameParams(
public record FindMaxFrameParams(
double rate_shelf,
double rate_delta,
long sample_time_ms,
long settling_time_ms,
String description
) {
public double computed_rate() {

View File

@ -14,32 +14,26 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.findmax;
import io.nosqlbench.scenarios.simframe.capture.JournalView;
import io.nosqlbench.scenarios.simframe.planning.SimFrame;
import io.nosqlbench.scenarios.simframe.planning.SimFramePlanner;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class SimFramePlanner {
private final Logger logger = LogManager.getLogger(SimFramePlanner.class);
/**
* Search params which control findmax
*/
private final FindmaxSearchParams findmax;
public SimFramePlanner(FindmaxSearchParams findMaxSettings) {
this.findmax = findMaxSettings;
public class FindMaxPlanner extends SimFramePlanner<FindmaxSearchParams, FindMaxFrameParams> {
private final Logger logger = LogManager.getLogger(FindMaxPlanner.class);
public FindMaxPlanner(FindmaxSearchParams findMaxSettings) {
super(findMaxSettings);
}
public SimFrameParams initialStep() {
return new SimFrameParams(
this.findmax.rate_base(), this.findmax.rate_step(), this.findmax.sample_time_ms(), "INITIAL"
public FindMaxFrameParams initialStep() {
return new FindMaxFrameParams(
config.rate_base(), config.rate_step(), config.sample_time_ms(), config.min_settling_ms(), "INITIAL"
);
}
@ -52,26 +46,28 @@ public class SimFramePlanner {
* All parameters and results, organized in enumerated simulation frames
* @return Optionally, a set of params which indicates another simulation frame should be sampled, else null
*/
public SimFrameParams nextStep(JournalView journal) {
public FindMaxFrameParams nextStep(JournalView journal) {
SimFrame last = journal.last();
SimFrame best = journal.bestRun();
if (best.index() == last.index()) { // got better consecutively
return new SimFrameParams(
return new FindMaxFrameParams(
last.params().rate_shelf(),
last.params().rate_delta() * findmax.rate_incr(),
last.params().rate_delta() * config.rate_incr(),
last.params().sample_time_ms(),
config.min_settling_ms(),
"CONTINUE after improvement from frame " + last.index()
);
} else if (best.index() == last.index() - 1) {
// got worse consecutively, this may be collapsed out since the general case below covers it (test first)
if ((last.params().computed_rate() - best.params().computed_rate()) <= findmax.rate_step()) {
if ((last.params().computed_rate() - best.params().computed_rate()) <= config.rate_step()) {
logger.info("could not divide search space further, stop condition met");
return null;
} else {
return new SimFrameParams(
return new FindMaxFrameParams(
best.params().computed_rate(),
findmax.rate_step(),
(long) (last.params().sample_time_ms() * findmax.sample_incr()),
config.rate_step(),
(long) (last.params().sample_time_ms() * config.sample_incr()),
config.min_settling_ms()*4,
"REBASE search range to new base after frame " + best.index()
);
}
@ -82,11 +78,12 @@ public class SimFramePlanner {
.filter(f -> f.params().computed_rate() > best.params().computed_rate())
.min(Comparator.comparingDouble(f -> f.params().computed_rate()))
.orElseThrow(() -> new RuntimeException("inconsistent samples"));
if ((nextWorseFrameWithHigherRate.params().computed_rate() - best.params().computed_rate()) > findmax.rate_step()) {
return new SimFrameParams(
if ((nextWorseFrameWithHigherRate.params().computed_rate() - best.params().computed_rate()) > config.rate_step()) {
return new FindMaxFrameParams(
best.params().computed_rate(),
findmax.rate_step(),
(long) (last.params().sample_time_ms() * findmax.sample_incr()),
config.rate_step(),
(long) (last.params().sample_time_ms() * config.sample_incr()),
config.min_settling_ms()* 2,
"REBASE search range from frames " + best.index() + "" +nextWorseFrameWithHigherRate.index()
);
} else {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.findmax;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioParams;
@ -32,23 +32,22 @@ public record FindmaxSearchParams(
double average_of,
double latency_cutoff,
double latency_pctile,
double testrate_cutoff,
double bestrate_cutoff
long min_settling_ms
) {
public FindmaxSearchParams(ScenarioParams params) {
this(
params.maybeGet("sample_time_ms").map(Integer::parseInt).orElse(3000),
params.maybeGet("sample_time_ms").map(Integer::parseInt).orElse(4000),
params.maybeGet("sample_max").map(Integer::parseInt).orElse(10000),
params.maybeGet("sample_incr").map(Double::parseDouble).orElse(1.01d),
params.maybeGet("sample_incr").map(Double::parseDouble).orElse(1.2d),
params.maybeGet("rate_base").map(Double::parseDouble).orElse(0d),
params.maybeGet("rate_step").map(Double::parseDouble).orElse(100d),
params.maybeGet("rate_incr").map(Double::parseDouble).orElse(2.0d),
params.maybeGet("rate_incr").map(Double::parseDouble).orElse(2d),
params.maybeGet("average_of").map(Integer::parseInt).orElse(2),
params.maybeGet("latency_cutoff").map(Double::parseDouble).orElse(50.0d),
params.maybeGet("testrate_cutoff").map(Double::parseDouble).orElse(0.8),
params.maybeGet("bestrate_cutoff").map(Double::parseDouble).orElse(0.90),
params.maybeGet("latency_pctile").map(Double::parseDouble).orElse(0.99)
params.maybeGet("latency_pctile").map(Double::parseDouble).orElse(0.99),
params.maybeGet("min_settling_ms").map(Long::parseLong).orElse(4000L)
);
}
}

View File

@ -14,9 +14,10 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.findmax;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricHistogram;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricTimer;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.components.events.ParamChange;
@ -24,6 +25,8 @@ import io.nosqlbench.engine.api.activityapi.core.Activity;
import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec;
import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec;
import io.nosqlbench.engine.core.lifecycle.scenario.direct.SCBaseScenario;
import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture;
import io.nosqlbench.scenarios.simframe.capture.SimFrameJournal;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -81,20 +84,32 @@ public class SC_findmax extends SCBaseScenario {
FindmaxSearchParams findmaxSettings = new FindmaxSearchParams(params);
int sampletime_ms = findmaxSettings.sample_time_ms();
Activity flywheel = controller.start(activityParams);
SimFrameCapture capture = this.perfValueMeasures(flywheel);
SimFramePlanner planner = new SimFramePlanner(findmaxSettings);
FindMaxPlanner planner = new FindMaxPlanner(findmaxSettings);
SimFrameJournal journal = new SimFrameJournal();
SimFrameParams frameParams = planner.initialStep();
FindMaxFrameParams frameParams = planner.initialStep();
while (frameParams != null) {
stdout.println(frameParams);
// flywheel.onEvent(ParamChange.of(new CycleRateSpec(frameParams.computed_rate(), 1.05d, SimRateSpec.Verb.restart)));
// long settling_time = frameParams.settling_time_ms();
// if (settling_time>0) {
// stdout.println("settling for " + settling_time + " ms");
// controller.waitMillis(settling_time);
// }
flywheel.onEvent(ParamChange.of(new CycleRateSpec(frameParams.computed_rate(), 1.05d, SimRateSpec.Verb.restart)));
capture.startWindow();
controller.waitMillis(frameParams.sample_time_ms());
for (int i = 0; i < 10; i++) {
controller.waitMillis(frameParams.sample_time_ms()/10);
stdout.println(capture.activeSample());
}
// controller.waitMillis(frameParams.sample_time_ms());
// capture.awaitSteadyState();
// capture.restartWindow();
// flywheel.onEvent(ParamChange.of(new CycleRateSpec(frameParams.computed_rate(), 1.05d, SimRateSpec.Verb.restart)));
// controller.waitMillis(frameParams.sample_time_ms());
capture.stopWindow();
journal.record(frameParams, capture.last());
stdout.println(capture.last());
@ -113,6 +128,8 @@ public class SC_findmax extends SCBaseScenario {
NBMetricTimer result_timer = activity.find().timer("name:result");
NBMetricTimer result_success_timer = activity.find().timer("name:result_success");
NBMetricGauge cyclerate_gauge = activity.find().gauge("name=config_cyclerate");
NBMetricHistogram tries_histo_src = activity.find().histogram("name=tries");
NBMetricHistogram tries_histo = tries_histo_src.attachHdrDeltaHistogram();
sampler.addDirect("target_rate", cyclerate_gauge::getValue, Double.NaN);
sampler.addDeltaTime("achieved_oprate", result_timer::getCount, Double.NaN);
@ -128,32 +145,11 @@ public class SC_findmax extends SCBaseScenario {
double basis = Math.min(1.0d, vars.get("achieved_ok_oprate") / vars.get("target_rate"));
return Math.pow(basis, 3);
});
// sampler.addRemix("retries_p99", (vars) -> {
// double retriesP99 = tries_histo.getDeltaSnapshot(90).get99thPercentile();
// return 1/retriesP99;
// });
// TODO: add response time with a sigmoid style threshold at fractional_quantile and cutoff_ms
// TODO: add tries based saturation detection, where p99 tries start increasing above 1
// // response time
// sampler.addDirect(
// "latency",
// () -> {
// double quantile_response_ns = result_success_timer.getDeltaSnapshot(1000).getValue(fractional_quantile);
// if (quantile_response_ns * 1000000 > cutoff_ms) {
// return 0.0d;
// } else {
// return quantile_response_ns;
// }
// },
// -1
// );
//
// // error count
// sampler.addDeltaTime(
// "error_rate",
// () -> result_timer.getCount() - result_success_timer.getCount(),
// -1
// );
//
return sampler;
}

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.optimo;
import io.nosqlbench.scenarios.simframe.capture.JournalView;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* A frame planner is what decides what next set of parameters to try based on a history
* of simulation frames, and whether to proceed with another sim frame.
* @param <C> The configuration type for the planner
* @param <P> The parameter set type for the planner, emitted for each time another sim frame should be run
*/
public abstract class OptimoPlanner<C,P> {
private final Logger logger = LogManager.getLogger(OptimoPlanner.class);
protected final C config;
public OptimoPlanner(C plannerConfig) {
this.config = plannerConfig;
}
public abstract P initialStep();
/**
* Using a stateful history of all control parameters and all results, decide if there
* is additional search space and return a set of parameters for the next workload
* simulation frame. If the stopping condition has been met, return null
*
* @param journal
* All parameters and results, organized in enumerated simulation frames
* @return Optionally, a set of params which indicates another simulation frame should be sampled, else null
*/
public abstract P nextStep(JournalView journal);
}

View File

@ -1,22 +1,20 @@
package io.nosqlbench.scenarios;
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2023 nosqlbench
*
* 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
* 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.
* 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.scenarios.simframe.optimo;
import io.nosqlbench.api.engine.metrics.ConvenientSnapshot;
import io.nosqlbench.api.engine.metrics.DeltaSnapshotReader;
@ -29,7 +27,7 @@ import io.nosqlbench.engine.api.activityapi.core.Activity;
import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.CycleRateSpec;
import io.nosqlbench.engine.api.activityapi.ratelimits.simrate.SimRateSpec;
import io.nosqlbench.engine.core.lifecycle.scenario.direct.SCBaseScenario;
import io.nosqlbench.scenarios.findmax.SimFrameCapture;
import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View File

@ -14,14 +14,17 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios.findmax;
package io.nosqlbench.scenarios.simframe.planning;
import io.nosqlbench.scenarios.simframe.capture.FrameSampleSet;
import io.nosqlbench.scenarios.simframe.findmax.FindMaxFrameParams;
/**
* Capture the control inputs as well as the samples of a sample period of a simulated workload.
* @param params The parameters which control the simulated workload during the sample window
* @param samples The measured samples, including key metrics and criteria for the sample window
*/
public record SimFrame(SimFrameParams params, FrameSampleSet samples) {
public record SimFrame(FindMaxFrameParams params, FrameSampleSet samples) {
public double value() {
return samples().value();
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.planning;
import io.nosqlbench.scenarios.simframe.capture.JournalView;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* A frame planner is what decides what next set of parameters to try based on a history
* of simulation frames, and whether to proceed with another sim frame.
* @param <C> The configuration type for the planner
* @param <P> The parameter set type for the planner, emitted for each time another sim frame should be run
*/
public abstract class SimFramePlanner<C,P> {
private final Logger logger = LogManager.getLogger(SimFramePlanner.class);
protected final C config;
public SimFramePlanner(C plannerConfig) {
this.config = plannerConfig;
}
public abstract P initialStep();
/**
* Using a stateful history of all control parameters and all results, decide if there
* is additional search space and return a set of parameters for the next workload
* simulation frame. If the stopping condition has been met, return null
*
* @param journal
* All parameters and results, organized in enumerated simulation frames
* @return Optionally, a set of params which indicates another simulation frame should be sampled, else null
*/
public abstract P nextStep(JournalView journal);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
public class DoubleRing {
private final double[] dbuf;
private int count;
private int idx;
public DoubleRing(int size) {
this.dbuf = new double[size];
this.count = 0;
}
public DoubleRing(double[] samples) {
this.dbuf=samples;
this.count =samples.length;
}
public double push(double value) {
double ejected = (count == dbuf.length) ? dbuf[idx] : Double.NaN;
count += (count < dbuf.length) ? 1 : 0;
dbuf[idx] = value;
idx = (idx + 1) % dbuf.length;
return ejected;
}
public int size() {
return dbuf.length;
}
public int count() {
return count;
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.function.DoubleSupplier;
public class StabilityDetector implements Runnable {
private final static Logger logger = LogManager.getLogger(StabilityDetector.class);
private final double timeSliceSeconds;
private final double threshold;
private final DoubleSupplier source;
private StatBucket[] buckets;
private int[] windows;
private volatile boolean running = true;
/**
* Configure a stability checker that reads values from a source on some timed loop,
* computes the streaming standard deviation, computes the ratio of stabilization between
* values from longer windows to shorter windows, and returns from its run method once
* the computed ratio is higher than the min threshold.
*
* @param timeSliceSeconds
* How frequently to gather a sample. 0.1 is recommended to start
* @param minThreshold
* The unit interval fractional stability measurement which must be met at a minimum in order to stop polling
* for stability
* @param source
* The source of data to be added to the streaming std dev computations
* @param windows
* The size of each window in the set of diminishing sizes. These contain the last N samples by size,
* respectively.
*/
public StabilityDetector(double timeSliceSeconds, double minThreshold, DoubleSupplier source, int... windows) {
if (windows.length < 2) {
throw new RuntimeException("you must provide at least to summarization windows, ordered in decreasing size.");
}
this.timeSliceSeconds = timeSliceSeconds;
this.threshold = minThreshold;
this.source = source;
this.windows = windows;
for (int i = 0; i < windows.length - 1; i++) {
if (windows[i] < windows[i + 1]) {
throw new RuntimeException("windows must be provided in descending size, but you specified " + List.of(windows));
}
}
}
private void reset() {
this.buckets = new StatBucket[windows.length];
for (int i = 0; i < windows.length; i++) {
buckets[i] = new StatBucket(windows[i]);
}
}
public double apply(double value) {
for (StatBucket bucket : buckets) {
bucket.apply(value);
}
return computeStability();
}
private boolean primed() {
for (StatBucket bucket : buckets) {
if (!bucket.primed()) {
return false;
}
}
return true;
}
private double computeStability() {
if (!primed()) {
return -1.0d;
}
double[] stddev = new double[buckets.length];
for (int i = 0; i < buckets.length; i++) {
stddev[i] = buckets[i].stddev();
}
double basis = 1.0d;
for (int i = 0; i < buckets.length - 1; i++) {
double reductionFactor = stddev[i] / stddev[i + 1];
basis *= reductionFactor;
}
System.out.printf("STABILITY %g :", basis);
for (int i = 0; i < stddev.length; i++) {
System.out.printf("[%d]: %g ", windows[i], stddev[i]);
}
System.out.println();
// logger.info("STABILITY " + basis);
return basis;
}
/**
* This run method is meant to be reused, since it resets internal state each time
*/
@Override
public void run() {
int interval = (int) this.timeSliceSeconds / 1000;
reset();
boolean steadyEnough = false;
long lastCheck = System.currentTimeMillis();
long nextCheckAt = lastCheck + interval;
while (running && !steadyEnough) {
long delay = nextCheckAt - System.currentTimeMillis();
while (delay > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {
}
delay = nextCheckAt - System.currentTimeMillis();
}
double value = source.getAsDouble();
apply(value);
double stabilityFactor = computeStability();
if (stabilityFactor > threshold) {
return;
}
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
import java.util.Objects;
public final class StatBucket {
DoubleRing ringbuf;
private double mean;
private double dSquared = 0.0d;
public StatBucket() {
this(10);
}
public StatBucket(int sampleWindow) {
this.ringbuf = new DoubleRing(sampleWindow);
}
public StatBucket(double[] samples) {
this.ringbuf = new DoubleRing(samples);
}
public StatBucket apply(double value) {
double popped = ringbuf.push(value);
if (ringbuf.count() == 1) {
mean = value;
dSquared = 0.0d;
} else if (Double.isNaN(popped)) {
var newMean = mean + ((value - mean) / ringbuf.count());
var dSquaredIncrement = ((value - newMean) * (value - mean));
dSquared += dSquaredIncrement;
mean = newMean;
} else {
var meanIncrement = (value - popped) / ringbuf.count();
var newMean = mean + meanIncrement;
var dSquaredIncrement = ((value - popped) * (value - newMean + popped - mean));
var newDSquared = this.dSquared + dSquaredIncrement;
mean = newMean;
dSquared = newDSquared;
}
return this;
}
public double variance() {
return dSquared / ringbuf.count();
}
public double stddev() {
return Math.sqrt(variance());
}
public int count() {
return ringbuf.count();
}
public double mean() {
return mean;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (StatBucket) obj;
return this.ringbuf.count() == that.ringbuf.count() &&
Double.doubleToLongBits(this.mean) == Double.doubleToLongBits(that.mean);
}
@Override
public int hashCode() {
return Objects.hash(ringbuf.count(), mean);
}
@Override
public String toString() {
return "StatBucket[" +
"count=" + ringbuf.count() + ", " +
"mean=" + mean + ", " +
"stddev=" + stddev() + ']';
}
public boolean primed() {
return this.count()== ringbuf.size();
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.function.ToDoubleFunction;
public class StatFunctions {
// public static double[] lastStddev(int[] ranges, LinkedList<TimedSample> values) {
// double[] avgs = new double[ranges.length];
// for (int i = 0; i < ranges.length; i++) {
// int range = ranges[i];
// if (values.size()>=range) {
// double avg=0.0d;
// ListIterator<TimedSample> iter = listIterator(size() - range);
// while (iter.hasNext()) avg += (iter.next().value / range);
// avgs[i]=avg;
// } else {
// avgs[i]=Double.NaN;
// }
// }
//
// }
//
// public static double[] stackedSample(LinkedList<TimedSample> values, ToDoubleFunction<double[]> func, int... windows) {
// double[] results = new double[windows.length];
// for (int i = 0; i < windows.length; i++) {
// int range = windows[i];
// if (values.size()>=range) {
// double avg=0.0d;
// ListIterator<TimedSample> iter = listIterator(size() - range);
// while (iter.hasNext()) avg += (iter.next().value / range);
// results[i]=avg;
// } else {
// results[i]=Double.NaN;
// }
// }
//
//
// }
}

View File

@ -14,13 +14,7 @@
* limitations under the License.
*/
package io.nosqlbench.scenarios;
package io.nosqlbench.scenarios.simframe.stats;
public enum Weighting {
uniform;
public double applyWeight(double input) {
return switch (this) {
case uniform -> input;
};
}
public record TimedSample(long msTime, double value) {
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
import java.util.*;
public class TimedSamples extends LinkedList<TimedSample> {
private long startAt;
public TimedSamples(long startTimeMs) {
this.startAt =startTimeMs;
}
public boolean isStable() {
if ((System.currentTimeMillis()-this.startAt) < 1000) return false;
if (this.size()<50) return false;
int[] ranges=new int[]{100,50,25,10};
double[] avgs = new double[ranges.length];
for (int i = 0; i < ranges.length; i++) {
int range = ranges[i];
if (size()>=range) {
double avg=0.0d;
ListIterator<TimedSample> iter = listIterator(size() - range);
while (iter.hasNext()) avg += (iter.next().value() / range);
avgs[i]=avg;
} else {
avgs[i]=Double.NaN;
}
}
return false;
}
// private double[] stdDevDecades(double[] values) {
// int offset=(values.length%10);
// int[] decades = new int[values.length/10];
// for (int d = 0; d<=decades.length; d++) {
// int start = (d*10)+offset;
// int end = start+10;
// for (int idx = start; idx < end; idx++) {
//
// }
// }
// for (int i = offset; i < values.length; i++) {
//
// }
// double[] v = new double[values.length];
// for (int idx = 0; idx <values.length; idx++) {
// v[idx]=values[(values.length-1)-idx];
// }
//
//
// }
private double stddev(double[] values) {
double acc= 0.0d;
for (double value : values) {
acc+=value;
}
var mean = acc/values.length;
acc=0.0d;
for(double value : values) {
acc += (mean-value)*(mean-value);
}
var meanDiffs=acc/values.length;
return Math.sqrt(meanDiffs);
}
private static double moving20Avg() {
return 0;
}
public double addAndGet(long milliTime, double newValue) {
add(new TimedSample(milliTime, newValue));
return newValue;
}
}

View File

@ -16,7 +16,7 @@
package io.nosqlbench.scenarios;
import io.nosqlbench.scenarios.findmax.SimFrameCapture;
import io.nosqlbench.scenarios.simframe.capture.SimFrameCapture;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 nosqlbench
*
* 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.scenarios.simframe.stats;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class StatBucketTest {
@Test
public void testStreamingMean() {
var bucket = new StatBucket();
bucket.apply(5.0d);
assertThat(bucket.mean()).isCloseTo(5.0d,Offset.offset(0.001d));
bucket.apply(10.0d);
assertThat(bucket.mean()).isCloseTo(7.5d,Offset.offset(0.001d));
bucket.apply(15.0d);
assertThat(bucket.mean()).isCloseTo(10.0d,Offset.offset(0.001d));
bucket.apply(20.0d);
assertThat(bucket.mean()).isCloseTo(12.5d,Offset.offset(0.001d));
}
@Test
public void testStreamingComputations() {
double[] samples = new double[]{2,4,4,4,5,5,7,9};
var bucket = new StatBucket(8);
for (int i = 0; i < samples.length * 10; i++) {
bucket.apply(samples[i%samples.length]);
if (i>0&&(i%samples.length)==0) {
assertThat(bucket.mean()).isCloseTo(5,Offset.offset(0.001d));
assertThat(bucket.stddev()).isCloseTo(2.0,Offset.offset(0.001d));
}
}
}
}

View File

@ -37,7 +37,7 @@ class IVecReaderTest {
HashSet<Integer> ref = idx_ref.get(0);
for (int j = 0; j < indices.length; j++) {
assertThat(indices[j]).isGreaterThanOrEqualTo(0);
assertThat(indices[j]).isLessThanOrEqualTo(10000);
assertThat(indices[j]).isLessThan(10000);
}
}
}