diff options
Diffstat (limited to 'tests/src/JIT/Performance/CodeQuality/V8/DeltaBlue/DeltaBlue.cs')
-rw-r--r-- | tests/src/JIT/Performance/CodeQuality/V8/DeltaBlue/DeltaBlue.cs | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/tests/src/JIT/Performance/CodeQuality/V8/DeltaBlue/DeltaBlue.cs b/tests/src/JIT/Performance/CodeQuality/V8/DeltaBlue/DeltaBlue.cs new file mode 100644 index 0000000000..ee14b8b1b1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/V8/DeltaBlue/DeltaBlue.cs @@ -0,0 +1,1068 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +/* + This is a Java implemention of the DeltaBlue algorithm described in: + "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver" + by Bjorn N. Freeman-Benson and John Maloney + January 1990 Communications of the ACM, + also available as University of Washington TR 89-08-06. + + This implementation by Mario Wolczko, Sun Microsystems, Sep 1996, + based on the Smalltalk implementation by John Maloney. + +*/ + +// The code has been adapted for use as a C# benchmark by Microsoft. + +#define USE_STACK + +using Microsoft.Xunit.Performance; +using System; +using System.Collections; + +[assembly: OptimizeForBenchmarks] +[assembly: MeasureInstructionsRetired] + +/* +Strengths are used to measure the relative importance of constraints. +New strengths may be inserted in the strength hierarchy without +disrupting current constraints. Strengths cannot be created outside +this class, so pointer comparison can be used for value comparison. +*/ + +internal class Strength +{ + private int _strengthValue; + private String _name; + + private Strength(int strengthValue, String name) + { + _strengthValue = strengthValue; + _name = name; + } + + public static Boolean stronger(Strength s1, Strength s2) + { + return s1._strengthValue < s2._strengthValue; + } + + public static Boolean weaker(Strength s1, Strength s2) + { + return s1._strengthValue > s2._strengthValue; + } + + public static Strength weakestOf(Strength s1, Strength s2) + { + return weaker(s1, s2) ? s1 : s2; + } + + public static Strength strongest(Strength s1, Strength s2) + { + return stronger(s1, s2) ? s1 : s2; + } + + // for iteration + public Strength nextWeaker() + { + switch (_strengthValue) + { + case 0: return weakest; + case 1: return weakDefault; + case 2: return normal; + case 3: return strongDefault; + case 4: return preferred; + case 5: return strongPreferred; + + case 6: + default: + Console.Error.WriteLine("Invalid call to nextStrength()!"); + //System.exit(1); + return null; + } + } + + // Strength constants + public static Strength required = new Strength(0, "required"); + public static Strength strongPreferred = new Strength(1, "strongPreferred"); + public static Strength preferred = new Strength(2, "preferred"); + public static Strength strongDefault = new Strength(3, "strongDefault"); + public static Strength normal = new Strength(4, "normal"); + public static Strength weakDefault = new Strength(5, "weakDefault"); + public static Strength weakest = new Strength(6, "weakest"); + + public void print() + { + Console.Write("strength[" + _strengthValue + "]"); + } +} + + + + +//------------------------------ variables ------------------------------ + +// I represent a constrained variable. In addition to my value, I +// maintain the structure of the constraint graph, the current +// dataflow graph, and various parameters of interest to the DeltaBlue +// incremental constraint solver. + +internal class Variable +{ + public int value; // my value; changed by constraints + public ArrayList constraints; // normal constraints that reference me + public Constraint determinedBy; // the constraint that currently determines + // my value (or null if there isn't one) + public int mark; // used by the planner to mark constraints + public Strength walkStrength; // my walkabout strength + public Boolean stay; // true if I am a planning-time constant + public String name; // a symbolic name for reporting purposes + + + private Variable(String name, int initialValue, Strength walkStrength, + int nconstraints) + { + value = initialValue; + constraints = new ArrayList(nconstraints); + determinedBy = null; + mark = 0; + this.walkStrength = walkStrength; + stay = true; + this.name = name; + } + + public Variable(String name, int value) : this(name, value, Strength.weakest, 2) + { + } + + public Variable(String name) : this(name, 0, Strength.weakest, 2) + { + } + + public void print() + { + Console.Write(name + "("); + walkStrength.print(); + Console.Write("," + value + ")"); + } + + // Add the given constraint to the set of all constraints that refer to me. + public void addConstraint(Constraint c) + { + constraints.Add(c); + } + + // Remove all traces of c from this variable. + public void removeConstraint(Constraint c) + { + constraints.Remove(c); + if (determinedBy == c) determinedBy = null; + } + + // Attempt to assign the given value to me using the given strength. + public void setValue(int value, Strength strength) + { + EditConstraint e = new EditConstraint(this, strength); + if (e.isSatisfied()) + { + this.value = value; + deltablue.planner.propagateFrom(this); + } + e.destroyConstraint(); + } +} + + + + +//------------------------ constraints ------------------------------------ + +// I am an abstract class representing a system-maintainable +// relationship (or "constraint") between a set of variables. I supply +// a strength instance variable; concrete subclasses provide a means +// of storing the constrained variables and other information required +// to represent a constraint. + +internal abstract class Constraint +{ + public Strength strength; // the strength of this constraint + + public Constraint() { } // this has to be here because of + // Java's constructor idiocy. + + public Constraint(Strength strength) + { + this.strength = strength; + } + + // Answer true if this constraint is satisfied in the current solution. + public abstract Boolean isSatisfied(); + + // Record the fact that I am unsatisfied. + public abstract void markUnsatisfied(); + + // Normal constraints are not input constraints. An input constraint + // is one that depends on external state, such as the mouse, the + // keyboard, a clock, or some arbitrary piece of imperative code. + public virtual Boolean isInput() { return false; } + + // Activate this constraint and attempt to satisfy it. + public void addConstraint() + { + addToGraph(); + deltablue.planner.incrementalAdd(this); + } + + // Deactivate this constraint, remove it from the constraint graph, + // possibly causing other constraints to be satisfied, and destroy + // it. + public void destroyConstraint() + { + if (isSatisfied()) deltablue.planner.incrementalRemove(this); + else + removeFromGraph(); + } + + // Add myself to the constraint graph. + public abstract void addToGraph(); + + // Remove myself from the constraint graph. + public abstract void removeFromGraph(); + + // Decide if I can be satisfied and record that decision. The output + // of the choosen method must not have the given mark and must have + // a walkabout strength less than that of this constraint. + public abstract void chooseMethod(int mark); + + // Set the mark of all input from the given mark. + public abstract void markInputs(int mark); + + // Assume that I am satisfied. Answer true if all my current inputs + // are known. A variable is known if either a) it is 'stay' (i.e. it + // is a constant at plan execution time), b) it has the given mark + // (indicating that it has been computed by a constraint appearing + // earlier in the plan), or c) it is not determined by any + // constraint. + public abstract Boolean inputsKnown(int mark); + + // Answer my current output variable. Raise an error if I am not + // currently satisfied. + public abstract Variable output(); + + // Attempt to find a way to enforce this constraint. If successful, + // record the solution, perhaps modifying the current dataflow + // graph. Answer the constraint that this constraint overrides, if + // there is one, or nil, if there isn't. + // Assume: I am not already satisfied. + // + public Constraint satisfy(int mark) + { + chooseMethod(mark); + if (!isSatisfied()) + { + if (strength == Strength.required) + { + deltablue.error("Could not satisfy a required constraint"); + } + return null; + } + // constraint can be satisfied + // mark inputs to allow cycle detection in addPropagate + markInputs(mark); + Variable outvar = output(); + Constraint overridden = outvar.determinedBy; + if (overridden != null) overridden.markUnsatisfied(); + outvar.determinedBy = this; + if (!deltablue.planner.addPropagate(this, mark)) + { + Console.WriteLine("Cycle encountered"); + return null; + } + outvar.mark = mark; + return overridden; + } + + // Enforce this constraint. Assume that it is satisfied. + public abstract void execute(); + + // Calculate the walkabout strength, the stay flag, and, if it is + // 'stay', the value for the current output of this + // constraint. Assume this constraint is satisfied. + public abstract void recalculate(); + + public abstract void printInputs(); + + public void printOutput() { output().print(); } + + public void print() + { + if (!isSatisfied()) + { + Console.Write("Unsatisfied"); + } + else + { + Console.Write("Satisfied("); + printInputs(); + Console.Write(" -> "); + printOutput(); + Console.Write(")"); + } + Console.Write("\n"); + } +} + + + +//-------------unary constraints------------------------------------------- + +// I am an abstract superclass for constraints having a single +// possible output variable. +// +internal abstract class UnaryConstraint : Constraint +{ + public Variable myOutput; // possible output variable + public Boolean satisfied; // true if I am currently satisfied + + public UnaryConstraint(Variable v, Strength strength) : base(strength) + + { + myOutput = v; + satisfied = false; + addConstraint(); + } + + // Answer true if this constraint is satisfied in the current solution. + public override Boolean isSatisfied() { return satisfied; } + + // Record the fact that I am unsatisfied. + public override void markUnsatisfied() { satisfied = false; } + + // Answer my current output variable. + public override Variable output() { return myOutput; } + + // Add myself to the constraint graph. + public override void addToGraph() + { + myOutput.addConstraint(this); + satisfied = false; + } + + // Remove myself from the constraint graph. + public override void removeFromGraph() + { + if (myOutput != null) myOutput.removeConstraint(this); + satisfied = false; + } + + // Decide if I can be satisfied and record that decision. + public override void chooseMethod(int mark) + { + satisfied = myOutput.mark != mark + && Strength.stronger(strength, myOutput.walkStrength); + } + + public override void markInputs(int mark) { } // I have no inputs + + public override Boolean inputsKnown(int mark) { return true; } + + // Calculate the walkabout strength, the stay flag, and, if it is + // 'stay', the value for the current output of this + // constraint. Assume this constraint is satisfied." + public override void recalculate() + { + myOutput.walkStrength = strength; + myOutput.stay = !isInput(); + if (myOutput.stay) execute(); // stay optimization + } + + public override void printInputs() { } // I have no inputs +} + + +// I am a unary input constraint used to mark a variable that the +// client wishes to change. +// +internal class EditConstraint : UnaryConstraint +{ + public EditConstraint(Variable v, Strength str) : base(v, str) { } + + // I indicate that a variable is to be changed by imperative code. + public override Boolean isInput() { return true; } + + public override void execute() { } // Edit constraints do nothing. +} + +// I mark variables that should, with some level of preference, stay +// the same. I have one method with zero inputs and one output, which +// does nothing. Planners may exploit the fact that, if I am +// satisfied, my output will not change during plan execution. This is +// called "stay optimization". +// +internal class StayConstraint : UnaryConstraint +{ + // Install a stay constraint with the given strength on the given variable. + public StayConstraint(Variable v, Strength str) : base(v, str) { } + + public override void execute() { } // Stay constraints do nothing. +} + + + + +//-------------binary constraints------------------------------------------- + + +// I am an abstract superclass for constraints having two possible +// output variables. +// +internal abstract class BinaryConstraint : Constraint +{ + public Variable v1, v2; // possible output variables + public sbyte direction; // one of the following... + public static sbyte backward = -1; // v1 is output + public static sbyte nodirection = 0; // not satisfied + public static sbyte forward = 1; // v2 is output + + public BinaryConstraint() { } // this has to be here because of + // Java's constructor idiocy. + + public BinaryConstraint(Variable var1, Variable var2, Strength strength) + : base(strength) + { + v1 = var1; + v2 = var2; + direction = nodirection; + addConstraint(); + } + + // Answer true if this constraint is satisfied in the current solution. + public override Boolean isSatisfied() { return direction != nodirection; } + + // Add myself to the constraint graph. + public override void addToGraph() + { + v1.addConstraint(this); + v2.addConstraint(this); + direction = nodirection; + } + + // Remove myself from the constraint graph. + public override void removeFromGraph() + { + if (v1 != null) v1.removeConstraint(this); + if (v2 != null) v2.removeConstraint(this); + direction = nodirection; + } + + // Decide if I can be satisfied and which way I should flow based on + // the relative strength of the variables I relate, and record that + // decision. + // + public override void chooseMethod(int mark) + { + if (v1.mark == mark) + direction = + v2.mark != mark && Strength.stronger(strength, v2.walkStrength) + ? forward : nodirection; + + if (v2.mark == mark) + direction = + v1.mark != mark && Strength.stronger(strength, v1.walkStrength) + ? backward : nodirection; + + // If we get here, neither variable is marked, so we have a choice. + if (Strength.weaker(v1.walkStrength, v2.walkStrength)) + direction = + Strength.stronger(strength, v1.walkStrength) ? backward : nodirection; + else + direction = + Strength.stronger(strength, v2.walkStrength) ? forward : nodirection; + } + + // Record the fact that I am unsatisfied. + public override void markUnsatisfied() { direction = nodirection; } + + // Mark the input variable with the given mark. + public override void markInputs(int mark) + { + input().mark = mark; + } + + public override Boolean inputsKnown(int mark) + { + Variable i = input(); + return i.mark == mark || i.stay || i.determinedBy == null; + } + + // Answer my current output variable. + public override Variable output() { return direction == forward ? v2 : v1; } + + // Answer my current input variable + public Variable input() { return direction == forward ? v1 : v2; } + + // Calculate the walkabout strength, the stay flag, and, if it is + // 'stay', the value for the current output of this + // constraint. Assume this constraint is satisfied. + // + public override void recalculate() + { + Variable invar = input(), outvar = output(); + outvar.walkStrength = Strength.weakestOf(strength, invar.walkStrength); + outvar.stay = invar.stay; + if (outvar.stay) execute(); + } + + public override void printInputs() + { + input().print(); + } +} + + +// I constrain two variables to have the same value: "v1 = v2". +// +internal class EqualityConstraint : BinaryConstraint +{ + // Install a constraint with the given strength equating the given variables. + public EqualityConstraint(Variable var1, Variable var2, Strength strength) + : base(var1, var2, strength) + + { + } + + // Enforce this constraint. Assume that it is satisfied. + public override void execute() + { + output().value = input().value; + } +} + + +// I relate two variables by the linear scaling relationship: "v2 = +// (v1 * scale) + offset". Either v1 or v2 may be changed to maintain +// this relationship but the scale factor and offset are considered +// read-only. +// +internal class ScaleConstraint : BinaryConstraint +{ + public Variable scale; // scale factor input variable + public Variable offset; // offset input variable + + // Install a scale constraint with the given strength on the given variables. + public ScaleConstraint(Variable src, Variable scale, Variable offset, + Variable dest, Strength strength) + { + // Curse this wretched language for insisting that constructor invocation + // must be the first thing in a method... + // ..because of that, we must copy the code from the inherited + // constructors. + this.strength = strength; + v1 = src; + v2 = dest; + direction = nodirection; + this.scale = scale; + this.offset = offset; + addConstraint(); + } + + // Add myself to the constraint graph. + public override void addToGraph() + { + base.addToGraph(); + scale.addConstraint(this); + offset.addConstraint(this); + } + + // Remove myself from the constraint graph. + public override void removeFromGraph() + { + base.removeFromGraph(); + if (scale != null) scale.removeConstraint(this); + if (offset != null) offset.removeConstraint(this); + } + + // Mark the inputs from the given mark. + public override void markInputs(int mark) + { + base.markInputs(mark); + scale.mark = offset.mark = mark; + } + + // Enforce this constraint. Assume that it is satisfied. + public override void execute() + { + if (direction == forward) + v2.value = v1.value * scale.value + offset.value; + else + v1.value = (v2.value - offset.value) / scale.value; + } + + // Calculate the walkabout strength, the stay flag, and, if it is + // 'stay', the value for the current output of this + // constraint. Assume this constraint is satisfied. + public override void recalculate() + { + Variable invar = input(), outvar = output(); + outvar.walkStrength = Strength.weakestOf(strength, invar.walkStrength); + outvar.stay = invar.stay && scale.stay && offset.stay; + if (outvar.stay) execute(); // stay optimization + } +} + + +// ------------------------------------------------------------ + + +// A Plan is an ordered list of constraints to be executed in sequence +// to resatisfy all currently satisfiable constraints in the face of +// one or more changing inputs. + +internal class Plan +{ + private ArrayList _v; + + public Plan() { _v = new ArrayList(); } + + public void addConstraint(Constraint c) { _v.Add(c); } + + public int size() { return _v.Count; } + + public Constraint constraintAt(int index) + { + return (Constraint)_v[index]; + } + + public void execute() + { + for (int i = 0; i < size(); ++i) + { + Constraint c = (Constraint)constraintAt(i); + c.execute(); + } + } +} + + +// ------------------------------------------------------------ + +// The deltablue planner + +internal class Planner +{ + private int _currentMark = 0; + + // Select a previously unused mark value. + private int newMark() { return ++_currentMark; } + + public Planner() + { + _currentMark = 0; + } + + // Attempt to satisfy the given constraint and, if successful, + // incrementally update the dataflow graph. Details: If satifying + // the constraint is successful, it may override a weaker constraint + // on its output. The algorithm attempts to resatisfy that + // constraint using some other method. This process is repeated + // until either a) it reaches a variable that was not previously + // determined by any constraint or b) it reaches a constraint that + // is too weak to be satisfied using any of its methods. The + // variables of constraints that have been processed are marked with + // a unique mark value so that we know where we've been. This allows + // the algorithm to avoid getting into an infinite loop even if the + // constraint graph has an inadvertent cycle. + // + public void incrementalAdd(Constraint c) + { + int mark = newMark(); + Constraint overridden = c.satisfy(mark); + while (overridden != null) + { + overridden = overridden.satisfy(mark); + } + } + + + // Entry point for retracting a constraint. Remove the given + // constraint and incrementally update the dataflow graph. + // Details: Retracting the given constraint may allow some currently + // unsatisfiable downstream constraint to be satisfied. We therefore collect + // a list of unsatisfied downstream constraints and attempt to + // satisfy each one in turn. This list is traversed by constraint + // strength, strongest first, as a heuristic for avoiding + // unnecessarily adding and then overriding weak constraints. + // Assume: c is satisfied. + // + public void incrementalRemove(Constraint c) + { + Variable outvar = c.output(); + c.markUnsatisfied(); + c.removeFromGraph(); + ArrayList unsatisfied = removePropagateFrom(outvar); + Strength strength = Strength.required; + do + { + for (int i = 0; i < unsatisfied.Count; ++i) + { + Constraint u = (Constraint)unsatisfied[i]; + if (u.strength == strength) + incrementalAdd(u); + } + strength = strength.nextWeaker(); + } while (strength != Strength.weakest); + } + + // Recompute the walkabout strengths and stay flags of all variables + // downstream of the given constraint and recompute the actual + // values of all variables whose stay flag is true. If a cycle is + // detected, remove the given constraint and answer + // false. Otherwise, answer true. + // Details: Cycles are detected when a marked variable is + // encountered downstream of the given constraint. The sender is + // assumed to have marked the inputs of the given constraint with + // the given mark. Thus, encountering a marked node downstream of + // the output constraint means that there is a path from the + // constraint's output to one of its inputs. + // + public Boolean addPropagate(Constraint c, int mark) + { + ArrayList todo = new ArrayList(); + todo.Add(c); + while (!(todo.Count == 0)) + { +#if USE_STACK + Constraint d = (Constraint)todo[todo.Count - 1]; + todo.RemoveAt(todo.Count - 1); +#else + Constraint d= (Constraint)todo[0]; + todo.RemoveAt(0); +#endif + if (d.output().mark == mark) + { + incrementalRemove(c); + return false; + } + d.recalculate(); + addConstraintsConsumingTo(d.output(), todo); + } + return true; + } + + + // The given variable has changed. Propagate new values downstream. + public void propagateFrom(Variable v) + { + ArrayList todo = new ArrayList(); + addConstraintsConsumingTo(v, todo); + while (!(todo.Count == 0)) + { +#if USE_STACK + Constraint c = (Constraint)todo[todo.Count - 1]; + todo.RemoveAt(0); +#else + Constraint c= (Constraint)todo[todo.Count-1]; + todo.RemoveAt(0); +#endif + c.execute(); + addConstraintsConsumingTo(c.output(), todo); + } + } + + // Update the walkabout strengths and stay flags of all variables + // downstream of the given constraint. Answer a collection of + // unsatisfied constraints sorted in order of decreasing strength. + // + public ArrayList removePropagateFrom(Variable outvar) + { + outvar.determinedBy = null; + outvar.walkStrength = Strength.weakest; + outvar.stay = true; + ArrayList unsatisfied = new ArrayList(); + ArrayList todo = new ArrayList(); + todo.Add(outvar); + while (!(todo.Count == 0)) + { +#if USE_STACK + Variable v = (Variable)todo[todo.Count - 1]; + todo.RemoveAt(todo.Count - 1); +#else + Variable v= (Variable)todo[0]; + todo.RemoveAt(0); +#endif + for (int i = 0; i < v.constraints.Count; ++i) + { + Constraint c = (Constraint)v.constraints[i]; + if (!c.isSatisfied()) + unsatisfied.Add(c); + } + Constraint determiningC = v.determinedBy; + for (int i = 0; i < v.constraints.Count; ++i) + { + Constraint nextC = (Constraint)v.constraints[i]; + if (nextC != determiningC && nextC.isSatisfied()) + { + nextC.recalculate(); + todo.Add(nextC.output()); + } + } + } + return unsatisfied; + } + + // Extract a plan for resatisfaction starting from the outputs of + // the given constraints, usually a set of input constraints. + // + public Plan extractPlanFromConstraints(ArrayList constraints) + { + ArrayList sources = new ArrayList(); + for (int i = 0; i < constraints.Count; ++i) + { + Constraint c = (Constraint)constraints[i]; + if (c.isInput() && c.isSatisfied()) + sources.Add(c); + } + return makePlan(sources); + } + + // Extract a plan for resatisfaction starting from the given source + // constraints, usually a set of input constraints. This method + // assumes that stay optimization is desired; the plan will contain + // only constraints whose output variables are not stay. Constraints + // that do no computation, such as stay and edit constraints, are + // not included in the plan. + // Details: The outputs of a constraint are marked when it is added + // to the plan under construction. A constraint may be appended to + // the plan when all its input variables are known. A variable is + // known if either a) the variable is marked (indicating that has + // been computed by a constraint appearing earlier in the plan), b) + // the variable is 'stay' (i.e. it is a constant at plan execution + // time), or c) the variable is not determined by any + // constraint. The last provision is for past states of history + // variables, which are not stay but which are also not computed by + // any constraint. + // Assume: sources are all satisfied. + // + public Plan makePlan(ArrayList sources) + { + int mark = newMark(); + Plan plan = new Plan(); + ArrayList todo = sources; + while (!(todo.Count == 0)) + { +#if USE_STACK + Constraint c = (Constraint)todo[todo.Count - 1]; + todo.RemoveAt(todo.Count - 1); +#else + Constraint c= (Constraint)todo[todo.Count-1]; + todo.RemoveAt(0); +#endif + if (c.output().mark != mark && c.inputsKnown(mark)) + { + // not in plan already and eligible for inclusion + plan.addConstraint(c); + c.output().mark = mark; + addConstraintsConsumingTo(c.output(), todo); + } + } + return plan; + } + + public void addConstraintsConsumingTo(Variable v, ArrayList coll) + { + Constraint determiningC = v.determinedBy; + ArrayList cc = v.constraints; + for (int i = 0; i < cc.Count; ++i) + { + Constraint c = (Constraint)cc[i]; + if (c != determiningC && c.isSatisfied()) + coll.Add(c); + } + } +} + +//------------------------------------------------------------ + +public class deltablue +{ + internal static Planner planner; + internal static int chains, projections; + + public static int Main(String[] args) + { + deltablue d = new deltablue(); + bool result = d.inst_main(args); + return (result ? 100 : -1); + } + + [Benchmark] + public static void Bench() + { + deltablue d = new deltablue(); + int iterations = 200; + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + d.inst_inner(iterations, false); + } + } + } + + public bool inst_main(String[] args) + { + int iterations = 200; // read iterations from arguments, walter 7/97 + if (args.Length > 0) + { + bool parsed = Int32.TryParse(args[0], out iterations); + if (!parsed) + { + Console.WriteLine("Error: expected iteration count, got '{0}'", args[0]); + return false; + } + } + + inst_inner(iterations, true); + + return true; + } + + public void inst_inner(int iterations, bool verbose) + { + chains = 0; // NS 11/11 + projections = 0; // NS 11/11 + if (verbose) + { + Console.WriteLine("deltablue parameters: " + iterations + " iterations"); + } + + DateTime start = DateTime.Now; + for (int i = 0; i < iterations; i++) + { + chainTest(1000); + projectionTest(1000); + } + DateTime end = DateTime.Now; + TimeSpan dur = end - start; + if (verbose) + { + Console.WriteLine("chains : " + chains); //NS + Console.WriteLine("projections : " + projections); //NS + Console.WriteLine("Doing {0} iters of Deltablue takes {1} ms; {2} us/iter.", + iterations, dur.TotalMilliseconds, (1000.0 * dur.TotalMilliseconds) / iterations); + } + } + + // This is the standard DeltaBlue benchmark. A long chain of + // equality constraints is constructed with a stay constraint on + // one end. An edit constraint is then added to the opposite end + // and the time is measured for adding and removing this + // constraint, and extracting and executing a constraint + // satisfaction plan. There are two cases. In case 1, the added + // constraint is stronger than the stay constraint and values must + // propagate down the entire length of the chain. In case 2, the + // added constraint is weaker than the stay constraint so it cannot + // be accomodated. The cost in this case is, of course, very + // low. Typical situations lie somewhere between these two + // extremes. + // + private void chainTest(int n) + { + planner = new Planner(); + + Variable prev = null, first = null, last = null; + + // Build chain of n equality constraints + for (int i = 0; i <= n; i++) + { + String name = "v" + i; + Variable v = new Variable(name); + if (prev != null) + new EqualityConstraint(prev, v, Strength.required); + if (i == 0) first = v; + if (i == n) last = v; + prev = v; + } + + new StayConstraint(last, Strength.strongDefault); + Constraint editC = new EditConstraint(first, Strength.preferred); + ArrayList editV = new ArrayList(); + editV.Add(editC); + Plan plan = planner.extractPlanFromConstraints(editV); + for (int i = 0; i < 100; i++) + { + first.value = i; + plan.execute(); + if (last.value != i) + error("Chain test failed!"); + } + editC.destroyConstraint(); + deltablue.chains++; + } + + + // This test constructs a two sets of variables related to each + // other by a simple linear transformation (scale and offset). The + // time is measured to change a variable on either side of the + // mapping and to change the scale and offset factors. + // + private void projectionTest(int n) + { + planner = new Planner(); + + Variable scale = new Variable("scale", 10); + Variable offset = new Variable("offset", 1000); + Variable src = null, dst = null; + + ArrayList dests = new ArrayList(); + + for (int i = 0; i < n; ++i) + { + src = new Variable("src" + i, i); + dst = new Variable("dst" + i, i); + dests.Add(dst); + new StayConstraint(src, Strength.normal); + new ScaleConstraint(src, scale, offset, dst, Strength.required); + } + + change(src, 17); + if (dst.value != 1170) error("Projection test 1 failed!"); + + change(dst, 1050); + if (src.value != 5) error("Projection test 2 failed!"); + + change(scale, 5); + for (int i = 0; i < n - 1; ++i) + { + if (((Variable)dests[i]).value != i * 5 + 1000) + error("Projection test 3 failed!"); + } + + change(offset, 2000); + for (int i = 0; i < n - 1; ++i) + { + if (((Variable)dests[i]).value != i * 5 + 2000) + error("Projection test 4 failed!"); + } + deltablue.projections++; + } + + private void change(Variable var, int newValue) + { + EditConstraint editC = new EditConstraint(var, Strength.preferred); + ArrayList editV = new ArrayList(); + editV.Add(editC); + Plan plan = planner.extractPlanFromConstraints(editV); + for (int i = 0; i < 10; i++) + { + var.value = newValue; + plan.execute(); + } + editC.destroyConstraint(); + } + + public static void error(String s) + { + throw new Exception(s); + } +} |