Skip to content

Commit

Permalink
Add insert benchmark and update readme
Browse files Browse the repository at this point in the history
Also try adjust to original semi-colon style

Closes isaacs#5
  • Loading branch information
Krzysztof Chrapka committed Jan 23, 2014
1 parent 5606a22 commit c919f58
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 45 deletions.
18 changes: 14 additions & 4 deletions README.md
Expand Up @@ -12,6 +12,10 @@ In cases where it's mixed, a linked list implementation can be
significantly faster. See the benchmark scripts in `bench/*.js` to
measure the differences.

One special case when you really want this package, is when you have a queue
from which entries can be inserted/removed from any place, that is even from
the middle. Or you just simply need a list, and you know why.

This lacks a lot of features that arrays have:

1. You can't specify the size at the outset.
Expand All @@ -38,23 +42,29 @@ npm install fast-list
```javascript
var FastList = require("fast-list")
var list = new FastList()
list.push("foo")
var foo_entry = list.push("foo")
list.unshift("bar")
list.push("baz")
console.log(list.length) // 2
list.insertAfter(foo_entry, "far");
console.log(list.length) // 4
console.log(list.pop()) // baz
console.log(list.pop()) // far
console.log(list.remove(foo_entry)) //foo
console.log(list.shift()) // bar
console.log(list.shift()) // foo
```

### Methods

* `push`: Just like Array.push, but only can take a single entry
* `push`: Just like Array.push, but only can take a single entry,
returns pointer needed for some other methods
* `pop`: Just like Array.pop. Note: if you're only using push and pop,
then you have a stack, and Arrays are better for that.
* `shift`: Just like Array.shift. Note: if you're only using push and
shift, then you have a queue, and Arrays are better for that.
* `unshift`: Just like Array.unshift, but only can take a single entry.
returns pointer needed for some other methods
*
* `insertBefore(pointer, entry)`:
* `drop`: Drop all entries
* `item(n)`: Retrieve the nth item in the list. This involves a walk
every time. It's very slow. If you find yourself using this,
Expand Down
31 changes: 31 additions & 0 deletions bench/random-insert-queue-small-set.js
@@ -0,0 +1,31 @@
var bench = require("bench")

var l = 50
, FastList = require("../fast-list.js")

exports.countPerLap = l
//exports.stepsPerLap = 10


exports.compare =
{ "random []": function () {
var list = []
for (var i = 0; i < l; i++) {
list.splice(Math.random()*(list.length+1), 0, i)
}
}
, "random FastList()": function () {
var list = new FastList()
var map = [null]
for (var i = 0; i < l; i++) {
var before = map[Math.random()*map.length]
if(!map[before]){
map.push(list.push(i))
} else {
map.insertBefore(map[before], i)
}
}
}
}

bench.runMain()
31 changes: 31 additions & 0 deletions bench/random-insert-queue.js
@@ -0,0 +1,31 @@
var bench = require("bench")

var l = 5000
, FastList = require("../fast-list.js")

exports.countPerLap = l
exports.stepsPerLap = 10


exports.compare =
{ "random []": function () {
var list = []
for (var i = 0; i < l; i++) {
list.splice(Math.random()*(list.length+1), 0, i)
}
}
, "random FastList()": function () {
var list = new FastList()
var map = [null]
for (var i = 0; i < l; i++) {
var before = map[Math.random()*map.length]
if(!map[before]){
map.push(list.push(i))
} else {
map.insertBefore(map[before], i)
}
}
}
}

bench.runMain()
53 changes: 34 additions & 19 deletions bench/random-remove-queue-unfair.js
@@ -1,20 +1,27 @@
var bench = require("bench")

/*
* This benchmark compares the scenario of
* randomly removing the elements in a queue
* while maintaining order - something that
* arrays are particularly poor at.
*/

//Fisher-Yates Shuffle
function shuffle(array) {
var m = array.length;
var t,new_pos;
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
new_pos = Math.floor(Math.random() * m);
--m;
// And swap it with the current element.
t = array[m];
array[m] = array[new_pos];
array[new_pos] = t;
}
return array;
var m = array.length
var t,new_pos
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
new_pos = Math.floor(Math.random() * m)
--m
// And swap it with the current element.
t = array[m]
array[m] = array[new_pos]
array[new_pos] = t
}
return array
}


Expand All @@ -30,18 +37,26 @@ exports.compare =
list.push(i)
}
for (var i = 0; i < l; i++) {
list = list.splice(Math.random()*(l-i-1), 1);
//remove a random element and shift the remaning
//array entries so that we don't have a hole in
//the middle
list = list.splice(Math.random()*(l-i-1), 1)
}
}
, "random FastList()": function () {
var list = new FastList();
var order = [];
var list = new FastList()

//this array will keep pointers to all
//entries in list in a random order
var order = []
for (var i = 0; i < l; i++) {
order.push(list.push(i));
order.push(list.push(i))
}
shuffle(order);
//shuffle internally calls l times
//Math.random() (shuffle is O(n))
shuffle(order)
for (var i = 0; i < l; i++) {
list.remove(order[i]);
list.remove(order[i])
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions bench/random-remove-queue.js
@@ -1,10 +1,17 @@
var bench = require("bench")

/*
* This benchmark compares the scenario of
* randomly removing the elements in a queue
* while maintaining order - something that
* arrays are particularly poor at.
*/

/* it would be faster if (as in a real scenario)
* we wouldn't be removing a random entry, and
* having O(l) list.entry() call each loop.
* If we got a pointer to list.remove in const() time
* instead, then well...
* If we got a pointer to list.remove in O(1) time
* instead, then well... (see unfair version of this)
*/

var l = 5000
Expand All @@ -19,7 +26,7 @@ exports.compare =
list.push(i)
}
for (var i = 0; i < l; i++) {
list = list.splice(Math.random()*(l-i-1), 1);
list = list.splice(Math.random()*(l-i-1), 1)
}
}
, "random FastList()": function () {
Expand All @@ -28,7 +35,7 @@ exports.compare =
list.push(i)
}
for (var i = 0; i < l; i++) {
list.remove(list.entry(Math.random()*(l-i-1)));
list.remove(list.entry(Math.random()*(l-i-1)))
}
}
}
Expand Down
38 changes: 20 additions & 18 deletions fast-list.js
@@ -1,7 +1,7 @@
;(function() { // closure for web browsers

function Item (data, prev, next, parent) {
this.parent = parent;
this.parent = parent
this.next = next
if (next) next.prev = this
this.prev = prev
Expand All @@ -20,8 +20,8 @@ FastList.prototype =
{ push: function (data) {
this._tail = new Item(data, this._tail, null, this)
if (!this._head) this._head = this._tail
this.length ++;
return this._tail;
this.length ++
return this._tail
}

, pop: function () {
Expand All @@ -34,14 +34,15 @@ FastList.prototype =
this.length --
if (this.length === 1) this._head = this._tail
else if (this.length === 0) this._head = this._tail = null
t.parent = null
return t.data
}

, unshift: function (data) {
this._head = new Item(data, null, this._head, this)
if (!this._tail) this._tail = this._head
this.length ++;
return this._head;
this.length ++
return this._head
}

, shift: function () {
Expand All @@ -54,30 +55,31 @@ FastList.prototype =
this.length --
if (this.length === 1) this._tail = this._head
else if (this.length === 0) this._head = this._tail = null
h.parent = null
return h.data
}
, remove: function (item) {
if (item.parent !== this) throw new Error('Item does not belong to this list');
if (item.prev) item.prev.next = item.next;
else this._head = item.next;
if (item.next) item.next.prev = item.prev;
else this._tail = item.prev;
item.next = item.prev = item.parent = null;
-- this.length;
return item.data;
if (item.parent !== this) throw new Error('Item does not belong to this list')
if (item.prev) item.prev.next = item.next
else this._head = item.next
if (item.next) item.next.prev = item.prev
else this._tail = item.prev
item.next = item.prev = item.parent = null
this.length --
return item.data
}
, insertBefore: function(item, data){
if (item.parent !== this) throw new Error('Item does not belong to this list');
if (item.parent !== this) throw new Error('Item does not belong to this list')
var n_item = new Item(data, item.prev, item, this)
if(this._head == item) this._head = n_item
++this.length
return n_item;
this.length ++
return n_item
}
, insertAfter: function(item, data){
if (item.parent !== this) throw new Error('Item does not belong to this list');
if (item.parent !== this) throw new Error('Item does not belong to this list')
var n_item = new Item(data, item, item.next, this)
if(this._tail == item) this._tail = n_item
++this.length
this.length ++
return n_item
}
, item: function (n) {
Expand Down

0 comments on commit c919f58

Please sign in to comment.