-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Abstract the DependenciesList and ConstraintsList into separate classes.
Use persistent data-structures on the back-end of these classes to reduce the time spent on copy-on-write operations. After this change the performance decreased on smaller tests, increased drastically on bigger tests (up to 50% improvement on the largest test) but increased the memory consumption by a negligible amount.
- Loading branch information
Showing
6 changed files
with
454 additions
and
146 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
var mori = Npm.require('mori'); | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// ConstraintsList | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// A persistent data-structure that keeps references to Constraint objects | ||
// arranged by the "name" field of Constraint, exact field and version. | ||
ConstraintSolver.ConstraintsList = function (prev) { | ||
var self = this; | ||
|
||
if (prev) { | ||
self.byName = prev.byName; | ||
self.length = prev.length; | ||
} else { | ||
self.byName = mori.hash_map(); | ||
self.length = 0; | ||
} | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.prototype.contains = function (c) { | ||
var self = this; | ||
if (! mori.has_key(self.byName, c.name)) | ||
return false; | ||
|
||
var bn = mori.get(self.byName, c.name); | ||
var constraints = mori.get(bn, c.exact ? "exact" : "inexact"); | ||
return mori.has_key(constraints, c.version); | ||
}; | ||
|
||
// returns a new version containing passed constraint | ||
ConstraintSolver.ConstraintsList.prototype.push = function (c) { | ||
var self = this; | ||
|
||
if (self.contains(c)) { | ||
return self; | ||
} | ||
|
||
var newList = new ConstraintSolver.ConstraintsList(self); | ||
|
||
// create a record or update the lookup table | ||
if (! mori.has_key(self.byName, c.name)) { | ||
var exactMap = mori.hash_map(); | ||
var inexactMap = mori.hash_map(); | ||
|
||
if (c.exact) { | ||
exactMap = mori.assoc(exactMap, c.version, c); | ||
} else { | ||
inexactMap = mori.assoc(inexactMap, c.version, c); | ||
} | ||
|
||
var bn = mori.hash_map("exact", exactMap, "inexact", inexactMap); | ||
newList.byName = mori.assoc(newList.byName, c.name, bn); | ||
} else { | ||
var exactStr = c.exact ? "exact" : "inexact"; | ||
|
||
var bn = mori.get(newList.byName, c.name); | ||
var constraints = mori.get(bn, exactStr); | ||
constraints = mori.assoc(constraints, c.version, c); | ||
bn = mori.assoc(bn, exactStr, constraints); | ||
newList.byName = mori.assoc(newList.byName, c.name, bn); | ||
} | ||
|
||
newList.length++; | ||
|
||
return newList; | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.prototype.forPackage = function (name) { | ||
var self = this; | ||
return mori.get(self.byName, name); | ||
}; | ||
|
||
// doesn't break on the false return value | ||
ConstraintSolver.ConstraintsList.prototype.each = function (iter) { | ||
var self = this; | ||
mori.each(self.byName, function (coll) { | ||
mori.each(mori.last(coll), function (exactInexactColl) { | ||
mori.each(mori.last(exactInexactColl), function (c) { | ||
iter(mori.last(c)); | ||
}); | ||
}); | ||
}); | ||
}; | ||
|
||
// doesn't break on the false return value | ||
ConstraintSolver.ConstraintsList.prototype.eachExact = function (iter) { | ||
var self = this; | ||
mori.each(self.byName, function (coll) { | ||
mori.each(mori.get(coll, "exact"), function (c) { | ||
iter(mori.last(c)); | ||
}); | ||
}); | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.prototype.union = function (anotherList) { | ||
var self = this; | ||
var newList, oldList; | ||
|
||
if (self.length <= anotherList.length) { | ||
newList = anotherList; | ||
oldList = self; | ||
} else { | ||
newList = self; | ||
oldList = anotherList; | ||
} | ||
|
||
oldList.each(function (c) { | ||
newList = newList.push(c); | ||
}); | ||
|
||
return newList; | ||
}; | ||
|
||
// Checks if the passed unit version violates any of the constraints | ||
ConstraintSolver.ConstraintsList.prototype.violated = function (uv) { | ||
var self = this; | ||
var forPackage = mori.get(self.byName, uv.name); | ||
var exact = mori.get(forPackage, "exact"); | ||
var inexact = mori.get(forPackage, "inexact"); | ||
|
||
if (! mori.every(function (c) { return mori.last(c).isSatisfied(uv); }, exact)) | ||
return true; | ||
|
||
return ! mori.every(function (c) { return mori.last(c).isSatisfied(uv); }, inexact); | ||
}; | ||
|
||
// a weird method that returns a list of exact constraints those correspond to | ||
// the dependencies in the passed list | ||
ConstraintSolver.ConstraintsList.prototype.exactDependenciesIntersection = | ||
function (deps) { | ||
var self = this; | ||
var newList = new ConstraintSolver.ConstraintsList(); | ||
|
||
self.eachExact(function (c) { | ||
if (deps.contains(c.name)) | ||
newList = newList.push(c); | ||
}); | ||
|
||
return newList; | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.prototype.toString = function (simple) { | ||
var self = this; | ||
var str = ""; | ||
|
||
var strs = []; | ||
|
||
self.each(function (c) { | ||
strs.push(c.toString()); | ||
}); | ||
|
||
strs.sort(); | ||
|
||
_.each(strs, function (c) { | ||
if (str !== "") { | ||
str += simple ? " " : ", "; | ||
} | ||
str += c; | ||
}); | ||
|
||
return simple ? str : "<constraints list: " + str + ">"; | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.prototype.toArray = function () { | ||
var self = this; | ||
var arr = []; | ||
self.each(function (c) { | ||
arr.push(c); | ||
}); | ||
|
||
return arr; | ||
}; | ||
|
||
ConstraintSolver.ConstraintsList.fromArray = function (arr) { | ||
var list = new ConstraintSolver.ConstraintsList(); | ||
_.each(arr, function (c) { | ||
list = list.push(c); | ||
}); | ||
|
||
return list; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
var mori = Npm.require('mori'); | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// DependenciesList | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// A persistent data-structure that wrapps persistent dictionary | ||
|
||
ConstraintSolver.DependenciesList = function (prev) { | ||
var self = this; | ||
|
||
if (prev) { | ||
self._mapping = prev._mapping; | ||
self._prioritized = prev._prioritized; | ||
} else { | ||
self._mapping = mori.hash_map(); | ||
self._prioritized = mori.list(); | ||
} | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.contains = function (d) { | ||
var self = this; | ||
return mori.has_key(self._mapping, d); | ||
}; | ||
|
||
// returns a new version containing passed dependency | ||
ConstraintSolver.DependenciesList.prototype.push = function (d) { | ||
var self = this; | ||
|
||
if (self.contains(d)) { | ||
return self; | ||
} | ||
|
||
var newList = new ConstraintSolver.DependenciesList(self); | ||
newList._mapping = mori.assoc(self._mapping, d, d); | ||
return newList; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.remove = function (d) { | ||
var self = this; | ||
var newList = new ConstraintSolver.DependenciesList(self); | ||
newList._mapping = mori.dissoc(self._mapping, d); | ||
|
||
if (mori.peek(newList._prioritized) === d) | ||
newList._prioritized = mori.pop(newList._prioritized); | ||
|
||
return newList; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.peek = function () { | ||
var self = this; | ||
var prioritized = mori.peek(self._prioritized); | ||
|
||
if (prioritized) | ||
return prioritized; | ||
|
||
return mori.last(mori.first(self._mapping)); | ||
}; | ||
|
||
// a weird method that returns a list of exact constraints those correspond to | ||
// the dependencies in this list | ||
ConstraintSolver.DependenciesList.prototype.exactConstraintsIntersection = | ||
function (constraintsList) { | ||
var self = this; | ||
var exactConstraints = new ConstraintSolver.ConstraintsList(); | ||
|
||
self.each(function (d) { | ||
var c = mori.last( | ||
// pick an exact constraint for this dependency if such exists | ||
mori.last(mori.get(mori.get(constraintsList.byName, d), "exact"))); | ||
|
||
if (c) | ||
exactConstraints = exactConstraints.push(c); | ||
}); | ||
|
||
return exactConstraints; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.union = function (anotherList) { | ||
var self = this; | ||
var newList = new ConstraintSolver.DependenciesList(self); | ||
newList._mapping = mori.union(newList._mapping, anotherList._mapping); | ||
|
||
return newList; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.isEmpty = function () { | ||
var self = this; | ||
return mori.is_empty(self._mapping); | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.each = function (iter) { | ||
var self = this; | ||
mori.each(self._mapping, function (d) { | ||
iter(mori.last(d)); | ||
}); | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.toString = function (simple) { | ||
var self = this; | ||
var str = ""; | ||
|
||
var strs = []; | ||
self.each(function (d) { | ||
strs.push(d); | ||
}); | ||
|
||
strs.sort(); | ||
_.each(strs, function (d) { | ||
if (str !== "") { | ||
str += simple ? " " : ", "; | ||
} | ||
str += d; | ||
}); | ||
|
||
return simple ? str : "<dependencies list: " + str + ">"; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.prototype.toArray = function () { | ||
var self = this; | ||
var arr = []; | ||
self.each(function (d) { | ||
arr.push(d); | ||
}); | ||
|
||
return arr; | ||
}; | ||
|
||
ConstraintSolver.DependenciesList.fromArray = function (arr, prioritized) { | ||
var list = new ConstraintSolver.DependenciesList(); | ||
var args = []; | ||
_.each(arr, function (d) { | ||
args.push(d); | ||
args.push(d); | ||
}); | ||
|
||
list._mapping = mori.hash_map.apply(mori, args); | ||
|
||
// the whole list should also be added as prioritized | ||
if (prioritized) { | ||
_.each(arr, function (d) { | ||
list._prioritized = mori.conj(list._prioritized, d); | ||
}); | ||
} | ||
|
||
return list; | ||
}; | ||
|
Oops, something went wrong.