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

Side by Side Diff: lib/src/line_splitting/solve_state.dart

Issue 1418483008: Optimize splitting lines with many rules. (Closed) Base URL: https://github.com/dart-lang/dart_style.git@master
Patch Set: Created 5 years, 1 month 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 | « lib/src/line_splitting/rule_set.dart ('k') | lib/src/rule/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
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dart_style.src.line_splitting.solve_state; 5 library dart_style.src.line_splitting.solve_state;
6 6
7 import '../debug.dart' as debug; 7 import '../debug.dart' as debug;
8 import '../rule/rule.dart'; 8 import '../rule/rule.dart';
9 import 'line_splitter.dart'; 9 import 'line_splitter.dart';
10 import 'rule_set.dart'; 10 import 'rule_set.dart';
11 11
12 /// A possibly incomplete solution in the line splitting search space. 12 /// A possibly incomplete solution in the line splitting search space.
13 /// 13 ///
14 /// A single [SolveState] binds some subset of the rules to values while 14 /// A single [SolveState] binds some subset of the rules to values while
15 /// leaving the rest unbound. If every rule is bound, the solve state describes 15 /// leaving the rest unbound. If every rule is bound, the solve state describes
16 /// a complete solution to the line splitting problem. Even if rules are 16 /// a complete solution to the line splitting problem. Even if rules are
17 /// unbound, a state can also usually be used as a solution by treating all 17 /// unbound, a state can also usually be used as a solution by treating all
18 /// unbound rules as unsplit. (The usually is because a state that constrains 18 /// unbound rules as unsplit. (The usually is because a state that constrains
19 /// an unbound rule to split can't be used with that rule unsplit.) 19 /// an unbound rule to split can't be used with that rule unsplit.)
20 /// 20 ///
21 /// From a given solve state, we can explore the search tree to more refined 21 /// From a given solve state, we can explore the search tree to more refined
22 /// solve states by producing new ones that add more bound rules to the current 22 /// solve states by producing new ones that add more bound rules to the current
23 /// state. 23 /// state.
24 class SolveState { 24 class SolveState {
25 final LineSplitter _splitter; 25 final LineSplitter _splitter;
26 final RuleSet _ruleValues; 26 final RuleSet _ruleValues;
27 27
28 /// The set of [Rule]s that are bound by [_ruleValues].
29 ///
30 /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
31 Set<Rule> _boundRules;
32
33 /// The set of [Rule]s that are not bound by [_ruleValues].
34 ///
35 /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
36 Set<Rule> _unboundRules;
37
28 /// The unbound rules in this state that can be bound to produce new more 38 /// The unbound rules in this state that can be bound to produce new more
29 /// refined states. 39 /// refined states.
30 /// 40 ///
31 /// Keeping this set small is the key to make the entire line splitter 41 /// Keeping this set small is the key to make the entire line splitter
32 /// perform well. If we consider too many rules at each state, our 42 /// perform well. If we consider too many rules at each state, our
33 /// exploration of the solution space is too branchy and we waste time on 43 /// exploration of the solution space is too branchy and we waste time on
34 /// dead end solutions. 44 /// dead end solutions.
35 /// 45 ///
36 /// Here is the key insight. The line splitter treats any unbound rule as 46 /// Here is the key insight. The line splitter treats any unbound rule as
37 /// being unsplit. This means refining a solution always means taking a rule 47 /// being unsplit. This means refining a solution always means taking a rule
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 } 252 }
243 253
244 _ensureConstraints(); 254 _ensureConstraints();
245 other._ensureConstraints(); 255 other._ensureConstraints();
246 if (_constraints.length != other._constraints.length) return false; 256 if (_constraints.length != other._constraints.length) return false;
247 257
248 for (var rule in _constraints.keys) { 258 for (var rule in _constraints.keys) {
249 if (_constraints[rule] != other._constraints[rule]) return false; 259 if (_constraints[rule] != other._constraints[rule]) return false;
250 } 260 }
251 261
262 _ensureUnboundConstraints();
263 other._ensureUnboundConstraints();
252 if (_unboundConstraints.length != other._unboundConstraints.length) { 264 if (_unboundConstraints.length != other._unboundConstraints.length) {
253 return false; 265 return false;
254 } 266 }
255 267
256 for (var rule in _unboundConstraints.keys) { 268 for (var rule in _unboundConstraints.keys) {
257 var disallowed = _unboundConstraints[rule]; 269 var disallowed = _unboundConstraints[rule];
258 var otherDisallowed = other._unboundConstraints[rule]; 270 var otherDisallowed = other._unboundConstraints[rule];
259 271
260 if (disallowed.length != otherDisallowed.length) return false; 272 if (disallowed.length != otherDisallowed.length) return false;
261 for (var value in disallowed) { 273 for (var value in disallowed) {
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 /// Adds [rule] and all of the rules it constrains to the set of [_liveRules]. 432 /// Adds [rule] and all of the rules it constrains to the set of [_liveRules].
421 /// 433 ///
422 /// Only does this if [rule] is a valid soft rule. Returns `true` if any new 434 /// Only does this if [rule] is a valid soft rule. Returns `true` if any new
423 /// live rules were added. 435 /// live rules were added.
424 bool _addLiveRules(Rule rule) { 436 bool _addLiveRules(Rule rule) {
425 if (rule == null) return false; 437 if (rule == null) return false;
426 if (rule is HardSplitRule) return false; 438 if (rule is HardSplitRule) return false;
427 439
428 var added = false; 440 var added = false;
429 for (var constrained in rule.allConstrainedRules) { 441 for (var constrained in rule.allConstrainedRules) {
430 // The rule may constrain some other rule that was already hardened and
431 // discarded. In that case, we can ignore it.
432 if (constrained.index == null) continue;
433
434 if (_ruleValues.contains(constrained)) continue; 442 if (_ruleValues.contains(constrained)) continue;
435 443
436 _liveRules.add(constrained); 444 _liveRules.add(constrained);
437 added = true; 445 added = true;
438 } 446 }
439 447
440 return added; 448 return added;
441 } 449 }
442 450
443 /// Lazily initializes the [_boundInUnboundLines], which is needed to compare 451 /// Lazily initializes the [_boundInUnboundLines], which is needed to compare
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 } 483 }
476 484
477 /// Lazily initializes the [_constraints], which is needed to compare two 485 /// Lazily initializes the [_constraints], which is needed to compare two
478 /// states for overlap. 486 /// states for overlap.
479 /// 487 ///
480 /// We do this lazily because the calculation is a bit slow, and is only 488 /// We do this lazily because the calculation is a bit slow, and is only
481 /// needed when we have two states with the same score. 489 /// needed when we have two states with the same score.
482 void _ensureConstraints() { 490 void _ensureConstraints() {
483 if (_constraints != null) return; 491 if (_constraints != null) return;
484 492
485 var unboundRules = []; 493 _unboundRules = new Set();
486 var boundRules = []; 494 _boundRules = new Set();
487 495
488 for (var rule in _splitter.rules) { 496 for (var rule in _splitter.rules) {
489 if (_ruleValues.contains(rule)) { 497 if (_ruleValues.contains(rule)) {
490 boundRules.add(rule); 498 _boundRules.add(rule);
491 } else { 499 } else {
492 unboundRules.add(rule); 500 _unboundRules.add(rule);
493 } 501 }
494 } 502 }
495 503
496 _constraints = {}; 504 _constraints = {};
kevmoo 2015/11/09 23:40:46 QQ: does order matter here? Would using HashMap fr
Bob Nystrom 2015/11/10 20:08:24 Nope. No measurable improvement.
497 505
498 for (var unbound in unboundRules) { 506 for (var bound in _boundRules) {
499 for (var bound in boundRules) { 507 for (var unbound in bound.constrainedRules) {
508 if (!_unboundRules.contains(unbound)) continue;
509
500 var value = _ruleValues.getValue(bound); 510 var value = _ruleValues.getValue(bound);
501 var constraint = bound.constrain(value, unbound); 511 var constraint = bound.constrain(value, unbound);
502 if (constraint != null) { 512 if (constraint != null) {
503 _constraints[unbound] = constraint; 513 _constraints[unbound] = constraint;
504 } 514 }
505 } 515 }
506 } 516 }
517 }
518
519 /// Lazily initializes the [_unboundConstraints], which is needed to compare
520 /// two states for overlap.
521 ///
522 /// We do this lazily because the calculation is a bit slow, and is only
523 /// needed when we have two states with the same score.
524 void _ensureUnboundConstraints() {
525 if (_unboundConstraints != null) return;
526
527 // _ensureConstraints should be called first which initializes these.
528 assert(_boundRules != null);
529 assert(_unboundRules != null);
507 530
508 _unboundConstraints = {}; 531 _unboundConstraints = {};
kevmoo 2015/11/09 23:40:46 ditto RE HashMap
Bob Nystrom 2015/11/10 20:08:24 Acknowledged.
509 532
510 for (var unbound in unboundRules) { 533 for (var unbound in _unboundRules) {
511 var disallowedValues; 534 var disallowedValues;
512 535
513 for (var value = 0; value < unbound.numValues; value++) { 536 for (var bound in unbound.constrainedRules) {
514 for (var j = 0; j < boundRules.length; j++) { 537 if (!_boundRules.contains(bound)) continue;
515 var bound = boundRules[j]; 538
539 var boundValue = _ruleValues.getValue(bound);
540
541 for (var value = 0; value < unbound.numValues; value++) {
516 var constraint = unbound.constrain(value, bound); 542 var constraint = unbound.constrain(value, bound);
517 543
518 // If the unbound rule doesn't place any constraint on this bound 544 // If the unbound rule doesn't place any constraint on this bound
519 // rule, we're fine. 545 // rule, we're fine.
520 if (constraint == null) continue; 546 if (constraint == null) continue;
521 547
522 // If the bound rule's value already meets the constraint it applies, 548 // If the bound rule's value already meets the constraint it applies,
523 // we don't need to track it. This way, two states that have the 549 // we don't need to track it. This way, two states that have the
524 // same bound value, one of which has a satisfied constraint, are 550 // same bound value, one of which has a satisfied constraint, are
525 // still allowed to overlap. 551 // still allowed to overlap.
526 var boundValue = _ruleValues.getValue(bound);
527 if (constraint == boundValue) continue; 552 if (constraint == boundValue) continue;
528 if (constraint == -1 && boundValue != 0) continue; 553 if (constraint == -1 && boundValue != 0) continue;
529 554
530 if (disallowedValues == null) { 555 if (disallowedValues == null) {
531 disallowedValues = new Set(); 556 disallowedValues = new Set();
532 _unboundConstraints[unbound] = disallowedValues; 557 _unboundConstraints[unbound] = disallowedValues;
533 } 558 }
534 559
535 disallowedValues.add(value); 560 disallowedValues.add(value);
536 } 561 }
(...skipping 24 matching lines...) Expand all
561 586
562 buffer.write(" \$${splits.cost}"); 587 buffer.write(" \$${splits.cost}");
563 588
564 if (overflowChars > 0) buffer.write(" (${overflowChars} over)"); 589 if (overflowChars > 0) buffer.write(" (${overflowChars} over)");
565 if (!_isComplete) buffer.write(" (incomplete)"); 590 if (!_isComplete) buffer.write(" (incomplete)");
566 if (splits == null) buffer.write(" invalid"); 591 if (splits == null) buffer.write(" invalid");
567 592
568 return buffer.toString(); 593 return buffer.toString();
569 } 594 }
570 } 595 }
OLDNEW
« no previous file with comments | « lib/src/line_splitting/rule_set.dart ('k') | lib/src/rule/argument.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698