Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

massive performance updates

  • Loading branch information...
commit 13fd4663b0c05f78951dd6630612f2fcb19e45a7 1 parent 92d7710
Einar Otto Stangvik einaros authored
5 History.md
View
@@ -1,3 +1,8 @@
+v0.3.2 - Dec 11th 2011
+======================
+
+* Further performance updates, including the additions of a native BufferUtil module, which deals with several of the cpu intensive WebSocket operations.
+
v0.3.1 - Dec 8th 2011
======================
139 bench/WebSocket.benchmark.js
View
@@ -1,9 +1,16 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
/**
* Benchmark dependencies.
*/
var benchmark = require('benchmark')
, Receiver = require('../').Receiver
+ , Sender = require('../').Sender
, suite = new benchmark.Suite('Receiver');
require('tinycolor');
require('./util');
@@ -14,82 +21,88 @@ require('./util');
suite.on('start', function () {
receiver = new Receiver();
- receiver.on('error', function() { console.log(arguments); });
+ sender = new Sender();
});
suite.on('cycle', function () {
receiver = new Receiver();
- receiver.on('error', function() { console.log(arguments); });
+ sender = new Sender();
});
/**
* Benchmarks.
*/
-var pingMessage = 'Hello'
- , pingPacket1 = getBufferFromHexString('89 ' + (pack(2, 0x80 | pingMessage.length)) +
- ' 34 83 a8 68 '+ getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')));
-suite.add('ping message', function () {
- receiver.add(pingPacket1);
-});
-
-var pingPacket2 = getBufferFromHexString('89 00')
-suite.add('ping with no data', function () {
- receiver.add(pingPacket2);
-});
+// var pingMessage = 'Hello'
+// , pingPacket1 = getBufferFromHexString('89 ' + (pack(2, 0x80 | pingMessage.length)) +
+// ' 34 83 a8 68 '+ getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')));
+// suite.add('ping message', function () {
+// receiver.add(pingPacket1);
+// });
+//
+// var pingPacket2 = getBufferFromHexString('89 00')
+// suite.add('ping with no data', function () {
+// receiver.add(pingPacket2);
+// });
+//
+// var closePacket = getBufferFromHexString('88 00');
+// suite.add('close message', function () {
+// receiver.add(closePacket);
+// receiver.endPacket();
+// });
+//
+// var maskedTextPacket = getBufferFromHexString('81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5');
+// suite.add('masked text message', function () {
+// receiver.add(maskedTextPacket);
+// });
-var closePacket = getBufferFromHexString('88 00');
-suite.add('close message', function () {
- receiver.add(closePacket);
- receiver.endPacket();
-});
+// binaryDataPacket = (function() {
+// var length = 125
+// , message = new Buffer(length)
+// for (var i = 0; i < length; ++i) {
+// message[i] = 61;
+// }
+// return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+// + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+// })();
+// suite.add('binary data', function () {
+// try {
+// receiver.add(binaryDataPacket);
+//
+// }
+// catch(e) {console.log(e)}
+// });
+//
+// binaryDataPacket2 = (function() {
+// var length = 65535
+// , message = new Buffer(length)
+// for (var i = 0; i < length; ++i) {
+// message[i] = i % 10;
+// }
+// return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+// + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+// })();
+// suite.add('binary data (65535 bytes long)', function () {
+// receiver.add(binaryDataPacket2);
+// });
-var maskedTextPacket = getBufferFromHexString('81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5');
-suite.add('masked text message', function () {
- receiver.add(maskedTextPacket);
-});
-
-binaryDataPacket = (function() {
- var length = 125
- , message = new Buffer(length)
- for (var i = 0; i < length; ++i) {
- message[i] = 61;
- }
- return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
- + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
-})();
-suite.add('binary data', function () {
- try {
- receiver.add(binaryDataPacket);
-
- }
- catch(e) {console.log(e)}
-});
-
-binaryDataPacket2 = (function() {
- var length = 65535
- , message = new Buffer(length)
- for (var i = 0; i < length; ++i) {
- message[i] = i % 10;
- }
- return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
- + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
-})();
-suite.add('binary data (65535 bytes long)', function () {
- receiver.add(binaryDataPacket2);
-});
+// binaryDataPacket3 = (function() {
+// var length = 200*1024
+// , message = new Buffer(length)
+// for (var i = 0; i < length; ++i) {
+// message[i] = i % 10;
+// }
+// return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
+// + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
+// })();
+// suite.add('binary data (200kB long)', function () {
+// receiver.add(binaryDataPacket3);
+// });
-binaryDataPacket3 = (function() {
- var length = 200*1024
- , message = new Buffer(length)
- for (var i = 0; i < length; ++i) {
- message[i] = i % 10;
- }
- return getBufferFromHexString('82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 '
- + getHexStringFromBuffer(mask(message), '34 83 a8 68'));
-})();
-suite.add('binary data (200kB long)', function () {
- receiver.add(binaryDataPacket3);
+framePacket = new Buffer(200*1024);
+framePacket.fill(99);
+suite.add('frameData', function () {
+ sender.frameData(0x2, framePacket, true, false);
});
/**
6 bench/util.js
View
@@ -1,3 +1,9 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
/**
* Returns a Buffer from a "ff 00 ff"-type hex string.
*/
8 bin/wscat
View
@@ -1,5 +1,11 @@
#!/usr/bin/env node
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
/**
* Module dependencies.
*/
@@ -81,7 +87,7 @@ Console.prototype.restore = function() {
*/
program
- .version('0.3.2-pre')
+ .version('0.3.2')
.usage('[options] <url>')
.option('-l, --listen <port>', 'listen on port')
.option('-c, --connect <url>', 'connect to a websocket server')
6 index.js
View
@@ -1,3 +1,9 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
module.exports = require('./lib/WebSocket');
module.exports.Server = require('./lib/WebSocketServer');
module.exports.Sender = require('./lib/Sender');
17 lib/BufferUtil.js
View
@@ -0,0 +1,17 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+/**
+ * Node version 0.4 and 0.6 compatibility
+ */
+
+try {
+ module.exports = require('../build/Release/bufferutil');
+} catch (e) { try {
+ module.exports = require('../build/default/bufferutil');
+} catch (e) {
+ throw e;
+}}
6 lib/ErrorCodes.js
View
@@ -1,3 +1,9 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
module.exports = {
isValidErrorCode: function(code) {
return (code >= 1000 && code <= 1010 && code != 1004 && code != 1005 && code != 1006) ||
19 lib/Receiver.js
View
@@ -9,7 +9,8 @@ var events = require('events')
, EventEmitter = events.EventEmitter
, Validation = require('./Validation').Validation
, ErrorCodes = require('./ErrorCodes')
- , BufferPool = require('./BufferPool');
+ , BufferPool = require('./BufferPool')
+ , bufferUtil = new require('./BufferUtil').BufferUtil;
/**
* Node version 0.4 and 0.6 compatibility
@@ -461,11 +462,7 @@ Receiver.prototype.reset = function() {
*/
Receiver.prototype.unmask = function (mask, buf, binary) {
- if (mask != null && buf != null) {
- for (var i = 0, ll = buf.length; i < ll; i++) {
- buf[i] ^= mask[i % 4];
- }
- }
+ if (mask != null && buf != null) bufferUtil.unmask(buf, mask);
if (binary) return buf;
return buf != null ? buf.toString('utf8') : '';
}
@@ -478,15 +475,9 @@ Receiver.prototype.unmask = function (mask, buf, binary) {
Receiver.prototype.concatBuffers = function(buffers) {
var length = 0;
- for (var i = 0, l = buffers.length; i < l; ++i) {
- length += buffers[i].length;
- }
+ for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length;
var mergedBuffer = new Buffer(length);
- var offset = 0;
- for (var i = 0, l = buffers.length; i < l; ++i) {
- buffers[i].copy(mergedBuffer, offset);
- offset += buffers[i].length;
- }
+ bufferUtil.merge(mergedBuffer, buffers);
return mergedBuffer;
}
23 lib/Sender.js
View
@@ -7,7 +7,8 @@
var events = require('events')
, util = require('util')
, EventEmitter = events.EventEmitter
- , ErrorCodes = require('./ErrorCodes');
+ , ErrorCodes = require('./ErrorCodes')
+ , bufferUtil = new require('./BufferUtil').BufferUtil;
/**
* Node version 0.4 and 0.6 compatibility
@@ -132,8 +133,12 @@ Sender.prototype.send = function(data, options, cb) {
*/
Sender.prototype.frameData = function(opcode, data, finalFragment, maskData) {
- var dataBuffer = getBufferFromData(data)
- , dataLength = dataBuffer.length
+ if (!data) return new Buffer([opcode | (finalFragment ? 0x80 : 0), 0]);
+ else if (!(data instanceof Buffer)) {
+ data = (data && typeof data.buffer !== 'undefined')
+ ? getArrayBuffer(data.buffer) : new Buffer(data);
+ }
+ var dataLength = data.length
, dataOffset = maskData ? 6 : 2
, secondByte = dataLength;
if (dataLength >= 65536) {
@@ -158,11 +163,11 @@ Sender.prototype.frameData = function(opcode, data, finalFragment, maskData) {
if (maskData) {
var mask = this._randomMask || (this._randomMask = getRandomMask());
mask.copy(outputBuffer, dataOffset - 4);
- applyMaskToBuffer(dataBuffer, mask);
+ bufferUtil.mask(data, mask, outputBuffer, dataOffset);
secondByte = secondByte | 0x80;
}
+ else data.copy(outputBuffer, dataOffset);
outputBuffer[1] = secondByte;
- dataBuffer.copy(outputBuffer, dataOffset);
return outputBuffer;
}
@@ -174,14 +179,6 @@ function applyMaskToBuffer(buf, mask) {
return buf;
}
-function getBufferFromData(data) {
- if (!data) return new Buffer(0);
- if (data instanceof Buffer) return new Buffer(data);
- return (data && typeof data.buffer !== 'undefined')
- ? getArrayBuffer(data.buffer)
- : new Buffer(data);
-}
-
function getArrayBuffer(array) {
var l = array.byteLength
, buffer = new Buffer(l);
7 lib/Validation.js
View
@@ -1,6 +1,13 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
/**
* Node version 0.4 and 0.6 compatibility
*/
+
try {
module.exports = require('../build/Release/validation');
} catch (e) { try {
2  package.json
View
@@ -2,7 +2,7 @@
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
"name": "ws",
"description": "simple and very fast websocket protocol client for node.js",
- "version": "0.3.2-pre",
+ "version": "0.3.2",
"repository": {
"type": "git",
"url": "git://github.com/einaros/ws.git"
100 src/bufferutil.cc
View
@@ -0,0 +1,100 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+ * MIT Licensed
+ */
+
+#include <v8.h>
+#include <node.h>
+#include <node_buffer.h>
+#include <node_object_wrap.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <wchar.h>
+#include <stdio.h>
+
+using namespace v8;
+using namespace node;
+
+class BufferUtil : public ObjectWrap
+{
+public:
+
+ static void Initialize(v8::Handle<v8::Object> target)
+ {
+ HandleScope scope;
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ NODE_SET_METHOD(t->GetFunction(), "unmask", BufferUtil::Unmask);
+ NODE_SET_METHOD(t->GetFunction(), "mask", BufferUtil::Mask);
+ NODE_SET_METHOD(t->GetFunction(), "merge", BufferUtil::Merge);
+ target->Set(String::NewSymbol("BufferUtil"), t->GetFunction());
+ }
+
+protected:
+
+ static Handle<Value> New(const Arguments& args)
+ {
+ HandleScope scope;
+ BufferUtil* bufferUtil = new BufferUtil();
+ bufferUtil->Wrap(args.This());
+ return args.This();
+ }
+
+ static Handle<Value> Merge(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> bufferObj = args[0]->ToObject();
+ char* buffer = Buffer::Data(bufferObj);
+ Local<Array> array = Local<Array>::Cast(args[1]);
+ int arrayLength = array->Length();
+ int offset = 0;
+ int i;
+ for (i = 0; i < arrayLength; ++i) {
+ Local<Object> src = array->Get(i)->ToObject();
+ int length = Buffer::Length(src);
+ memcpy(buffer + offset, Buffer::Data(src), length);
+ offset += length;
+ }
+ return scope.Close(True());
+ }
+
+ static Handle<Value> Unmask(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> buffer_obj = args[0]->ToObject();
+ unsigned char* buffer = (unsigned char*)Buffer::Data(buffer_obj);
+ size_t length = Buffer::Length(buffer_obj);
+ Local<Object> mask_obj = args[1]->ToObject();
+ unsigned char *mask = (unsigned char*)Buffer::Data(mask_obj);
+ int i;
+ for (i = 0; i < length; ++i) {
+ buffer[i] ^= mask[i % 4];
+ }
+ return scope.Close(True());
+ }
+
+ static Handle<Value> Mask(const Arguments& args)
+ {
+ HandleScope scope;
+ Local<Object> buffer_obj = args[0]->ToObject();
+ unsigned char* buffer = (unsigned char*)Buffer::Data(buffer_obj);
+ size_t length = Buffer::Length(buffer_obj);
+ Local<Object> mask_obj = args[1]->ToObject();
+ unsigned char *mask = (unsigned char*)Buffer::Data(mask_obj);
+ Local<Object> output_obj = args[2]->ToObject();
+ unsigned char* output = (unsigned char*)Buffer::Data(output_obj);
+ int dataOffset = args[3]->Int32Value();
+ int i;
+ for (i = 0; i < length; ++i) {
+ output[dataOffset + i] = buffer[i] ^ mask[i % 4];
+ }
+ return scope.Close(True());
+ }
+};
+
+extern "C" void init (Handle<Object> target)
+{
+ HandleScope scope;
+ BufferUtil::Initialize(target);
+}
5 wscript
View
@@ -1,6 +1,6 @@
srcdir = '.'
blddir = 'build'
-VERSION = '0.3.2-pre'
+VERSION = '0.3.2'
def set_options(opt):
opt.tool_options('compiler_cxx')
@@ -15,3 +15,6 @@ def build(bld):
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'validation'
obj.source = 'src/validation.cc'
+ obj2 = bld.new_task_gen('cxx', 'shlib', 'node_addon')
+ obj2.target = 'bufferutil'
+ obj2.source = 'src/bufferutil.cc'
Please sign in to comment.
Something went wrong with that request. Please try again.