Skip to content

Commit

Permalink
dgram: support dgram.send with multiple buffers
Browse files Browse the repository at this point in the history
Added ability to dgram.send to send multiple buffers, _writev style.
The offset and length parameters in dgram.send are now optional.
Refactored the dgram benchmarks, and seperated them from net.
Added docs for the new signature.

Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Fixes: nodejs#4302
PR-URL: nodejs#4374
  • Loading branch information
mcollina authored and Michael Scovetta committed Apr 2, 2016
1 parent 728a799 commit 7e8c5d5
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 117 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,10 @@ bench-events: all
bench-util: all
@$(NODE) benchmark/common.js util

bench-all: bench bench-misc bench-array bench-buffer bench-url bench-events
bench-dgram: all
@$(NODE) benchmark/common.js dgram

bench-all: bench bench-misc bench-array bench-buffer bench-url bench-events bench-dgram bench-util

bench: bench-net bench-http bench-fs bench-tls

Expand Down
80 changes: 80 additions & 0 deletions benchmark/dgram/array-vs-concat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// test UDP send throughput with the multi buffer API against Buffer.concat
'use strict';

const common = require('../common.js');
const PORT = common.PORT;

// `num` is the number of send requests to queue up each time.
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
// event loop cycles more than anything else.
var bench = common.createBenchmark(main, {
len: [64, 256, 512, 1024],
num: [100],
chunks: [1, 2, 4, 8],
type: ['concat', 'multi'],
dur: [5]
});

var dur;
var len;
var num;
var type;
var chunk;
var chunks;
var encoding;

function main(conf) {
dur = +conf.dur;
len = +conf.len;
num = +conf.num;
type = conf.type;
chunks = +conf.chunks;

chunk = []
for (var i = 0; i < chunks; i++) {
chunk.push(new Buffer(Math.round(len / chunks)));
}

server();
}

var dgram = require('dgram');

function server() {
var sent = 0;
var received = 0;
var socket = dgram.createSocket('udp4');

var onsend = type === 'concat' ? onsendConcat : onsendMulti;

function onsendConcat() {
if (sent++ % num == 0)
for (var i = 0; i < num; i++) {
socket.send(Buffer.concat(chunk), PORT, '127.0.0.1', onsend);
}
}

function onsendMulti() {
if (sent++ % num == 0)
for (var i = 0; i < num; i++) {
socket.send(chunk, PORT, '127.0.0.1', onsend);
}
}

socket.on('listening', function() {
bench.start();
onsend();

setTimeout(function() {
var bytes = sent * len;
var gbits = (bytes * 8) / (1024 * 1024 * 1024);
bench.end(gbits);
}, dur * 1000);
});

socket.on('message', function(buf, rinfo) {
received++;
});

socket.bind(PORT);
}
70 changes: 70 additions & 0 deletions benchmark/dgram/multi-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// test UDP send/recv throughput with the multi buffer API
'use strict';

const common = require('../common.js');
const PORT = common.PORT;

// `num` is the number of send requests to queue up each time.
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
// event loop cycles more than anything else.
var bench = common.createBenchmark(main, {
len: [64, 256, 1024],
num: [100],
chunks: [1, 2, 4, 8],
type: ['send', 'recv'],
dur: [5]
});

var dur;
var len;
var num;
var type;
var chunk;
var chunks;
var encoding;

function main(conf) {
dur = +conf.dur;
len = +conf.len;
num = +conf.num;
type = conf.type;
chunks = +conf.chunks;

chunk = []
for (var i = 0; i < chunks; i++) {
chunk.push(new Buffer(Math.round(len / chunks)));
}

server();
}

var dgram = require('dgram');

function server() {
var sent = 0;
var received = 0;
var socket = dgram.createSocket('udp4');

function onsend() {
if (sent++ % num == 0)
for (var i = 0; i < num; i++)
socket.send(chunk, PORT, '127.0.0.1', onsend);
}

socket.on('listening', function() {
bench.start();
onsend();

setTimeout(function() {
var bytes = (type === 'send' ? sent : received) * len;
var gbits = (bytes * 8) / (1024 * 1024 * 1024);
bench.end(gbits);
}, dur * 1000);
});

socket.on('message', function(buf, rinfo) {
received++;
});

socket.bind(PORT);
}
7 changes: 4 additions & 3 deletions benchmark/net/dgram.js → benchmark/dgram/offset-length.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// test UDP send/recv throughput
// test UDP send/recv throughput with the "old" offset/length API
'use strict';

var common = require('../common.js');
var PORT = common.PORT;
const common = require('../common.js');
const PORT = common.PORT;

// `num` is the number of send requests to queue up each time.
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
Expand Down
62 changes: 62 additions & 0 deletions benchmark/dgram/single-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// test UDP send/recv throughput with the new single Buffer API
'use strict';

const common = require('../common.js');
const PORT = common.PORT;

// `num` is the number of send requests to queue up each time.
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
// event loop cycles more than anything else.
var bench = common.createBenchmark(main, {
len: [1, 64, 256, 1024],
num: [100],
type: ['send', 'recv'],
dur: [5]
});

var dur;
var len;
var num;
var type;
var chunk;
var encoding;

function main(conf) {
dur = +conf.dur;
len = +conf.len;
num = +conf.num;
type = conf.type;
chunk = new Buffer(len);
server();
}

var dgram = require('dgram');

function server() {
var sent = 0;
var received = 0;
var socket = dgram.createSocket('udp4');

function onsend() {
if (sent++ % num == 0)
for (var i = 0; i < num; i++)
socket.send(chunk, PORT, '127.0.0.1', onsend);
}

socket.on('listening', function() {
bench.start();
onsend();

setTimeout(function() {
var bytes = (type === 'send' ? sent : received) * chunk.length;
var gbits = (bytes * 8) / (1024 * 1024 * 1024);
bench.end(gbits);
}, dur * 1000);
});

socket.on('message', function(buf, rinfo) {
received++;
});

socket.bind(PORT);
}
26 changes: 23 additions & 3 deletions doc/api/dgram.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,10 @@ never have reason to call this.
If `multicastInterface` is not specified, the operating system will attempt to
drop membership on all valid interfaces.

### socket.send(buf, offset, length, port, address[, callback])
### socket.send(buf, [offset, length,] port, address[, callback])

* `buf` Buffer object or string. Message to be sent
* `buf` Buffer object, string, or an array of either. Message to be
sent.
* `offset` Integer. Offset in the buffer where the message starts.
* `length` Integer. Number of bytes in the message.
* `port` Integer. Destination port.
Expand Down Expand Up @@ -224,17 +225,36 @@ The only way to know for sure that the datagram has been sent is by using a
passed as the first argument to the `callback`. If a `callback` is not given,
the error is emitted as an `'error'` event on the `socket` object.

Offset and length are optional, but if you specify one you would need to
specify the other. Also, they are supported only when the first
argument is a `Buffer`.

Example of sending a UDP packet to a random port on `localhost`;

```js
const dgram = require('dgram');
const message = new Buffer('Some bytes');
const client = dgram.createSocket('udp4');
client.send(message, 0, message.length, 41234, 'localhost', (err) => {
client.send(message, 41234, 'localhost', (err) => {
client.close();
});
```

Example of sending a UDP packet composed of multiple buffers to a random port on `localhost`;

```js
const dgram = require('dgram');
const buf1 = new Buffer('Some ');
const buf2 = new Buffer('bytes');
const client = dgram.createSocket('udp4');
client.send([buf1, buf2], 41234, 'localhost', (err) => {
client.close();
});
```

Sending multiple buffers might be faster or slower depending on your
application and operating system: benchmark it. Usually it is faster.

**A Note about UDP datagram size**

The maximum size of an `IPv4/v6` datagram depends on the `MTU`
Expand Down
Loading

0 comments on commit 7e8c5d5

Please sign in to comment.