Skip to content

Commit

Permalink
Abstract the DependenciesList and ConstraintsList into separate classes.
Browse files Browse the repository at this point in the history
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
Slava committed Apr 22, 2014
1 parent b73d670 commit a70065c
Show file tree
Hide file tree
Showing 6 changed files with 454 additions and 146 deletions.
3 changes: 3 additions & 0 deletions packages/constraint-solver/.npm/package/npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 20 additions & 18 deletions packages/constraint-solver/constraint-solver.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,27 +216,29 @@ ConstraintSolver.PackagesResolver.prototype._getResolverOptions =
};

resolverOptions.estimateCostFunction = function (state, options) {
var totalCost = 0;
var rootDeps = options.rootDependencies;
return _.chain(state.dependencies)
.reduce(function (sum, dep) {
if (!self.resolver.unitsVersions[dep])
console.log("FAIL, no info about: ", dep) // xcxc
var uv = _.find(_.clone(self.resolver.unitsVersions[dep]).reverse(),
function (uv) { return unitVersionDoesntValidateConstraints(uv, state.constraints); });

if (! uv) {
//console.log("FFFAIIIL", dep, state.constraints.map(function (x) { return x.toString() })) // xcxc
return Infinity;
}

if (! _.contains(rootDeps, dep))
return sum;
state.dependencies.each(function (dep) {
if (!self.resolver.unitsVersions[dep])
console.log("FAIL, no info about: ", dep) // xcxc
var uv = _.find(_.clone(self.resolver.unitsVersions[dep]).reverse(),
function (uv) { return !state.constraints.violated(uv); });

if (! uv) {
totalCost = Infinity;
return;
}

var cost = semverToNum(self.resolver._latestVersion[uv.name]) -
semverToNum(uv.version);
if (! _.contains(rootDeps, dep))
return;

return cost + sum;
}, 0).value();
var cost = semverToNum(self.resolver._latestVersion[uv.name]) -
semverToNum(uv.version);

totalCost += cost;
});

return totalCost;
};

break;
Expand Down
182 changes: 182 additions & 0 deletions packages/constraint-solver/constraints-list.js
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;
};

147 changes: 147 additions & 0 deletions packages/constraint-solver/dependencies-list.js
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;
};

0 comments on commit a70065c

Please sign in to comment.