Skip to content

Commit

Permalink
Merge pull request #92 from paldepind/sort-performance
Browse files Browse the repository at this point in the history
Improve performance of sortBy
  • Loading branch information
davidchambers authored Apr 29, 2018
2 parents d148eb0 + 9c5744e commit 90baa0e
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 13 deletions.
14 changes: 13 additions & 1 deletion bench/old-vs-new.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use strict';

var L = require('list/fantasy-land');
var benchmark = require('sanctuary-benchmark');

var oldZ = require('sanctuary-type-classes');
var newZ = require('..');


var Identity = require('../test/Identity');
var shuffle = require('../test/shuffle');


// inc :: Number -> Number
function inc(x) {
Expand All @@ -18,13 +20,23 @@ function prep(specs) {
return newZ.map(function(f) { return [{}, f]; }, specs);
}

var shuffledArray = (function() {
var xs = [];
for (var x = 0; x < 5000; x += 1) xs.push(x);
shuffle(xs);
return xs;
}());
var shuffledList = L.fromArray(shuffledArray);

module.exports = benchmark(oldZ, newZ, {leftHeader: 'old', rightHeader: 'new'}, prep({
'functions.of.Array': function(Z) { Z.of(Array, 42); },
'functions.of.Identity': function(Z) { Z.of(Identity, 42); },
'methods.equals.Identity': function(Z) { Z.equals(Identity(0), Identity(0)); },
'methods.equals.Object': function(Z) { Z.equals({x: 0, y: 0}, {y: 0, x: 0}); },
'methods.map.Array': function(Z) { Z.map(inc, [1, 2, 3]); },
'methods.map.Identity': function(Z) { Z.map(inc, Identity(1)); },
'methods.sort.Array': function(Z) { Z.sort(shuffledArray); },
'methods.sort.List': function(Z) { Z.sort(shuffledList); },
'methods.toString.Identity': function(Z) { Z.toString(Identity(0)); },
'methods.toString.Object': function(Z) { Z.toString({x: 0, y: 0}); },
'methods.toString.String': function(Z) { Z.toString('hello'); },
Expand Down
36 changes: 24 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,9 @@

// Array$prototype$reduce :: Array a ~> ((b, a) -> b, b) -> b
function Array$prototype$reduce(f, initial) {
return this.reduce(function(acc, x) { return f(acc, x); }, initial);
var acc = initial;
for (var idx = 0; idx < this.length; idx += 1) acc = f(acc, this[idx]);
return acc;
}

// Array$prototype$traverse :: Applicative f => Array a ~> (TypeRep f, a -> f b) -> f (Array b)
Expand Down Expand Up @@ -2150,21 +2152,31 @@
//. Cons('red', Cons('blue', Cons('green', Nil)))
//. ```
function sortBy(f, foldable) {
var rs = reduce(function(xs, x) {
var fx = f(x);
var lower = 0;
var upper = xs.length;
while (lower < upper) {
var idx = Math.floor((lower + upper) / 2);
if (lte(xs[idx].fx, fx)) lower = idx + 1; else upper = idx;
}
xs.splice(lower, 0, {x: x, fx: fx});
return xs;
var rs = reduce(function(rs, x) {
rs.push({idx: rs.length, x: x, fx: f(x)});
return rs;
}, [], foldable);

var lte_ = (function(r) {
switch (typeof (r && r.fx)) {
case 'number': return function(x, y) { return x <= y || x !== x; };
case 'string': return function(x, y) { return x <= y; };
default: return lte;
}
}(rs[0]));

rs.sort(function(a, b) {
return lte_(a.fx, b.fx) ? lte_(b.fx, a.fx) ? a.idx - b.idx : -1 : 1;
});

if (Array.isArray(foldable)) {
for (var idx = 0; idx < rs.length; idx += 1) rs[idx] = rs[idx].x;
return rs;
}

var F = foldable.constructor;
var result = empty(F);
for (var idx = 0; idx < rs.length; idx += 1) {
for (idx = 0; idx < rs.length; idx += 1) {
result = concat(result, of(F, rs[idx].x));
}
return result;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"devDependencies": {
"fantasy-land": "3.5.0",
"list": "2.0.11",
"sanctuary-benchmark": "1.0.x",
"sanctuary-scripts": "1.5.x"
},
Expand Down
2 changes: 2 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,8 @@ test('sort', function() {
eq(Z.sort(Cons('foo', Nil)), Cons('foo', Nil));
eq(Z.sort(Cons('foo', Cons('bar', Nil))), Cons('bar', Cons('foo', Nil)));
eq(Z.sort(Cons('foo', Cons('bar', Cons('baz', Nil)))), Cons('bar', Cons('baz', Cons('foo', Nil))));
eq(Z.sort([NaN, 3, NaN, 1, NaN, 2, NaN]), [NaN, NaN, NaN, NaN, 1, 2, 3]);
eq(Z.sort([Just(3), Just(1), Just(2)]), [Just(1), Just(2), Just(3)]);
}

runAssertions();
Expand Down
13 changes: 13 additions & 0 deletions test/shuffle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle

// shuffle :: Array a -> Undefined
module.exports = function(xs) {
for (var i = xs.length - 1; i > 0; i -= 1) {
var j = Math.floor(Math.random() * (i + 1));
var x = xs[i];
xs[i] = xs[j];
xs[j] = x;
}
};

0 comments on commit 90baa0e

Please sign in to comment.