Hello,
In a test app (checking data transfer performance), I need to generate a random string of 50000000 characters (the number may vary, but, it is of this kind of magnitude).
I found this contrib node node-red-contrib-random-string (node) - Node-RED that does (almost) what I need except the size of the string is in the config of the node and not as an input (eg. msg.size).
I add a look at the github repository of this contrib and looked at the code here https://github.com/Wavebreakers/node-red-contrib-random-string/blob/main/random-string.js
As the code is fairly simple I have used the random generation part in a function node. It works.
However, I noticed (for this kind of size) a fairly large difference in the execution time.
The contrib node being more than twice as fast as the function, even if the js in the code is exactly the same.
Here the two flows creating a 50M string along with flow-timers.
[
{
"id": "9a693308.7ebaf",
"type": "subflow",
"name": "flow-timer",
"info": "",
"category": "",
"in": [
{
"x": 80,
"y": 100,
"wires": [
{
"id": "7fc82258.93e36c"
}
]
}
],
"out": [
{
"x": 440,
"y": 100,
"wires": [
{
"id": "7fc82258.93e36c",
"port": 0
}
]
}
],
"env": [
{
"name": "name",
"type": "str",
"value": "measure",
"ui": {
"icon": "font-awesome/fa-tag",
"label": {
"en-US": "Timer Name"
},
"type": "input",
"opts": {
"types": [
"str",
"env"
]
}
}
},
{
"name": "operation",
"type": "str",
"value": "start",
"ui": {
"icon": "font-awesome/fa-cog",
"label": {
"en-US": "Operation"
},
"type": "select",
"opts": {
"opts": [
{
"l": {
"en-US": "start"
},
"v": "start"
},
{
"l": {
"en-US": "stop"
},
"v": "stop"
},
{
"l": {
"en-US": "msg.topic"
},
"v": "msg.topic"
},
{
"l": {
"en-US": "msg.operation"
},
"v": "msg.operation"
},
{
"l": {
"en-US": "msg.payload"
},
"v": "msg.payload"
}
]
}
}
}
],
"meta": {
"module": "node-red-contrib-flow-performance",
"type": "flow-performance",
"version": "1.0.1",
"author": "Steve-Mcl",
"desc": "Inline flow performance measure node",
"keywords": "node-red performance",
"license": "MIT"
},
"color": "#DAEAAA",
"icon": "node-red/timer.svg",
"status": {
"x": 280,
"y": 160,
"wires": [
{
"id": "7fc82258.93e36c",
"port": 1
}
]
}
},
{
"id": "7fc82258.93e36c",
"type": "function",
"z": "9a693308.7ebaf",
"name": "do operation",
"func": "// @ts-ignore\nvar name = msg.perfName || env.get(\"name\");\n// @ts-ignore\nvar operation = msg.perfOperation || env.get(\"operation\");\nvar measures = global.get(\"flow_timers\") || {};\nvar measure = measures[name] || {};\n\nfunction doOp(measure, op){\n if(operation === \"start\"){\n measure.start = Date.now();//change to process.hrtime\n measure.stop = null;\n measure.durationMs = null;\n } else if(operation === \"stop\") {\n measure.stop = Date.now();//change to process.hrtime\n measure.durationMs = measure.start ? measure.stop - measure.start : null;\n msg._performance = measure;\n }\n}\n\n\nif(operation === \"start\"){\n doOp(measure, operation);\n} else if(operation === \"stop\") {\n doOp(measure, operation);\n node.send([null, { payload: { text: name + \": \" + measure.durationMs + \"ms\" }}]);\n} else if(operation === \"msg.topic\") {\n operation = msg.topic;\n doOp(measure, operation);\n} else if(operation === \"msg.operation\") {\n operation = msg.operation;\n doOp(measure, operation);\n} else if(operation === \"msg.payload\") {\n operation = msg.payload;\n doOp(measure, operation);\n} else {\n return [msg, null];\n}\nmeasures[name] = measure;\nglobal.set(\"flow_timers\", measures);\n\nreturn [msg, null];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 100,
"wires": [
[],
[]
]
},
{
"id": "d82e74922cabc567",
"type": "random-string",
"z": "47900301b5f7a8d3",
"size": "50000000",
"characters": "",
"property": "payload",
"x": 580,
"y": 300,
"wires": [
[
"f6181e74.1732a"
]
]
},
{
"id": "23b867b438bef31b",
"type": "inject",
"z": "47900301b5f7a8d3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 300,
"wires": [
[
"80b9b0b2.1dd84"
]
]
},
{
"id": "db0c9a1d5590c769",
"type": "debug",
"z": "47900301b5f7a8d3",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 920,
"y": 300,
"wires": []
},
{
"id": "80b9b0b2.1dd84",
"type": "subflow:9a693308.7ebaf",
"z": "47900301b5f7a8d3",
"name": "",
"env": [
{
"name": "name",
"value": "function",
"type": "str"
}
],
"x": 400,
"y": 300,
"wires": [
[
"d82e74922cabc567"
]
]
},
{
"id": "f6181e74.1732a",
"type": "subflow:9a693308.7ebaf",
"z": "47900301b5f7a8d3",
"name": "",
"env": [
{
"name": "name",
"value": "function",
"type": "str"
},
{
"name": "operation",
"value": "stop",
"type": "str"
}
],
"x": 760,
"y": 300,
"wires": [
[
"db0c9a1d5590c769"
]
]
},
{
"id": "307e766c6c9a6a58",
"type": "function",
"z": "47900301b5f7a8d3",
"name": "function 1",
"func": "let characters = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\nlet size = msg.payload;\nlet payload = '';\n\t\t\tfor (let i = 0; i < size; i++) {\n\t\t\t\tpayload += characters.charAt(Math.floor(Math.random() * characters.length));\n\t\t\t}\nmsg.payload=payload;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 560,
"y": 380,
"wires": [
[
"0853a821e09eee06"
]
]
},
{
"id": "288b528a6b71c1dc",
"type": "inject",
"z": "47900301b5f7a8d3",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "50000000",
"payloadType": "num",
"x": 260,
"y": 380,
"wires": [
[
"e469935b15a999d2"
]
]
},
{
"id": "e469935b15a999d2",
"type": "subflow:9a693308.7ebaf",
"z": "47900301b5f7a8d3",
"name": "",
"env": [
{
"name": "name",
"value": "function",
"type": "str"
}
],
"x": 400,
"y": 380,
"wires": [
[
"307e766c6c9a6a58"
]
]
},
{
"id": "9aad27f0e84edf0e",
"type": "debug",
"z": "47900301b5f7a8d3",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 900,
"y": 380,
"wires": []
},
{
"id": "0853a821e09eee06",
"type": "subflow:9a693308.7ebaf",
"z": "47900301b5f7a8d3",
"name": "",
"env": [
{
"name": "name",
"value": "function",
"type": "str"
},
{
"name": "operation",
"value": "stop",
"type": "str"
}
],
"x": 740,
"y": 380,
"wires": [
[
"9aad27f0e84edf0e"
]
]
}
]
How come the "same" code can be much faster (roughly twice) in the contrib node compare to the function ?
I have tried a significant number of time to run both. And the result is always the same.
What would be the most efficient way to create a very large random string / buffer ?