Skip to content

Commit

Permalink
crypto,tls: perf improvements for crypto and tls getCiphers
Browse files Browse the repository at this point in the history
Improve performance of crypto.getCiphers, getHashes, getCurves
and tls.getCiphers by consolidating filterDuplicates logic, adding
caching of output, and streamlining filterDuplicates implementation.

Benchmarks:

crypto.getCiphers n=1    v6.2.1 = 2559.3, new = 15890 ...... -83.89%
crypto.getCiphers n=5000 v6.2.1 = 3516.3, new = 24203000 ... -99.99%

tls.getCiphers    n=1    v6.2.1 = 3405.3, new = 14877 ...... -77.11%
tls.getCiphers    n=5000 v6.2.1 = 6074.4, new = 24202000 ... -99.97%

PR-URL: #7225
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
  • Loading branch information
jasnell authored and Fishrock123 committed Jun 27, 2016
1 parent e7279f1 commit 8e927c7
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 38 deletions.
20 changes: 20 additions & 0 deletions benchmark/crypto/get-ciphers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

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

const bench = common.createBenchmark(main, {
n: [1, 5000],
v: ['crypto', 'tls']
});

function main(conf) {
const n = +conf.n;
const v = conf.v;
const method = require(v).getCiphers;
var i = 0;

common.v8ForceOptimization(method);
bench.start();
for (; i < n; i++) method();
bench.end(n);
}
36 changes: 9 additions & 27 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -632,41 +632,23 @@ exports.randomBytes = exports.pseudoRandomBytes = randomBytes;

exports.rng = exports.prng = randomBytes;

exports.getCiphers = function() {
return filterDuplicates(getCiphers());
};


exports.getHashes = function() {
return filterDuplicates(getHashes());
};
exports.getCiphers = internalUtil.cachedResult(() => {
return internalUtil.filterDuplicateStrings(getCiphers());
});

exports.getHashes = internalUtil.cachedResult(() => {
return internalUtil.filterDuplicateStrings(getHashes());
});

exports.getCurves = function() {
return filterDuplicates(getCurves());
};
exports.getCurves = internalUtil.cachedResult(() => {
return internalUtil.filterDuplicateStrings(getCurves());
});

Object.defineProperty(exports, 'fips', {
get: getFipsCrypto,
set: setFipsCrypto
});

function filterDuplicates(names) {
// Drop all-caps names in favor of their lowercase aliases,
// for example, 'sha1' instead of 'SHA1'.
var ctx = {};
names.forEach(function(name) {
var key = name;
if (/^[0-9A-Z\-]+$/.test(key)) key = key.toLowerCase();
if (!ctx.hasOwnProperty(key) || ctx[key] < name)
ctx[key] = name;
});

return Object.getOwnPropertyNames(ctx).map(function(key) {
return ctx[key];
}).sort();
}

// Legacy API
Object.defineProperty(exports, 'createCredentials', {
configurable: true,
Expand Down
31 changes: 31 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,34 @@ exports.assertCrypto = function(exports) {
if (noCrypto)
throw new Error('Node.js is not compiled with openssl crypto support');
};

// Filters duplicate strings. Used to support functions in crypto and tls
// modules. Implemented specifically to maintain existing behaviors in each.
exports.filterDuplicateStrings = function filterDuplicateStrings(items, low) {
if (!Array.isArray(items))
return [];
const len = items.length;
if (len <= 1)
return items;
const map = new Map();
for (var i = 0; i < len; i++) {
const item = items[i];
const key = item.toLowerCase();
if (low) {
map.set(key, key);
} else {
if (!map.has(key) || map.get(key) <= item)
map.set(key, item);
}
}
return Array.from(map.values()).sort();
};

exports.cachedResult = function cachedResult(fn) {
var result;
return () => {
if (result === undefined)
result = fn();
return result;
};
};
16 changes: 5 additions & 11 deletions lib/tls.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

require('internal/util').assertCrypto(exports);
const internalUtil = require('internal/util');
internalUtil.assertCrypto(exports);

const net = require('net');
const url = require('url');
Expand All @@ -21,16 +22,9 @@ exports.DEFAULT_CIPHERS =

exports.DEFAULT_ECDH_CURVE = 'prime256v1';

exports.getCiphers = function() {
const names = binding.getSSLCiphers();
// Drop all-caps names in favor of their lowercase aliases,
var ctx = {};
names.forEach(function(name) {
if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase();
ctx[name] = true;
});
return Object.getOwnPropertyNames(ctx).sort();
};
exports.getCiphers = internalUtil.cachedResult(() => {
return internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true);
});

// Convert protocols array into valid OpenSSL protocols list
// ("\x06spdy/2\x08http/1.1\x08http/1.0")
Expand Down

0 comments on commit 8e927c7

Please sign in to comment.