Browse Source

crypto,tls: perf improvements for crypto and tls getCiphers

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: https://github.com/nodejs/node/pull/7225
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
v7.x
James M Snell 9 years ago
parent
commit
6be73feaeb
  1. 20
      benchmark/crypto/get-ciphers.js
  2. 36
      lib/crypto.js
  3. 31
      lib/internal/util.js
  4. 16
      lib/tls.js

20
benchmark/crypto/get-ciphers.js

@ -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
lib/crypto.js

@ -632,41 +632,23 @@ exports.randomBytes = exports.pseudoRandomBytes = randomBytes;
exports.rng = exports.prng = randomBytes; exports.rng = exports.prng = randomBytes;
exports.getCiphers = function() { exports.getCiphers = internalUtil.cachedResult(() => {
return filterDuplicates(getCiphers()); return internalUtil.filterDuplicateStrings(getCiphers());
}; });
exports.getHashes = function() {
return filterDuplicates(getHashes());
};
exports.getHashes = internalUtil.cachedResult(() => {
return internalUtil.filterDuplicateStrings(getHashes());
});
exports.getCurves = function() { exports.getCurves = internalUtil.cachedResult(() => {
return filterDuplicates(getCurves()); return internalUtil.filterDuplicateStrings(getCurves());
}; });
Object.defineProperty(exports, 'fips', { Object.defineProperty(exports, 'fips', {
get: getFipsCrypto, get: getFipsCrypto,
set: setFipsCrypto 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 // Legacy API
Object.defineProperty(exports, 'createCredentials', { Object.defineProperty(exports, 'createCredentials', {
configurable: true, configurable: true,

31
lib/internal/util.js

@ -121,3 +121,34 @@ exports.normalizeEncoding = function normalizeEncoding(enc) {
} }
} }
}; };
// 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
lib/tls.js

@ -1,6 +1,7 @@
'use strict'; 'use strict';
require('internal/util').assertCrypto(exports); const internalUtil = require('internal/util');
internalUtil.assertCrypto(exports);
const net = require('net'); const net = require('net');
const url = require('url'); const url = require('url');
@ -21,16 +22,9 @@ exports.DEFAULT_CIPHERS =
exports.DEFAULT_ECDH_CURVE = 'prime256v1'; exports.DEFAULT_ECDH_CURVE = 'prime256v1';
exports.getCiphers = function() { exports.getCiphers = internalUtil.cachedResult(() => {
const names = binding.getSSLCiphers(); return internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true);
// 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();
};
// Convert protocols array into valid OpenSSL protocols list // Convert protocols array into valid OpenSSL protocols list
// ("\x06spdy/2\x08http/1.1\x08http/1.0") // ("\x06spdy/2\x08http/1.1\x08http/1.0")

Loading…
Cancel
Save