Skip to content

Commit

Permalink
Testing out the cost of crossing the js/C++ barrier for parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
christkv committed Dec 19, 2011
1 parent 85c227c commit b62724a
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 180 deletions.
206 changes: 35 additions & 171 deletions benchmark/bson_benchmark.js
Expand Up @@ -6,11 +6,11 @@ var BSON = require('../lib/mongodb').BSONNative.BSON,
debug = require('util').debug,
inspect = require('util').inspect;

var BSON = require('../lib/mongodb').BSONPure.BSON,
ObjectID = require('../lib/mongodb').BSONPure.ObjectID,
Code = require('../lib/mongodb').BSONPure.Code,
Long = require('../lib/mongodb').BSONPure.Long,
Binary = require('../lib/mongodb').BSONPure.Binary;
// var BSON = require('../lib/mongodb').BSONPure.BSON,
// ObjectID = require('../lib/mongodb').BSONPure.ObjectID,
// Code = require('../lib/mongodb').BSONPure.Code,
// Long = require('../lib/mongodb').BSONPure.Long,
// Binary = require('../lib/mongodb').BSONPure.Binary;

var COUNT = 10000;
// var COUNT = 100;
Expand All @@ -33,183 +33,47 @@ var object = {
code: new Code("function() {}", {i:1})
}

// var object = {
// string: "Strings are great",
// // decimal: 3.14159265,
// // bool: true,
// // integer: 5,
// //
// // subObject: {
// // moreText: "Bacon ipsum dolor sit amet cow pork belly rump ribeye pastrami andouille. Tail hamburger pork belly, drumstick flank salami t-bone sirloin pork chop ribeye ham chuck pork loin shankle. Ham fatback pork swine, sirloin shankle short loin andouille shank sausage meatloaf drumstick. Pig chicken cow bresaola, pork loin jerky meatball tenderloin brisket strip steak jowl spare ribs. Biltong sirloin pork belly boudin, bacon pastrami rump chicken. Jowl rump fatback, biltong bacon t-bone turkey. Turkey pork loin boudin, tenderloin jerky beef ribs pastrami spare ribs biltong pork chop beef.",
// // longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly boudin shoulder ribeye pork chop brisket biltong short ribs. Salami beef pork belly, t-bone sirloin meatloaf tail jowl spare ribs. Sirloin biltong bresaola cow turkey. Biltong fatback meatball, bresaola tail shankle turkey pancetta ham ribeye flank bacon jerky pork chop. Boudin sirloin shoulder, salami swine flank jerky t-bone pork chop pork beef tongue. Bresaola ribeye jerky andouille. Ribeye ground round sausage biltong beef ribs chuck, shank hamburger chicken short ribs spare ribs tenderloin meatloaf pork loin."
// // },
// //
// subArray: [1,2,3,4,5,6,7,8,9,10],
// // anotherString: "another string",
// // code: new Code("function() {}", {i:1})
// }
// Number of objects
var numberOfObjects = 1000;
// var numberOfObjects = 2;

// Object serialized
objectBSON = BSON.serialize(object, null, true)

// Buffer With copies of the objectBSON
var data = new Buffer(objectBSON.length * numberOfObjects);
var index = 0;

// Copy the buffer 1000 times to create a strea m of objects
for(var i = 0; i < numberOfObjects; i++) {
// Copy data
objectBSON.copy(data, index);
// Adjust index
index = index + objectBSON.length;
}

// console.log("-----------------------------------------------------------------------------------")
// console.dir(objectBSON)

// for (i=10000; --i>=0; ) {
// objectBSON = BSON.serialize(object, null, true)
// }
//
var x, start, end, j
var objectBSON, objectJSON

console.log(COUNT + "x (objectBSON = BSON.serialize(object))")
start = new Date

// // var object = {
// // '_id': new ObjectID(),
// // 'x': 1,
// // 'integer':5,
// // 'number':5.05,
// // 'boolean':false,
// // 'array':['test', 'benchmark']
// // }
//
for (j=COUNT; --j>=0; ) {
// // var object = {
// // string: "Strings are great",
// // decimal: 3.14159265,
// // bool: true,
// // integer: 5,
// // subObject: {
// // moreText: "Bacon ipsum dolor sit amet cow pork belly rump ribeye pastrami andouille. Tail hamburger pork belly, drumstick flank salami t-bone sirloin pork chop ribeye ham chuck pork loin shankle. Ham fatback pork swine, sirloin shankle short loin andouille shank sausage meatloaf drumstick. Pig chicken cow bresaola, pork loin jerky meatball tenderloin brisket strip steak jowl spare ribs. Biltong sirloin pork belly boudin, bacon pastrami rump chicken. Jowl rump fatback, biltong bacon t-bone turkey. Turkey pork loin boudin, tenderloin jerky beef ribs pastrami spare ribs biltong pork chop beef.",
// // longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly boudin shoulder ribeye pork chop brisket biltong short ribs. Salami beef pork belly, t-bone sirloin meatloaf tail jowl spare ribs. Sirloin biltong bresaola cow turkey. Biltong fatback meatball, bresaola tail shankle turkey pancetta ham ribeye flank bacon jerky pork chop. Boudin sirloin shoulder, salami swine flank jerky t-bone pork chop pork beef tongue. Bresaola ribeye jerky andouille. Ribeye ground round sausage biltong beef ribs chuck, shank hamburger chicken short ribs spare ribs tenderloin meatloaf pork loin."
// // },
// // subArray: [1,2,3,4,5,6,7,8,9,10],
// // anotherString: "another string"
// // }
//
// // debug("============== calculateObjectSize :: " + BSON.calculateObjectSize(object))
// // debug("============== _calculateObjectSize :: " + BSON._calculateObjectSize(object))
// // BSON._calculateObjectSize(object);
// // BSON.calculateObjectSize(object);

objectBSON = BSON.serialize(object, null, true)
// // objectBSON = BSON._serialize(object, null, true)
//
// // var b = BSON.deserialize(objectBSON);
//
// // debug(inspect(b))
//
var objects = BSON.deserializeStream(data, 0, numberOfObjects);
// console.log("----------------------------------------------------------------------------------- 0")
// var objects = BSON.deserialize(data);
console.log("----------------------------------------------------------------------------------- 1")
console.dir(objects)

// var doc = {};
// for(var i = 0; i < 1; i++) {
// doc['timestamp' + i] = Date.now();
// }
// var docs = [];
// for(var i = 0; i < 1; i++) {
// docs.push(doc);
// for (j=COUNT; --j>=0; ) {
// // objectBSON = BSON.serialize(object, null, true)
// objects
// }
//
// var object = {'doc':docs}
// debug(inspect(object))
//
// BSON.calculateObjectSize(object);

// objectBSON = BSON.serialize(object, null, true)
//
// // var b = BSON.deserialize(objectBSON);
//
// // debug(inspect(b))
}

// // debug(inspect(objectBSON))

end = new Date
var opsprsecond = COUNT / ((end - start)/1000);
console.log("bson size (bytes): ", objectBSON.length);
console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec");
console.log("MB/s = " + ((opsprsecond*objectBSON.length)/1024));
// console.log("bson size (bytes): ", ((objectBSON.length * COUNT)/(1024*1024))/((end - start)/1000));

// var COUNT = 1000000;
// // var COUNT = 1;
//
// console.log(COUNT + "x (objectBSON = BSON.serialize(object))")
// start = new Date
//
// // // var object = {
// // // '_id': new ObjectID(),
// // // 'x': 1,
// // // 'integer':5,
// // // 'number':5.05,
// // // 'boolean':false,
// // // 'array':['test', 'benchmark']
// // // }
//
// for (i=COUNT; --i>=0; ) {
// // var object = {
// // string: "Strings are great",
// // decimal: 3.14159265,
// // bool: true,
// // integer: 5,
// // subObject: {
// // moreText: "Bacon ipsum dolor sit amet cow pork belly rump ribeye pastrami andouille. Tail hamburger pork belly, drumstick flank salami t-bone sirloin pork chop ribeye ham chuck pork loin shankle. Ham fatback pork swine, sirloin shankle short loin andouille shank sausage meatloaf drumstick. Pig chicken cow bresaola, pork loin jerky meatball tenderloin brisket strip steak jowl spare ribs. Biltong sirloin pork belly boudin, bacon pastrami rump chicken. Jowl rump fatback, biltong bacon t-bone turkey. Turkey pork loin boudin, tenderloin jerky beef ribs pastrami spare ribs biltong pork chop beef.",
// // longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly boudin shoulder ribeye pork chop brisket biltong short ribs. Salami beef pork belly, t-bone sirloin meatloaf tail jowl spare ribs. Sirloin biltong bresaola cow turkey. Biltong fatback meatball, bresaola tail shankle turkey pancetta ham ribeye flank bacon jerky pork chop. Boudin sirloin shoulder, salami swine flank jerky t-bone pork chop pork beef tongue. Bresaola ribeye jerky andouille. Ribeye ground round sausage biltong beef ribs chuck, shank hamburger chicken short ribs spare ribs tenderloin meatloaf pork loin."
// // },
// // subArray: [1,2,3,4,5,6,7,8,9,10],
// // anotherString: "another string"
// // }
//
// // debug("============== calculateObjectSize :: " + BSON.calculateObjectSize(object))
// // debug("============== _calculateObjectSize :: " + BSON._calculateObjectSize(object))
// // BSON._calculateObjectSize(object);
// // BSON._calculateObjectSize(object);
//
// // objectBSON = BSON.serialize(object, null, true)
// object = BSON.deserialize(objectBSON);
// }
//
// // // debug(inspect(objectBSON))
// //
// end = new Date
// // console.log("bson size (bytes): ", objectBSON.length)
// console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec")
//
// // debug("-------------------------------------------------------------")
// // debug(object)
//
//
// // // var COUNT = 1000000;
// //
// // // console.log(COUNT + "x (objectJSON = JSON.stringify(object))")
// // // start = new Date
// // //
// // // for (i=COUNT; --i>=0; ) {
// // // objectJSON = JSON.stringify(object)
// // // }
// // //
// // // end = new Date
// // console.log("json size (chars): ", objectJSON.length)
// // console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec")
// //
// // var COUNT = 1000000;
// // var COUNT = 1;
// //
// // console.log(COUNT + " BSON.deserialize(objectBSON)")
// // var COUNT = 1000000;
// //
// // start = new Date
// //
// // for (i=COUNT; --i>=0; ) {
// // x = BSON.deserialize(objectBSON)
// // }
// //
// // end = new Date
// // console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec")
// //
// //
// // debug(inspect(x))
//
// // console.log(COUNT + " JSON.parse(objectJSON)")
// // start = new Date
// //
// // for (i=COUNT; --i>=0; ) {
// // x = JSON.parse(objectJSON)
// // }
// //
// // end = new Date
// // console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec")
console.log("MB/s = " + ((opsprsecond*objectBSON.length)/1024));
30 changes: 27 additions & 3 deletions external-libs/bson/bson.cc
Expand Up @@ -76,6 +76,7 @@ void BSON::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(constructor_template->GetFunction(), "serialize", BSONSerialize);
NODE_SET_METHOD(constructor_template->GetFunction(), "serializeWithBufferAndIndex", SerializeWithBufferAndIndex);
NODE_SET_METHOD(constructor_template->GetFunction(), "deserialize", BSONDeserialize);
NODE_SET_METHOD(constructor_template->GetFunction(), "deserializeStream", BSONDeserializeStream);
NODE_SET_METHOD(constructor_template->GetFunction(), "encodeLong", EncodeLong);
NODE_SET_METHOD(constructor_template->GetFunction(), "toLong", ToLong);
NODE_SET_METHOD(constructor_template->GetFunction(), "toInt", ToInt);
Expand Down Expand Up @@ -1149,6 +1150,32 @@ uint32_t BSON::calculate_object_size(Handle<Value> value, bool serializeFunction
return object_size;
}

Handle<Value> BSON::BSONDeserializeStream(const Arguments &args) {
HandleScope scope;

// At least 3 arguments required
if(args.Length() < 3) VException("Arguments required (Buffer(data), Number(index in data), Number(number of documents to deserialize), Object(optional))");

// If the number of argumets equals 3
if(args.Length() >= 3) {
if(!Buffer::HasInstance(args[0])) return VException("First argument must be Buffer instance");
if(!args[1]->IsUint32()) return VException("Second argument must be a positive index number");
if(!args[2]->IsUint32()) return VException("Third argument must be a positive number of documents to deserialize");
}

// If we have 4 arguments
if(args.Length() == 4 && !args[3]->IsObject()) return VException("Fourth argument must be an object with options");

// Create return Object to wrap data in
Local<Object> resultObject = Object::New();
// Create an array for results
Local<Array> documents = Array::New(args[2]->ToUint32()->Value());
// Add objects to the result Object
resultObject->Set(String::New("index"), Integer::New(0));
resultObject->Set(String::New("documents"), documents);
return scope.Close(resultObject);
}

Handle<Value> BSON::BSONDeserialize(const Arguments &args) {
HandleScope scope;

Expand Down Expand Up @@ -1177,12 +1204,9 @@ Handle<Value> BSON::BSONDeserialize(const Arguments &args) {

return BSON::deserialize(data, NULL);
} else {
// Let's fetch the encoding
// enum encoding enc = ParseEncoding(args[1]);
// The length of the data for this encoding
ssize_t len = DecodeBytes(args[0], BINARY);
// Let's define the buffer size
// data = new char[len];
data = (char *)malloc(len);
// Write the data to the buffer from the string object
ssize_t written = DecodeWrite(data, len, args[0], BINARY);
Expand Down
1 change: 1 addition & 0 deletions external-libs/bson/bson.h
Expand Up @@ -16,6 +16,7 @@ class BSON : public ObjectWrap {
static void Initialize(Handle<Object> target);
static Handle<Value> BSONSerialize(const Arguments &args);
static Handle<Value> BSONDeserialize(const Arguments &args);
static Handle<Value> BSONDeserializeStream(const Arguments &args);

// Encode functions
static Handle<Value> EncodeLong(const Arguments &args);
Expand Down
39 changes: 33 additions & 6 deletions lib/mongodb/bson/bson.js
Expand Up @@ -804,14 +804,38 @@ var crc32 = function(string, start, end) {
return crc ^ (-1);
}

/**
* Deserialize stream `data` as BSON documents.
*
* @param {TODO} data
* @param {TODO} options
* @return {TODO}
*/
BSON.deserializeStream = function(data, startIndex, numberOfDocuments, options) {
options = options != null ? options : {};
var documents = new Array(numberOfDocuments);
var index = startIndex;
// Loop over all documents
for(var i = 0; i < numberOfDocuments; i++) {
// Find size of the document
var size = data[index] | data[index + 1] << 8 | data[index + 2] << 16 | data[index + 3] << 24;
// Update options with index
options['index'] = index;
// Parse the document at this point
documents[i] = BSON.deserialize(data, options);
// Adjust index by the document size
index = index + size;
}

// Return object containing end index of parsing and list of documents
return {index:index, documents:documents};
}

/**
* Deserialize `data` as BSON.
*
* @param {TODO} data
* @param {Bool} is_array_item
* @param {TODO} returnData
* @param {TODO} returnArray
* @param {TODO} options
* @return {TODO}
*/
BSON.deserialize = function(data, options) {
Expand Down Expand Up @@ -840,14 +864,17 @@ BSON.deserialize = function(data, options) {
var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions'];
var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions'];
var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32'];
index = options['index'] == null ? index : options['index'];

// Decode
var size = data[index] | data[index + 1] << 8 | data[index + 2] << 16 | data[index + 3] << 24;

// Data length
var dataLength = index + size;

// Ajust index
index = index + 4;
// Data length
var dataLength = data.length;


while(index < dataLength) {
// Read the first byte indicating the type of object
var type = data[index];
Expand Down

0 comments on commit b62724a

Please sign in to comment.