From a25873d4bd81f8fa7045dc72b76e306b603e53d9 Mon Sep 17 00:00:00 2001 From: Andras Date: Sat, 19 Sep 2020 19:53:27 -0400 Subject: [PATCH] 2x faster --- README.md | 42 +++++++++++++++++++-------------- benchmark.js | 8 ++++++- package.json | 2 +- uuid.js | 66 +++++++++++++++++++++++++--------------------------- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 057bfcd..4ab61f8 100644 --- a/README.md +++ b/README.md @@ -10,27 +10,34 @@ My try at nudging the state of the art past the 1 million UUIDs per second barri RFC at https://tools.ietf.org/html/rfc4122 -Summary -------- +Overview +-------- const uuid = require('uuid-quick'); let id = uuid(); +`uuid-quick` is a fast Version 4 (random) UUID generator (see RFC Section 4.4, page 14). +The new version 0.2.0 is almost twice as version 0.1.0 was. + $ node benchmark.js - qtimeit=0.21.0 node=10.15.0 v8=6.8.275.32-node.45 platform=linux kernel=4.9.0-0.bpo.4-amd64 up_threshold=false - arch=ia32 mhz=4183 cpuCount=8 cpu="Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz" - name speed rate - uuid 423,029 ops/sec 212 - node-uuid 417,133 ops/sec 209 - fast-uuid 766,254 ops/sec 383 > - uuid-quick 2,229,696 ops/sec 1115 >> - mongoid-js short 34,198,722 ops/sec 17099 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + qtimeit=0.21.0 node=10.15.0 v8=6.8.275.32-node.45 platform=linux kernel=5.5.0-1-amd64 up_threshold=false + arch=ia32 mhz=4482[os] cpuCount=16 cpu="AMD Ryzen 7 3800X 8-Core Processor" + name speed rate + uuid 277,767 ops/sec 1000 >> + node-uuid 304,581 ops/sec 1097 >> + fast-uuid 871,343 ops/sec 3137 >>>>>> + uuid-quick 4,892,916 ops/sec 17615 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -The results look pretty good, 3-5x faster than the more common uuid packages. Still much much -slower than sequential id generators like `mongoid-js`. This latter 15x speed difference is -inherent in the nature of these UUIDS, they have to be random in every position each time; -sequential generators have redundancies that can be optimized out. +If only uniqueness is needed, then a good sequential id generator like e.g. `mongoid-js` can be +much faster than even the fastest uuid. This speed difference is inherent in UUID's, they have to be +random at avery position; sequential generators have redundancies that can be optimized out. + + qtimeit=0.21.0 node=10.15.0 v8=6.8.275.32-node.45 platform=linux kernel=5.5.0-1-amd64 up_threshold=false + arch=ia32 mhz=4480[os] cpuCount=16 cpu="AMD Ryzen 7 3800X 8-Core Processor" + name speed rate + uuid-quick 4,961,158 ops/sec 1000 >> + mongoid-js short 34,117,176 ops/sec 6877 >>>>>>>>>>>>>> API @@ -41,8 +48,8 @@ API The `uuid-quick` package exports a function that returns Version 4 UUID strings. These are 32 random hexadecimal characters separated by 4 dashes `-` grouped 8-4-4-4-12 in the form "11111111-1111-4111-8111-111111111111", where "1" is a hex digit, "4" is the letter '4', and "8" -is one of '8', '9', 'a' or 'b'. (I said 32, but careful readers will count only 30.5 random hex -characters. I, umm, approximated.) +is one of '8', '9', 'a' or 'b'. (I said 32, but careful readers will note that there are +actually only 30.5 random hex characters, the other 6 bits of the 32 chars are constant.) const uuid = require('uuid-quick'); let id = uuid(); @@ -58,12 +65,13 @@ same as `uuid()` ### uuid.rand the random number generator function to use. By default this property is set to `Math.random`. -Must return floating-point values between 0 and 1 with at least 36 bits of precision. +Must return floating-point values between 0 and 1 with at least 48 bits of precision. Changelog --------- +- 0.2.0 - rewrite, now 2x faster - 0.1.0 - first version diff --git a/benchmark.js b/benchmark.js index 19eb5e8..b6bc34c 100644 --- a/benchmark.js +++ b/benchmark.js @@ -46,7 +46,7 @@ var mongoidFactory = new mongoidjs.MongoId(); qtimeit.bench.timeGoal = .2; qtimeit.bench.showRunDetails = false; qtimeit.bench.visualize = true; -qtimeit.bench.baselineAvg = 2000000; +//qtimeit.bench.baselineAvg = 2000000; qtimeit.bench.bargraphScale = 2; qtimeit.bench.opsPerTest = 2; @@ -64,5 +64,11 @@ qtimeit.bench({ //'hyperid': function() { x = hyperid(); x2 = hyperid() }, // not a uuid, is fixed uuid+counter //'mongoid-js': function() { x = mongoidjs(); x2 = mongoidjs() }, //'mongoid-js': function() { x = mongoidFactory.fetch(); x2 = mongoidFactory.fetch() }, + //'mongoid-js short': function() { x = mongoidFactory.fetchShort(); x2 = mongoidFactory.fetchShort() }, +}); + +qtimeit.bench({ + 'uuid-quick': function() { x = uuidquick(); x2 = uuidquick() }, + //'mongoid-js': function() { x = mongoidFactory.fetch(); x2 = mongoidFactory.fetch() }, 'mongoid-js short': function() { x = mongoidFactory.fetchShort(); x2 = mongoidFactory.fetchShort() }, }); diff --git a/package.json b/package.json index 99cbfbf..fc44911 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uuid-quick", - "version": "0.1.1", + "version": "0.2.0", "description": "quicker uuid, very fast", "main": "uuid.js", diff --git a/uuid.js b/uuid.js index d4662f4..dc23120 100644 --- a/uuid.js +++ b/uuid.js @@ -1,7 +1,7 @@ /** * uuid-quick -- much faster uuid() generator * - * Copyright (C) 2019 Andras Radics + * Copyright (C) 2019-2020 Andras Radics * Licensed under the Apache License, Version 2.0 * * 2019-01-15 - AR. @@ -19,50 +19,48 @@ uuid.v4 = uuid_4; uuid.rand = Math.random; toStruct(uuid); +var CH_1 = 0x31; +var CH_4 = 0x34; +var CH_8 = 0x38; +var CH_DASH = 0x2D; -var hexchars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; -for (var i=0; i<16; i++) hexchars[i * 16] = hexchars[i]; // map high nybble too +// fromCharCode is with many args is faster but only since node-v0.11; spread args faster since node-v8 +// In newer node versions fromCharCode.apply is also fast, but spread args are faster. +// fromCharCode.apply is only fast since node-v8, spread args are faster but were 30x slower before v8 +// Node before v0.11 is slow with multi-arg fromCharCode and is faster with a convert-char-at-a-time loop. +function tryEval(s) { try { return eval(s) } catch (e) {} } +var fromCharCodeLoop = eval("true && function(a) { var s = ''; for (var i=0; i= 9 ? fromCharCodeSpread : fromCharCodeLoop"); -// template from fast-uuid.js: "10000000-1000-4000-8000-100000000000"; +var hexmap = [0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66]; var arr = new Array(36); function uuid_4() { var rand = uuid.rand; - setChars(arr, rand(), 0); - setChars(arr, rand(), 9); - setChars(arr, rand(), 18); - setChars(arr, rand(), 27); + setChars12(arr, rand(), 0); + setChars12(arr, rand(), 12); + setChars12(arr, rand(), 24); - arr[8] = '-'; - arr[13] = '-'; - arr[14] = '4'; - arr[18] = '-'; - arr[19] = hexchars[8 + (arr[19].charCodeAt(0) & 3)]; - arr[23] = '-'; + arr[8] = CH_DASH; + arr[13] = CH_DASH; + arr[14] = CH_4; + arr[18] = CH_DASH + arr[19] = CH_8 + (arr[19] & 3); + arr[23] = CH_DASH; - //return arr.join(''); // 1.15m/s v8, 1.2m/s v10 - return arrayConcat(arr); // 1.9m/s v8, 2.1m/s v10 + return fromCharCode(arr); } -function arrayConcat(a) { - var s = a[0], len = a.length; - for (var i=1; i>> 4) & 0xF]; + buf[pos + i + 2] = hexmap[(n >>> 8) & 0xF]; } - buf[pos+8] = hexchars[(n * 16) & 0xF]; return; }