mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2024-11-22 00:38:05 -06:00
stability detection in progress
This commit is contained in:
parent
651aaecbf0
commit
1cc2f5352c
@ -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 "dimensions:1024,dataset=e5_large_v2"" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$/local/jvector" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="jdk21" />
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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]));
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
// }
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.scenarios.findmax.conditioning;
|
||||
package io.nosqlbench.scenarios.simframe;
|
||||
|
||||
public class ZScore {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.scenarios.findmax;
|
||||
package io.nosqlbench.scenarios.simframe.capture;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -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;
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.scenarios.findmax;
|
||||
package io.nosqlbench.scenarios.simframe.capture;
|
||||
|
||||
public enum EvalType {
|
||||
/**
|
@ -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());
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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> {
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.scenarios.findmax;
|
||||
package io.nosqlbench.scenarios.simframe.capture;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -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() {
|
@ -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 {
|
@ -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)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
@ -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) {
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user