1
1
mirror of https://github.com/github/semantic.git synced 2024-12-27 00:44:57 +03:00

µ-optimizations for SES.

1. Avoid redundant computations of cost on the fast path (equal
elements at this vertex).

2. Don’t call `min`; inline the branches instead.

3. Don’t call `costOfStream`; inline the costs instead.
This commit is contained in:
Rob Rix 2015-11-12 16:09:53 -05:00
parent 5253de2ef5
commit dac24bbf24

View File

@ -34,18 +34,38 @@ public func SES<Leaf, Annotation, C: CollectionType>(a: C, _ b: C, cost: Free<Le
if let diagonal = diagonal, right = right, down = down { if let diagonal = diagonal, right = right, down = down {
let here = recur(a[i], b[j]) let here = recur(a[i], b[j])
// If the diff at this vertex is zero-cost, were not going to find a cheaper one either rightwards or downwards. We can therefore short-circuit selecting the best outgoing edge and save ourselves evaluating the entire row rightwards and the entire column downwards from this point. if let diagonalDiff = here {
// let hereCost = cost(diagonalDiff)
// Thus, in the best case (two equal sequences), we now complete in O(n + m). However, this optimization only applies to equalities at the beginning of the edit graph; once inequalities are encountered, the remainder of the diff is effectively O(nm). let diagonalCost = hereCost + (diagonal.value.first?.1 ?? 0)
if let here = here where cost(here) == 0 { return cons(here, rest: diagonal) } // If the diff at this vertex is zero-cost, were not going to find a cheaper one either rightwards or downwards. We can therefore short-circuit selecting the best outgoing edge and save ourselves evaluating the entire row rightwards and the entire column downwards from this point.
let right = (right, Diff.Delete(a[i]), costOfStream(right)) //
let down = (down, Diff.Insert(b[j]), costOfStream(down)) // Thus, in the best case (two equal sequences), we now complete in O(n + m). However, this optimization only applies to equalities at the beginning of the edit graph; once inequalities are encountered, the remainder of the diff is effectively O(nm).
let diagonal = here.map { (diagonal, $0, costOfStream(diagonal)) } guard hereCost != 0 else { return .Cons((diagonalDiff, diagonalCost), diagonal) }
// nominate the best edge to continue along, not considering diagonal if `recur` returned `nil`.
let (best, diff, _) = diagonal let rightDiff = Diff.Delete(a[i])
.map { min($0, right, down) { $0.2 < $1.2 } } let rightCost = cost(rightDiff) + (right.value.first?.1 ?? 0)
?? min(right, down) { $0.2 < $1.2 } let downDiff = Diff.Insert(b[j])
return cons(diff, rest: best) let downCost = cost(downDiff) + (down.value.first?.1 ?? 0)
if rightCost < downCost && rightCost < diagonalCost {
return .Cons((rightDiff, rightCost), right)
} else if downCost < diagonalCost {
return .Cons((downDiff, downCost), down)
} else {
return .Cons((diagonalDiff, diagonalCost), diagonal)
}
} else {
let rightDiff = Diff.Delete(a[i])
let rightCost = cost(rightDiff) + (right.value.first?.1 ?? 0)
let downDiff = Diff.Insert(b[j])
let downCost = cost(downDiff) + (down.value.first?.1 ?? 0)
if rightCost < downCost {
return .Cons((rightDiff, rightCost), right)
} else {
return .Cons((downDiff, downCost), down)
}
}
} }
// right extent of the edit graph; can only move down // right extent of the edit graph; can only move down