Skip to content

Instantly share code, notes, and snippets.

@baptistemanson
Created February 10, 2017 21:22
Show Gist options
  • Save baptistemanson/a5f6f78ffc38b0e8f141e88a00c76a63 to your computer and use it in GitHub Desktop.
Save baptistemanson/a5f6f78ffc38b0e8f141e88a00c76a63 to your computer and use it in GitHub Desktop.
JS fast packing with JSON
/**
* FAST PACKING / UNPACKING JSON
*
* If all objects in a collection follows a similar schema,
* then there is gain in changing the representation from a dictionary to a simple array.
*
* It is known results used in database, protocols, in v8 itself with shadow maps and IRL.
*
* In this example, we expect our final exchange to be equivalent to this literal representation:
* [
* {id:1,name:'first'},
* {id:2,name:'second'},
* {id:3,name:'third'},
* {id:4,name:'fourth'}
* ]
*
* We show that, knowing the schema, the following representation is better in speed for the whole chain and in space as well.
* [
* [1, 'first'],
* [2, 'second'],
* [3, 'third'],
* [4, 'fourth']
* ]
* ]
* This example works for this schema, but can be easily generalized for any tabular data structure.
*/
/**
* Packs a collection based on a schema function.
*
* It is equivalent to ` collection.map(packFunc) .` .map() was just utterly slow on nodejs 7.0.
*
*/
let packer = (collection, packFunc) => {
let l = collection.length;
var result = new Array(l)
for(var i=0; i<l; i++) {
result[i] = packFunc(collection[i])
}
return result
}
/**
* Unpacks a collection based on an schemaConstructor
*
* It is iterating over the collection, using the constructor on each element. Can also be a one liner with .map()
*/
let unpacker = (collection, schemaConstructor) => {
let l = collection.length;
var result = new Array(l)
for(var i=0; i<l; i++) {
result[i] = new schemaConstructor(collection[i])
}
return result
}
// Function that maps one packed array to an object
function unpack (arr) {
this.id = arr[0]
this.name = arr[1]
}
// Function that maps one object to one pack array.
let pack = element => [element.id, element.name]
// Generates test fixtures
let mockGen = collectionLength => {
let collection = new Array(collectionLength)
for (var i = 0; i < collectionLength; i++) {
collection[i] = {id:i, name:'randomStrings'}
}
return collection
}
/** Display utilities */
var now = require("performance-now")
let t = {}
let mark = label => t[label] = now()
let delta = (labelStart, labelEnd, index) => console.log( (t[labelEnd] - t[labelStart]).toFixed(3), index)
/**
*
* MAIN
*
*/
console.log('generating a collection of a 1,000 objects')
let expectedResults = mockGen(1000)
console.log('encoding data / JSON.stringify...')
mark(0)
let naiveJSON = JSON.stringify(expectedResults)
mark(1)
let packJSON = JSON.stringify(packer(expectedResults, pack))
mark(2)
delta(0,1, 'ms 1 - naive server encoding')
delta(1,2, 'ms 2 - packed server encoding')
console.log('size / uncompressed')
console.log(Math.round(naiveJSON.length/1000) + ' kB for 1 - naive')
console.log(Math.round(packJSON.length/1000) + ' kB for 2 - packed')
console.log('parsing data / JSON.parse...')
mark(10)
var i = 10000
while(i--) result = JSON.parse(naiveJSON)
mark(11)
var i = 10000
while(i--) result = unpacker(JSON.parse(packJSON), unpack)
mark(12)
delta(10,11, 'ms 1 - naive client decoding')
delta(11,12, 'ms 2 - packed client decoding')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment