From 1b3f5443b3e5e2a4bd6fb51dddf674b7406a3d14 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 22 Jan 2016 14:09:51 +0100 Subject: [PATCH] implemented small lookup buffer. This heavily improves lookups for slow databases --- src/Database.js | 14 ++--- src/Utils.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 9 deletions(-) diff --git a/src/Database.js b/src/Database.js index cc20e1eb..324f29c3 100644 --- a/src/Database.js +++ b/src/Database.js @@ -403,16 +403,12 @@ module.exports = function (Y /* :any */) { } } requestTransaction (makeGen/* :any */, callImmediately) { - if (false || callImmediately) { // TODO: decide whether this is ok or not.. - this.waitingTransactions.push(makeGen) - if (!this.transactionInProgress) { - this.transactionInProgress = true + this.waitingTransactions.push(makeGen) + if (!this.transactionInProgress) { + this.transactionInProgress = true + if (true || callImmediately) { // TODO: decide whether this is ok or not.. this.transact(this.getNextRequest()) - } - } else { - this.waitingTransactions.push(makeGen) - if (!this.transactionInProgress) { - this.transactionInProgress = true + } else { var self = this setTimeout(function () { self.transact(self.getNextRequest()) diff --git a/src/Utils.js b/src/Utils.js index 8cea17d7..08abdd2a 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -225,4 +225,164 @@ module.exports = function (Y /* : any*/) { } } Y.utils.compareIds = compareIds + + function createEmptyOpsArray (n) { + var a = new Array(n) + for (var i = 0; i < a.length; i++) { + a[i] = { + id: [null, null] + } + } + return a + } + + function createSmallLookupBuffer (Store) { + /* + This buffer implements a very small buffer that temporarily stores operations + after they are read / before they are written. + The buffer basically implements FIFO. Often requested lookups will be re-queued every time they are looked up / written. + + It can speed up lookups on Operation Stores and State Stores. But it does not require notable use of memory or processing power. + + Good for os and ss, bot not for ds (because it often uses methods that require a flush) + + I tried to optimize this for performance, therefore no highlevel operations. + */ + class SmallLookupBuffer extends Store { + constructor (read, write) { + super() + this.writeBuffer = createEmptyOpsArray(5) + this.readBuffer = createEmptyOpsArray(10) + } + * find (id) { + var i, r + for (i = this.readBuffer.length - 1; i >= 0; i--) { + r = this.readBuffer[i] + // we don't have to use compareids, because id is always defined! + if (r.id[1] === id[1] && r.id[0] === id[0]) { + // found r + // move r to the end of readBuffer + for (; i < this.readBuffer.length - 1; i++) { + this.readBuffer[i] = this.readBuffer[i + 1] + } + this.readBuffer[this.readBuffer.length - 1] = r + return r + } + } + var o + for (i = this.writeBuffer.length - 1; i >= 0; i--) { + r = this.writeBuffer[i] + if (r.id[1] === id[1] && r.id[0] === id[0]) { + o = r + break + } + } + if (i < 0) { + // did not reach break in last loop + // read id and put it to the end of readBuffer + o = yield* super.find(id) + } + if (o != null) { + for (i = 0; i < this.readBuffer.length - 1; i++) { + this.readBuffer[i] = this.readBuffer[i + 1] + } + this.readBuffer[this.readBuffer.length - 1] = o + } + return o + } + * put (o) { + var id = o.id + var i, r // helper variables + for (i = this.writeBuffer.length - 1; i >= 0; i--) { + r = this.writeBuffer[i] + if (r.id[1] === id[1] && r.id[0] === id[0]) { + // is already in buffer + // forget r, and move o to the end of writeBuffer + for (; i < this.writeBuffer.length - 1; i++) { + this.writeBuffer[i] = this.writeBuffer[i + 1] + } + this.writeBuffer[this.writeBuffer.length - 1] = o + break + } + } + if (i < 0) { + // did not reach break in last loop + // write writeBuffer[0] + var write = this.writeBuffer[0] + if (write.id[0] !== null) { + yield* super.put(write) + } + // put o to the end of writeBuffer + for (i = 0; i < this.writeBuffer.length - 1; i++) { + this.writeBuffer[i] = this.writeBuffer[i + 1] + } + this.writeBuffer[this.writeBuffer.length - 1] = o + } + // check readBuffer for every occurence of o.id, overwrite if found + // whether found or not, we'll append o to the readbuffer + for (i = 0; i < this.readBuffer.length - 1; i++) { + r = this.readBuffer[i + 1] + if (r.id[1] === id[1] && r.id[0] === id[0]) { + this.readBuffer[i] = o + } else { + this.readBuffer[i] = r + } + } + this.readBuffer[this.readBuffer.length - 1] = o + } + * delete (id) { + var i, r + for (i = 0; i < this.readBuffer.length; i++) { + r = this.readBuffer[i] + if (r.id[1] === id[1] && r.id[0] === id[0]) { + this.readBuffer[i] = { + id: [null, null] + } + } + } + for (i = 0; i < this.writeBuffer.length; i++) { + r = this.writeBuffer[i] + if (r.id[1] === id[1] && r.id[0] === id[0]) { + this.writeBuffer[i] = { + id: [null, null] + } + } + } + yield* super.delete(id) + } + * findWithLowerBound () { + yield* this.flush() + return yield* super.findWithLowerBound.apply(this, arguments) + } + * findWithUpperBound () { + yield* this.flush() + return yield* super.findWithUpperBound.apply(this, arguments) + } + * findNext () { + yield* this.flush() + return yield* super.findNext.apply(this, arguments) + } + * findPrev () { + yield* this.flush() + return yield* super.findPrev.apply(this, arguments) + } + * iterate () { + yield* this.flush() + yield* super.iterate.apply(this, arguments) + } + * flush () { + for (var i = 0; i < this.writeBuffer.length; i++) { + var write = this.writeBuffer[i] + if (write.id[0] !== null) { + yield* super.put(write) + this.writeBuffer[i] = { + id: [null, null] + } + } + } + } + } + return SmallLookupBuffer + } + Y.utils.createSmallLookupBuffer = createSmallLookupBuffer }