Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(448)

Side by Side Diff: pkg/kernel/testcases/input/DeltaBlue.dart

Issue 2825063002: Move kernel baseline tests to front_end. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/front_end/tool/fasta_perf.dart ('k') | pkg/kernel/testcases/input/argument.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2011 Google Inc. All Rights Reserved.
2 // Copyright 1996 John Maloney and Mario Wolczko
3 //
4 // This file is part of GNU Smalltalk.
5 //
6 // GNU Smalltalk is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU General Public License as published by the Free
8 // Software Foundation; either version 2, or (at your option) any later version.
9 //
10 // GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // GNU Smalltalk; see the file COPYING. If not, write to the Free Software
17 // Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 //
19 // Translated first from Smalltalk to JavaScript, and finally to
20 // Dart by Google 2008-2010.
21
22 /**
23 * A Dart implementation of the DeltaBlue constraint-solving
24 * algorithm, as described in:
25 *
26 * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
27 * Bjorn N. Freeman-Benson and John Maloney
28 * January 1990 Communications of the ACM,
29 * also available as University of Washington TR 89-08-06.
30 *
31 * Beware: this benchmark is written in a grotesque style where
32 * the constraint model is built by side-effects from constructors.
33 * I've kept it this way to avoid deviating too much from the original
34 * implementation.
35 */
36
37 main() {
38 new DeltaBlue().run();
39 }
40
41 /// Benchmark class required to report results.
42 class DeltaBlue {
43 void run() {
44 chainTest(100);
45 projectionTest(100);
46 }
47 }
48
49 /**
50 * Strengths are used to measure the relative importance of constraints.
51 * New strengths may be inserted in the strength hierarchy without
52 * disrupting current constraints. Strengths cannot be created outside
53 * this class, so == can be used for value comparison.
54 */
55 class Strength {
56 final int value;
57 final String name;
58
59 const Strength(this.value, this.name);
60
61 Strength nextWeaker() => const <Strength>[
62 STRONG_PREFERRED,
63 PREFERRED,
64 STRONG_DEFAULT,
65 NORMAL,
66 WEAK_DEFAULT,
67 WEAKEST
68 ][value];
69
70 static bool stronger(Strength s1, Strength s2) {
71 return s1.value < s2.value;
72 }
73
74 static bool weaker(Strength s1, Strength s2) {
75 return s1.value > s2.value;
76 }
77
78 static Strength weakest(Strength s1, Strength s2) {
79 return weaker(s1, s2) ? s1 : s2;
80 }
81
82 static Strength strongest(Strength s1, Strength s2) {
83 return stronger(s1, s2) ? s1 : s2;
84 }
85 }
86
87 // Compile time computed constants.
88 const REQUIRED = const Strength(0, "required");
89 const STRONG_PREFERRED = const Strength(1, "strongPreferred");
90 const PREFERRED = const Strength(2, "preferred");
91 const STRONG_DEFAULT = const Strength(3, "strongDefault");
92 const NORMAL = const Strength(4, "normal");
93 const WEAK_DEFAULT = const Strength(5, "weakDefault");
94 const WEAKEST = const Strength(6, "weakest");
95
96 abstract class Constraint {
97 final Strength strength;
98
99 const Constraint(this.strength);
100
101 bool isSatisfied();
102 void markUnsatisfied();
103 void addToGraph();
104 void removeFromGraph();
105 void chooseMethod(int mark);
106 void markInputs(int mark);
107 bool inputsKnown(int mark);
108 Variable output();
109 void execute();
110 void recalculate();
111
112 /// Activate this constraint and attempt to satisfy it.
113 void addConstraint() {
114 addToGraph();
115 planner.incrementalAdd(this);
116 }
117
118 /**
119 * Attempt to find a way to enforce this constraint. If successful,
120 * record the solution, perhaps modifying the current dataflow
121 * graph. Answer the constraint that this constraint overrides, if
122 * there is one, or nil, if there isn't.
123 * Assume: I am not already satisfied.
124 */
125 Constraint satisfy(mark) {
126 chooseMethod(mark);
127 if (!isSatisfied()) {
128 if (strength == REQUIRED) {
129 print("Could not satisfy a required constraint!");
130 }
131 return null;
132 }
133 markInputs(mark);
134 Variable out = output();
135 Constraint overridden = out.determinedBy;
136 if (overridden != null) overridden.markUnsatisfied();
137 out.determinedBy = this;
138 if (!planner.addPropagate(this, mark)) print("Cycle encountered");
139 out.mark = mark;
140 return overridden;
141 }
142
143 void destroyConstraint() {
144 if (isSatisfied()) planner.incrementalRemove(this);
145 removeFromGraph();
146 }
147
148 /**
149 * Normal constraints are not input constraints. An input constraint
150 * is one that depends on external state, such as the mouse, the
151 * keybord, a clock, or some arbitraty piece of imperative code.
152 */
153 bool isInput() => false;
154 }
155
156 /**
157 * Abstract superclass for constraints having a single possible output variable.
158 */
159 abstract class UnaryConstraint extends Constraint {
160 final Variable myOutput;
161 bool satisfied = false;
162
163 UnaryConstraint(this.myOutput, Strength strength) : super(strength) {
164 addConstraint();
165 }
166
167 /// Adds this constraint to the constraint graph
168 void addToGraph() {
169 myOutput.addConstraint(this);
170 satisfied = false;
171 }
172
173 /// Decides if this constraint can be satisfied and records that decision.
174 void chooseMethod(int mark) {
175 satisfied = (myOutput.mark != mark) &&
176 Strength.stronger(strength, myOutput.walkStrength);
177 }
178
179 /// Returns true if this constraint is satisfied in the current solution.
180 bool isSatisfied() => satisfied;
181
182 void markInputs(int mark) {
183 // has no inputs.
184 }
185
186 /// Returns the current output variable.
187 Variable output() => myOutput;
188
189 /**
190 * Calculate the walkabout strength, the stay flag, and, if it is
191 * 'stay', the value for the current output of this constraint. Assume
192 * this constraint is satisfied.
193 */
194 void recalculate() {
195 myOutput.walkStrength = strength;
196 myOutput.stay = !isInput();
197 if (myOutput.stay) execute(); // Stay optimization.
198 }
199
200 /// Records that this constraint is unsatisfied.
201 void markUnsatisfied() {
202 satisfied = false;
203 }
204
205 bool inputsKnown(int mark) => true;
206
207 void removeFromGraph() {
208 if (myOutput != null) myOutput.removeConstraint(this);
209 satisfied = false;
210 }
211 }
212
213 /**
214 * Variables that should, with some level of preference, stay the same.
215 * Planners may exploit the fact that instances, if satisfied, will not
216 * change their output during plan execution. This is called "stay
217 * optimization".
218 */
219 class StayConstraint extends UnaryConstraint {
220 StayConstraint(Variable v, Strength str) : super(v, str);
221
222 void execute() {
223 // Stay constraints do nothing.
224 }
225 }
226
227 /**
228 * A unary input constraint used to mark a variable that the client
229 * wishes to change.
230 */
231 class EditConstraint extends UnaryConstraint {
232 EditConstraint(Variable v, Strength str) : super(v, str);
233
234 /// Edits indicate that a variable is to be changed by imperative code.
235 bool isInput() => true;
236
237 void execute() {
238 // Edit constraints do nothing.
239 }
240 }
241
242 // Directions.
243 const int NONE = 1;
244 const int FORWARD = 2;
245 const int BACKWARD = 0;
246
247 /**
248 * Abstract superclass for constraints having two possible output
249 * variables.
250 */
251 abstract class BinaryConstraint extends Constraint {
252 Variable v1;
253 Variable v2;
254 int direction = NONE;
255
256 BinaryConstraint(this.v1, this.v2, Strength strength) : super(strength) {
257 addConstraint();
258 }
259
260 /**
261 * Decides if this constraint can be satisfied and which way it
262 * should flow based on the relative strength of the variables related,
263 * and record that decision.
264 */
265 void chooseMethod(int mark) {
266 if (v1.mark == mark) {
267 direction =
268 (v2.mark != mark && Strength.stronger(strength, v2.walkStrength))
269 ? FORWARD
270 : NONE;
271 }
272 if (v2.mark == mark) {
273 direction =
274 (v1.mark != mark && Strength.stronger(strength, v1.walkStrength))
275 ? BACKWARD
276 : NONE;
277 }
278 if (Strength.weaker(v1.walkStrength, v2.walkStrength)) {
279 direction =
280 Strength.stronger(strength, v1.walkStrength) ? BACKWARD : NONE;
281 } else {
282 direction =
283 Strength.stronger(strength, v2.walkStrength) ? FORWARD : BACKWARD;
284 }
285 }
286
287 /// Add this constraint to the constraint graph.
288 void addToGraph() {
289 v1.addConstraint(this);
290 v2.addConstraint(this);
291 direction = NONE;
292 }
293
294 /// Answer true if this constraint is satisfied in the current solution.
295 bool isSatisfied() => direction != NONE;
296
297 /// Mark the input variable with the given mark.
298 void markInputs(int mark) {
299 input().mark = mark;
300 }
301
302 /// Returns the current input variable
303 Variable input() => direction == FORWARD ? v1 : v2;
304
305 /// Returns the current output variable.
306 Variable output() => direction == FORWARD ? v2 : v1;
307
308 /**
309 * Calculate the walkabout strength, the stay flag, and, if it is
310 * 'stay', the value for the current output of this
311 * constraint. Assume this constraint is satisfied.
312 */
313 void recalculate() {
314 Variable ihn = input(), out = output();
315 out.walkStrength = Strength.weakest(strength, ihn.walkStrength);
316 out.stay = ihn.stay;
317 if (out.stay) execute();
318 }
319
320 /// Record the fact that this constraint is unsatisfied.
321 void markUnsatisfied() {
322 direction = NONE;
323 }
324
325 bool inputsKnown(int mark) {
326 Variable i = input();
327 return i.mark == mark || i.stay || i.determinedBy == null;
328 }
329
330 void removeFromGraph() {
331 if (v1 != null) v1.removeConstraint(this);
332 if (v2 != null) v2.removeConstraint(this);
333 direction = NONE;
334 }
335 }
336
337 /**
338 * Relates two variables by the linear scaling relationship: "v2 =
339 * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
340 * this relationship but the scale factor and offset are considered
341 * read-only.
342 */
343
344 class ScaleConstraint extends BinaryConstraint {
345 final Variable scale;
346 final Variable offset;
347
348 ScaleConstraint(
349 Variable src, this.scale, this.offset, Variable dest, Strength strength)
350 : super(src, dest, strength);
351
352 /// Adds this constraint to the constraint graph.
353 void addToGraph() {
354 super.addToGraph();
355 scale.addConstraint(this);
356 offset.addConstraint(this);
357 }
358
359 void removeFromGraph() {
360 super.removeFromGraph();
361 if (scale != null) scale.removeConstraint(this);
362 if (offset != null) offset.removeConstraint(this);
363 }
364
365 void markInputs(int mark) {
366 super.markInputs(mark);
367 scale.mark = offset.mark = mark;
368 }
369
370 /// Enforce this constraint. Assume that it is satisfied.
371 void execute() {
372 if (direction == FORWARD) {
373 v2.value = v1.value * scale.value + offset.value;
374 } else {
375 v1.value = (v2.value - offset.value) ~/ scale.value;
376 }
377 }
378
379 /**
380 * Calculate the walkabout strength, the stay flag, and, if it is
381 * 'stay', the value for the current output of this constraint. Assume
382 * this constraint is satisfied.
383 */
384 void recalculate() {
385 Variable ihn = input(), out = output();
386 out.walkStrength = Strength.weakest(strength, ihn.walkStrength);
387 out.stay = ihn.stay && scale.stay && offset.stay;
388 if (out.stay) execute();
389 }
390 }
391
392 /**
393 * Constrains two variables to have the same value.
394 */
395 class EqualityConstraint extends BinaryConstraint {
396 EqualityConstraint(Variable v1, Variable v2, Strength strength)
397 : super(v1, v2, strength);
398
399 /// Enforce this constraint. Assume that it is satisfied.
400 void execute() {
401 output().value = input().value;
402 }
403 }
404
405 /**
406 * A constrained variable. In addition to its value, it maintain the
407 * structure of the constraint graph, the current dataflow graph, and
408 * various parameters of interest to the DeltaBlue incremental
409 * constraint solver.
410 **/
411 class Variable {
412 List<Constraint> constraints = <Constraint>[];
413 Constraint determinedBy;
414 int mark = 0;
415 Strength walkStrength = WEAKEST;
416 bool stay = true;
417 int value;
418 final String name;
419
420 Variable(this.name, this.value);
421
422 /**
423 * Add the given constraint to the set of all constraints that refer
424 * this variable.
425 */
426 void addConstraint(Constraint c) {
427 constraints.add(c);
428 }
429
430 /// Removes all traces of c from this variable.
431 void removeConstraint(Constraint c) {
432 constraints.remove(c);
433 if (determinedBy == c) determinedBy = null;
434 }
435 }
436
437 class Planner {
438 int currentMark = 0;
439
440 /**
441 * Attempt to satisfy the given constraint and, if successful,
442 * incrementally update the dataflow graph. Details: If satifying
443 * the constraint is successful, it may override a weaker constraint
444 * on its output. The algorithm attempts to resatisfy that
445 * constraint using some other method. This process is repeated
446 * until either a) it reaches a variable that was not previously
447 * determined by any constraint or b) it reaches a constraint that
448 * is too weak to be satisfied using any of its methods. The
449 * variables of constraints that have been processed are marked with
450 * a unique mark value so that we know where we've been. This allows
451 * the algorithm to avoid getting into an infinite loop even if the
452 * constraint graph has an inadvertent cycle.
453 */
454 void incrementalAdd(Constraint c) {
455 int mark = newMark();
456 for (Constraint overridden = c.satisfy(mark);
457 overridden != null;
458 overridden = overridden.satisfy(mark));
459 }
460
461 /**
462 * Entry point for retracting a constraint. Remove the given
463 * constraint and incrementally update the dataflow graph.
464 * Details: Retracting the given constraint may allow some currently
465 * unsatisfiable downstream constraint to be satisfied. We therefore collect
466 * a list of unsatisfied downstream constraints and attempt to
467 * satisfy each one in turn. This list is traversed by constraint
468 * strength, strongest first, as a heuristic for avoiding
469 * unnecessarily adding and then overriding weak constraints.
470 * Assume: [c] is satisfied.
471 */
472 void incrementalRemove(Constraint c) {
473 Variable out = c.output();
474 c.markUnsatisfied();
475 c.removeFromGraph();
476 List<Constraint> unsatisfied = removePropagateFrom(out);
477 Strength strength = REQUIRED;
478 do {
479 for (int i = 0; i < unsatisfied.length; i++) {
480 Constraint u = unsatisfied[i];
481 if (u.strength == strength) incrementalAdd(u);
482 }
483 strength = strength.nextWeaker();
484 } while (strength != WEAKEST);
485 }
486
487 /// Select a previously unused mark value.
488 int newMark() => ++currentMark;
489
490 /**
491 * Extract a plan for resatisfaction starting from the given source
492 * constraints, usually a set of input constraints. This method
493 * assumes that stay optimization is desired; the plan will contain
494 * only constraints whose output variables are not stay. Constraints
495 * that do no computation, such as stay and edit constraints, are
496 * not included in the plan.
497 * Details: The outputs of a constraint are marked when it is added
498 * to the plan under construction. A constraint may be appended to
499 * the plan when all its input variables are known. A variable is
500 * known if either a) the variable is marked (indicating that has
501 * been computed by a constraint appearing earlier in the plan), b)
502 * the variable is 'stay' (i.e. it is a constant at plan execution
503 * time), or c) the variable is not determined by any
504 * constraint. The last provision is for past states of history
505 * variables, which are not stay but which are also not computed by
506 * any constraint.
507 * Assume: [sources] are all satisfied.
508 */
509 Plan makePlan(List<Constraint> sources) {
510 int mark = newMark();
511 Plan plan = new Plan();
512 List<Constraint> todo = sources;
513 while (todo.length > 0) {
514 Constraint c = todo.removeLast();
515 if (c.output().mark != mark && c.inputsKnown(mark)) {
516 plan.addConstraint(c);
517 c.output().mark = mark;
518 addConstraintsConsumingTo(c.output(), todo);
519 }
520 }
521 return plan;
522 }
523
524 /**
525 * Extract a plan for resatisfying starting from the output of the
526 * given [constraints], usually a set of input constraints.
527 */
528 Plan extractPlanFromConstraints(List<Constraint> constraints) {
529 List<Constraint> sources = <Constraint>[];
530 for (int i = 0; i < constraints.length; i++) {
531 Constraint c = constraints[i];
532 // if not in plan already and eligible for inclusion.
533 if (c.isInput() && c.isSatisfied()) sources.add(c);
534 }
535 return makePlan(sources);
536 }
537
538 /**
539 * Recompute the walkabout strengths and stay flags of all variables
540 * downstream of the given constraint and recompute the actual
541 * values of all variables whose stay flag is true. If a cycle is
542 * detected, remove the given constraint and answer
543 * false. Otherwise, answer true.
544 * Details: Cycles are detected when a marked variable is
545 * encountered downstream of the given constraint. The sender is
546 * assumed to have marked the inputs of the given constraint with
547 * the given mark. Thus, encountering a marked node downstream of
548 * the output constraint means that there is a path from the
549 * constraint's output to one of its inputs.
550 */
551 bool addPropagate(Constraint c, int mark) {
552 List<Constraint> todo = <Constraint>[c];
553 while (todo.length > 0) {
554 Constraint d = todo.removeLast();
555 if (d.output().mark == mark) {
556 incrementalRemove(c);
557 return false;
558 }
559 d.recalculate();
560 addConstraintsConsumingTo(d.output(), todo);
561 }
562 return true;
563 }
564
565 /**
566 * Update the walkabout strengths and stay flags of all variables
567 * downstream of the given constraint. Answer a collection of
568 * unsatisfied constraints sorted in order of decreasing strength.
569 */
570 List<Constraint> removePropagateFrom(Variable out) {
571 out.determinedBy = null;
572 out.walkStrength = WEAKEST;
573 out.stay = true;
574 List<Constraint> unsatisfied = <Constraint>[];
575 List<Variable> todo = <Variable>[out];
576 while (todo.length > 0) {
577 Variable v = todo.removeLast();
578 for (int i = 0; i < v.constraints.length; i++) {
579 Constraint c = v.constraints[i];
580 if (!c.isSatisfied()) unsatisfied.add(c);
581 }
582 Constraint determining = v.determinedBy;
583 for (int i = 0; i < v.constraints.length; i++) {
584 Constraint next = v.constraints[i];
585 if (next != determining && next.isSatisfied()) {
586 next.recalculate();
587 todo.add(next.output());
588 }
589 }
590 }
591 return unsatisfied;
592 }
593
594 void addConstraintsConsumingTo(Variable v, List<Constraint> coll) {
595 Constraint determining = v.determinedBy;
596 for (int i = 0; i < v.constraints.length; i++) {
597 Constraint c = v.constraints[i];
598 if (c != determining && c.isSatisfied()) coll.add(c);
599 }
600 }
601 }
602
603 /**
604 * A Plan is an ordered list of constraints to be executed in sequence
605 * to resatisfy all currently satisfiable constraints in the face of
606 * one or more changing inputs.
607 */
608 class Plan {
609 List<Constraint> list = <Constraint>[];
610
611 void addConstraint(Constraint c) {
612 list.add(c);
613 }
614
615 int size() => list.length;
616
617 void execute() {
618 for (int i = 0; i < list.length; i++) {
619 list[i].execute();
620 }
621 }
622 }
623
624 /**
625 * This is the standard DeltaBlue benchmark. A long chain of equality
626 * constraints is constructed with a stay constraint on one end. An
627 * edit constraint is then added to the opposite end and the time is
628 * measured for adding and removing this constraint, and extracting
629 * and executing a constraint satisfaction plan. There are two cases.
630 * In case 1, the added constraint is stronger than the stay
631 * constraint and values must propagate down the entire length of the
632 * chain. In case 2, the added constraint is weaker than the stay
633 * constraint so it cannot be accomodated. The cost in this case is,
634 * of course, very low. Typical situations lie somewhere between these
635 * two extremes.
636 */
637 void chainTest(int n) {
638 planner = new Planner();
639 Variable prev = null, first = null, last = null;
640 // Build chain of n equality constraints.
641 for (int i = 0; i <= n; i++) {
642 Variable v = new Variable("v$i", 0);
643 if (prev != null) new EqualityConstraint(prev, v, REQUIRED);
644 if (i == 0) first = v;
645 if (i == n) last = v;
646 prev = v;
647 }
648 new StayConstraint(last, STRONG_DEFAULT);
649 EditConstraint edit = new EditConstraint(first, PREFERRED);
650 Plan plan = planner.extractPlanFromConstraints(<Constraint>[edit]);
651 for (int i = 0; i < 100; i++) {
652 first.value = i;
653 plan.execute();
654 if (last.value != i) {
655 print("Chain test failed:");
656 print("Expected last value to be $i but it was ${last.value}.");
657 }
658 }
659 }
660
661 /**
662 * This test constructs a two sets of variables related to each
663 * other by a simple linear transformation (scale and offset). The
664 * time is measured to change a variable on either side of the
665 * mapping and to change the scale and offset factors.
666 */
667 void projectionTest(int n) {
668 planner = new Planner();
669 Variable scale = new Variable("scale", 10);
670 Variable offset = new Variable("offset", 1000);
671 Variable src = null, dst = null;
672
673 List<Variable> dests = <Variable>[];
674 for (int i = 0; i < n; i++) {
675 src = new Variable("src", i);
676 dst = new Variable("dst", i);
677 dests.add(dst);
678 new StayConstraint(src, NORMAL);
679 new ScaleConstraint(src, scale, offset, dst, REQUIRED);
680 }
681 change(src, 17);
682 if (dst.value != 1170) print("Projection 1 failed");
683 change(dst, 1050);
684 if (src.value != 5) print("Projection 2 failed");
685 change(scale, 5);
686 for (int i = 0; i < n - 1; i++) {
687 if (dests[i].value != i * 5 + 1000) print("Projection 3 failed");
688 }
689 change(offset, 2000);
690 for (int i = 0; i < n - 1; i++) {
691 if (dests[i].value != i * 5 + 2000) print("Projection 4 failed");
692 }
693 }
694
695 void change(Variable v, int newValue) {
696 EditConstraint edit = new EditConstraint(v, PREFERRED);
697 Plan plan = planner.extractPlanFromConstraints(<EditConstraint>[edit]);
698 for (int i = 0; i < 10; i++) {
699 v.value = newValue;
700 plan.execute();
701 }
702 edit.destroyConstraint();
703 }
704
705 Planner planner;
OLDNEW
« no previous file with comments | « pkg/front_end/tool/fasta_perf.dart ('k') | pkg/kernel/testcases/input/argument.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698