Skip to content

Commit

Permalink
Bug fix for automatic name detection when profiling
Browse files Browse the repository at this point in the history
  • Loading branch information
David Legg committed Dec 14, 2023
1 parent e02a782 commit ae8331b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

import gov.nasa.jpl.aerie.contrib.streamline.core.*;
import gov.nasa.jpl.aerie.merlin.framework.Condition;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;
import gov.nasa.jpl.aerie.merlin.protocol.types.Unit;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.*;
import static java.util.Comparator.comparingLong;
import static org.apache.commons.lang3.StringUtils.isEmpty;

/**
* Functions for profiling resources and conditions
Expand Down Expand Up @@ -45,57 +47,65 @@ private Profiling() {}
private static final Map<String, CallStats> effectsEmitted = new HashMap<>();

public static <D> Resource<D> profile(Resource<D> resource) {
return profile(Naming.getName(resource).orElse("anonymous resource"), resource);
return profile(null, resource);
}

public static <D> Resource<D> profile(String name, Resource<D> resource) {
initialize("Resource", resourceSamples, name);
return () -> resourceSamples.get(name).accrue(resource::getDynamics);
Resource<D> result = new Resource<>() {
@Override
public ErrorCatching<Expiring<D>> getDynamics() {
return resourceSamples.computeIfAbsent(computeName(name, this), k -> new CallStats())
.accrue(resource::getDynamics);
}
};
assignName(result, name, resource);
return result;
}

public static Condition profile(Condition condition) {
return profile(Naming.getName(condition).orElse("anonymous condition"), condition);
return profile(null, condition);
}

public static Condition profile(String name, Condition condition) {
initialize("Condition", conditionEvaluations, name);
return (positive, atEarliest, atLatest) ->
conditionEvaluations.get(name).accrue(
() -> condition.nextSatisfied(positive, atEarliest, atLatest));
Condition result = new Condition() {
@Override
public Optional<Duration> nextSatisfied(boolean positive, Duration atEarliest, Duration atLatest) {
return accrue(conditionEvaluations, computeName(name, this), () -> condition.nextSatisfied(positive, atEarliest, atLatest));
}
};
assignName(result, name, condition);
return result;
}

public static Supplier<Condition> profile(Supplier<Condition> conditionSupplier) {
return profile(Naming.getName(conditionSupplier).orElse("anonymous condition"), conditionSupplier);
return profile(null, conditionSupplier);
}

public static Supplier<Condition> profile(String name, Supplier<Condition> conditionSupplier) {
initialize("Condition", conditionEvaluations, name);
return () -> {
final var condition = conditionSupplier.get();
return (positive, atEarliest, atLatest) ->
conditionEvaluations.get(name).accrue(
() -> condition.nextSatisfied(positive, atEarliest, atLatest));
};
return () -> profile(name, conditionSupplier.get());
}

public static Runnable profile(Runnable task) {
return profile(Naming.getName(task).orElse("anonymous task"), task);
return profile(null, task);
}

public static Runnable profile(String name, Runnable task) {
if (isEmpty(name)) return task;
initialize("Task", taskExecutions, name);
return () -> taskExecutions.get(name).accrue(task);
return () -> profileTask(name, () -> { task.run(); return Unit.UNIT; });
}

public static <R> Supplier<R> profileTask(Supplier<R> task) {
return profileTask(Naming.getName(task).orElse("anonymous task"), task);
return profileTask(null, task);
}

public static <R> Supplier<R> profileTask(String name, Supplier<R> task) {
if (isEmpty(name)) return task;
initialize("Task", taskExecutions, name);
return () -> taskExecutions.get(name).accrue(task);
Supplier<R> result = new Supplier<>() {
@Override
public R get() {
return accrue(taskExecutions, computeName(name, this), task);
}
};
assignName(result, name, task);
return result;
}

private static long ANONYMOUS_CELL_RESOURCE_ID = 0;
Expand All @@ -107,14 +117,14 @@ public void emit(DynamicsEffect<D> effect) {
// Get the name the first time an effect is emitted,
// which will be after any registrations happen.
if (name == null) {
name = Naming.getName(this).orElseGet(() -> {
name = getName(this, "...");
if (name.equals("...")) {
var generatedName = "CellResource" + (ANONYMOUS_CELL_RESOURCE_ID++);
Naming.name(this, generatedName);
return generatedName;
});
initialize("Effects", effectsEmitted, name);
name(this, generatedName);
name = generatedName;
}
}
resource.emit(x -> effectsEmitted.get(name).accrue(() -> effect.apply(x)));
resource.emit(x -> accrue(effectsEmitted, name, () -> effect.apply(x)));
}

@Override
Expand All @@ -124,14 +134,22 @@ public ErrorCatching<Expiring<D>> getDynamics() {
};
}

private static void initialize(String typeName, Map<String, CallStats> resourceSamples, String name) {
if (resourceSamples.containsKey(name)) {
System.out.printf("WARNING! %s %s is already being profiled. This may be a name collision.%n", typeName, name);
private static String computeName(String explicitName, Object profiledThing) {
return explicitName != null ? explicitName : getName(profiledThing, "...");
}

private static void assignName(Object profiledThing, String explicitName, Object originalThing) {
if (explicitName == null) {
name(profiledThing, "%s", originalThing);
} else {
resourceSamples.put(name, new CallStats());
name(profiledThing, explicitName);
}
}

private static <R> R accrue(Map<String, CallStats> statsMap, String name, Supplier<R> call) {
return statsMap.computeIfAbsent(name, k -> new CallStats()).accrue(call);
}

public static void dump() {
long overallElapsedNanos = System.nanoTime() - overallStartTime;
System.out.printf("Overall time: %d ms%n", overallElapsedNanos / 1_000_000);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gov.nasa.jpl.aerie.streamline_demo;

import gov.nasa.jpl.aerie.merlin.framework.annotations.Export.Parameter;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

public final class Configuration {
@Parameter
Expand All @@ -9,4 +10,7 @@ public final class Configuration {
@Parameter
public double approximationTolerance = 1e-2;

@Parameter
public Duration profilingDumpTime = Duration.ZERO;

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.linear.Linear;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.LinearBoundaryConsistencySolver;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.LinearBoundaryConsistencySolver.Domain;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.Polynomial;
Expand All @@ -12,20 +11,10 @@
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.eraseExpiry;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.forward;
import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad.*;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.*;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.choose;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.linear.Linear.linear;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.LinearBoundaryConsistencySolver.Comparison.*;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.LinearBoundaryConsistencySolver.LinearExpression.lx;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.add;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.clamp;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.constant;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.differentiate;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.greaterThanOrEquals;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.lessThanOrEquals;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.max;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.polynomialResource;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.subtract;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.*;

public class DataModel {
public MutableResource<Polynomial> desiredRateA = PolynomialResources.polynomialResource(0);
Expand Down Expand Up @@ -100,31 +89,18 @@ public DataModel(final Registrar registrar, final Configuration config) {
}

private void registerStates(Registrar registrar, Configuration config) {
registrar.real("desiredRateA", linearize(desiredRateA));
registrar.real("desiredRateB", linearize(desiredRateB));
registrar.real("desiredRateC", linearize(desiredRateC));

registrar.real("actualRateA", linearize(actualRateA));
registrar.real("actualRateB", linearize(actualRateB));
registrar.real("actualRateC", linearize(actualRateC));

registrar.real("volumeA", linearize(volumeA));
registrar.real("volumeB", linearize(volumeB));
registrar.real("volumeC", linearize(volumeC));
registrar.real("totalVolume", linearize(totalVolume));
registrar.real("maxVolume", linearize(upperBoundOnTotalVolume));
}

static Resource<Linear> linearize(Resource<Polynomial> p) {
var result = map(p, p$ -> {
if (p$.degree() <= 1) {
return linear(p$.getCoefficient(0), p$.getCoefficient(1));
} else {
throw new IllegalStateException("Resource was super-linear");
}
});
// Reverse the normal direction of naming, so that names registered for result propagate back to p
name(p, "%s", result);
return result;
registrar.real("desiredRateA", assumeLinear(desiredRateA));
registrar.real("desiredRateB", assumeLinear(desiredRateB));
registrar.real("desiredRateC", assumeLinear(desiredRateC));

registrar.real("actualRateA", assumeLinear(actualRateA));
registrar.real("actualRateB", assumeLinear(actualRateB));
registrar.real("actualRateC", assumeLinear(actualRateC));

registrar.real("volumeA", assumeLinear(volumeA));
registrar.real("volumeB", assumeLinear(volumeB));
registrar.real("volumeC", assumeLinear(volumeC));
registrar.real("totalVolume", assumeLinear(totalVolume));
registrar.real("maxVolume", assumeLinear(upperBoundOnTotalVolume));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@

import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.discreteResource;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteResourceMonad.map;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.asPolynomial;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.clamp;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.constant;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.multiply;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.polynomialResource;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.polynomial.PolynomialResources.*;

public class ErrorTestingModel {
public MutableResource<Discrete<Boolean>> bool = DiscreteResources.discreteResource(true);
Expand All @@ -34,11 +30,10 @@ public class ErrorTestingModel {
public ErrorTestingModel(final Registrar registrar, final Configuration config) {
registrar.discrete("errorTesting/bool", bool, new BooleanValueMapper());
registrar.discrete("errorTesting/counter", counter, new IntegerValueMapper());
// Explicitly register a name for continuous, because the derived linearized resource can't have effects
registrar.real("errorTesting/continuous", DataModel.linearize(continuous));
registrar.real("errorTesting/derived", DataModel.linearize(derived));
registrar.real("errorTesting/lowerBound", DataModel.linearize(lowerBound));
registrar.real("errorTesting/upperBound", DataModel.linearize(upperBound));
registrar.real("errorTesting/clamped", DataModel.linearize(clamped));
registrar.real("errorTesting/continuous", assumeLinear(continuous));
registrar.real("errorTesting/derived", assumeLinear(derived));
registrar.real("errorTesting/lowerBound", assumeLinear(lowerBound));
registrar.real("errorTesting/upperBound", assumeLinear(upperBound));
registrar.real("errorTesting/clamped", assumeLinear(clamped));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package gov.nasa.jpl.aerie.streamline_demo;

import gov.nasa.jpl.aerie.contrib.streamline.debugging.Profiling;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar;
import gov.nasa.jpl.aerie.merlin.framework.ModelActions;

public final class Mission {
public final DataModel dataModel;
Expand All @@ -13,5 +15,8 @@ public Mission(final gov.nasa.jpl.aerie.merlin.framework.Registrar registrar$, f
dataModel = new DataModel(registrar, config);
errorTestingModel = new ErrorTestingModel(registrar, config);
approximationModel = new ApproximationModel(registrar, config);
if (config.profilingDumpTime.isPositive()) {
ModelActions.defer(config.profilingDumpTime, Profiling::dump);
}
}
}

0 comments on commit ae8331b

Please sign in to comment.