[Reference] Optimising Node-RED

Hi all, a little topic for discussion :smiley: preparing us for a new year of Node-RED fun.

How do we optimise the running of Node-RED?

This is mainly aimed at people using a Pi but should be generally useful for anyone who needs to squeeze out some extra performance. I certainly don't have all the answers so please chip in and I will try to collate a decent list that maybe can then go into the documentation.

Here is my starter list:

Operating System

  • Don't run an OS desktop on the same device as Node-RED. Especially on a Pi, the desktop consumes a LOT of resources.
  • Check for SWAP usage (using top or similar), because a Pi uses an SD-Card for storage, this is extremely slow. But even on other systems use of SWAP significantly slows down the system and limits Node-RED performance.
  • Change Node-RED's nice value. Dave's installer for the Pi sets niceness at 10 which is lower than the average. Try setting to zero or even a negative number to boost the priority of the Node-RED process compared to others on the system. This may, of course, have a negative impact on other processes running on the same machine. This is changed in the systemd service file that runs Node-RED on bootup.
  • Change the node.js parameter max-old-space-size. Like the nice value, you are likely to need to play with this one. This parameter changes when Node.js tries to recover memory from JavaScript variables that may no longer be needed. This is called "garbage collection" (gc). GC is slow and it will potentially slow down your flows. On the other hand, if Node.js runs short of memory it may GC more rapidly or even fail (out of stack space error). On a Pi0/Pi1 you may need to restrict this setting to 128 (MiB). For a Pi2/Pi3 256 (MiB) should be generally good. But it you are having performance issues, you may need to balance this setting against the memory use of other services. If you have 2GB of RAM or more, you should generally be able to leave off this parameter and let Node.JS do its own thing unless you start getting stack errors.
  • Change from using an SD-Card to an SSD. On most Pi's you have to boot off an SD-Card (except for the latest Pi4's) but you can put everything else on an attached SSD (via a USB adapter). Even a spinning hard drive will be faster than the SD-Card. This will really only make a difference if you are doing lots of filing system read/write activity. If you do this, move the log file locations as well as this is often the main load on the SD-Card.
  • Move some services to another device. If your Node-RED system device is being pushed hard, it may help to use a second device and move some services to it. Note, however, that this now means accessing services over your network which itself will be slower. However, overall performance may improve by relieving the pressure on your Node-RED device. Certainly if you need to run a desktop, use a second device not the one running Node-RED. Databases and MQTT brokers would also be reasonable candidates for moving.
  • Turn off unnecessary services. Easy to say, not always easy to identify suitable candidates though. Do some searches online for optimising your device. Take it slow and remove one thing at a time. Keep good notes.
  • Create (or find) an optimised lightweight OS. Really, this is one for people more experienced with managing operating systems. Lightweight versions might be missing critical applications that you will need to know how to identify and install.

Flows

  • Check the size of context/flow/global variables. If you let them get too large, you could run out of memory and force Node.js to garbage collect. In general, you should always prefer passing data using the msg passing that is the core of Node-RED.
  • Avoid having 2 wires out of a node. When you direct the msg output of a node to 2 or more downstream nodes, Node-RED has to "clone" the msg which is a relatively slow operation.
  • Prefer Function Node's over JSONata. Currently, JSONata is typically several times slower than a function node. However, if you don't need to optimise, don't worry about this. In most cases you will not see an impact.
  • Avoid Function Node's. Function Node's use Node.js's virtual machine facility which wraps the code in a protective layer to help protect the underlying service. This has an overhead of course. However, in the majority of cases you will not see any difference in performance, only in extreme cases.
  • Prefer JavaScript Objects over Arrays. This might be a little more controversial. With an array, if you want to access a named entry, you have to search the array using some kind of function. With an Object that uses the appropriate property name (key), you can access the entry directly (e.g. rooms.bathroom). In addition, with an Object you can still use it as an array with Object.values(myobject) and can even get an array of the keys using Object.keys(myobject). So in general, Objects are more flexible and easier to use.

Above all - do not optimise to early! Also, don't optimise unless you need to. Keeping things standard makes them easier to manage.

I am sure that there are a lot more performance tips out there so please contribute to the discussion.


Updates

  • JK 2020-12-31: Added bullets 6 & 7 to the OS section, added warning not to over-optimise.
9 Likes

I'd like to respond for the defence :slight_smile:
Who needs "performance" using Node-RED on a Pi?
Not me and I suspect not many others

:slight_smile:

4 Likes

Well, the topic crops up from time-to-time on the forum and it gets tiring repeating the same things. So I thought that it would be sensible to have all of the optimisation stuff in one place. Then the docs can be updated if needed.

It isn't just about Pi's of course - that's the info I most readily had at hand. We get questions about high-performance throughput and this will increase I'm sure as industrial use of Node-RED increases. Whether you need ms response times or to handle thousands of sensor inputs, we know that Node-RED may be able to do some of this but that it may take a slightly different approach.

1 Like

Would that be a good idea to :

  • disable swap ?
  • use mount options to RAM ?

A 2-edged sword I'm afraid. Whist it prevents SWAP from being used, it also takes away a safety net for memory use. It will keep your system faster but things may fall over more often. My preference is to keep SWAP but notify if it is being used excessively.

I think you are referring to moving the SWAP storage to RAM? Again, this is a 2-edged sword in that you will be making your memory problems worse by using more of it. It would be better to disable swap.

There are other things you could move to RAM such as tmp storage and logs. However, it is unlikely that either will really improve throughput if you are memory constrained. They will speed up access to tmp storage if that is something you need to do a lot which may help in some use cases.

Moving logs to RAM storage only really helps reduce wear on SD-Cards, it doesn't really help system performance unless you are pushing CPU and I/O to the max. And of course, that has the disadvantage that your logs disappear whenever you reboot the system (or it crashes).

The real trick to managing limited system resources is to keep things in balance.

Excellent thread.

I must admit, I did start out trying to keep everything optimised on the Pi 3B+, was this way of doing it faster than that, etc.. But later realised that it was Stability over anything else I was after.

As for speed, in my application - using Node-RED as a communication hub, data logger and HMI for a Home Control system running on a separate controller, time is not of the essence, what does in matter if I don't read a temperature for another second?

However, I shall watch this thread with interest to see if I can add some of the stuff to my flows!

2 Likes

This is a great beginning for what I think will be a good discussion. Have we officially started? I have a bunch of thoughts on the subject, not all of which align with Julian's...

Pitch in by all means.

My viewpoint is:

  1. Write clear and proper flows (without bothering too much about performance).
  2. test flows and if you face performance issue or are concerned about performance also measure/monitor its performance in order to identify the performance bottlenecks.
  3. Optimize any performance bottlenecks identified in step 2.
  4. go back to step 2 until performance is OK.

Key thing to remember is the 20/80 rule : your node-red application is spending 80% of its time in only 20% of the code. So it is important to target the correct part of your flow when optimizing.

I agree that I have not yet a brilliant strategy of identifying performance bottlenecks.
In most cases I manage to find the respective problem nodes by temporarily unwiring them from the flow and see what difference it makes.

2 Likes

I absolutely agree with all of that, just adding that associated with the 80/20 rule is the fact that the area that turns out to be the bottleneck that might need some optimisation is virtually never the area that you expected. Which is the reason why it is best to use the simplest solution initially, and only worry about optimisation if performance actually becomes an issue.

1 Like

How about helping node developers improve the codebase for their nodes.

Like during something like Lighthouse in ChromeDevTools does but in node-red palette management.
Lighthouse recommends smaller libraries that improve bundle size that should return a faster running service. Just like here where Lighthouse discourage the use of Moment.js.

Rightly or wrongly, I must admit to trying to 'brew my own' flows from the basic nodes available in Node-RED rather than download a specific node. e.g. my Sonoff devices run using standard http nodes rather than the Sonoff specific nodes.

As for the referenced moment.js, I have been using Node-RED for over two years now, because of the perceived associated overhead of the large code base, I only downloaded moment a couple of months ago because I needed to define 'past days' in Epoch time, and couldn't find a way to do it in the 'standard' Node-RED nodes.

So, before I start, I do not intend to upset anyone with my simple engineering mind, especially in view of the amazing people who offer their time and skills for free in the Open Source World, they are all heroes in my eyes. I really can't do this sort of stuff and rely entirely on their goodwill! In the (distant) past I have programmed at machine code level (8080, Z80, 6502 among others with memory size in the kB range)!

In my mind, I see 'lighter weight' nodes as a possible route for optimisation. It seems to me, that 'nowadays', this node relies on that library, which includes that other library or code snippet adding to overheads.

If the overall overhead can be reduced, then this might allow for more lightweight/faster code. But then Coders know far more than me and will choose the best options.

Of course there is the one caveat, as long as the reliability remains!

I know, I want it all ways!!

Absolutely. I would certainly encourage anyone to help out. If not with coding, then with documentation, example flows, testing. Node-RED is open source, without contributors it cannot survive.

Wasn't quite what I had in mind when starting the thread but still a good point. :grinning_face_with_smiling_eyes:

Some good points. Thanks. Yes, node developers certainly have a responsibility and it is easily overlooked, especially by part-time coders like myself who are so focussed on the result but don't necessarily have the skills/knowledge to do things in the most efficient way. So it isn't just about developing nodes (which needs some basic level of design skill as well). Other coders and non-coders can review code and make suggestions, I've had some really good ones for some of my nodes and they are always welcome. Even if I don't directly use the suggested approach, they generally start a train of thought that leads to an improvement.

Reading through other people's code also helps you develop your own skills and may encourage you to implement that vital missing component you wanted :grinning_face_with_smiling_eyes:

Indeed. I'm always amazed at how stable Node-RED is. Considering its complexity. To me, that's the magic sauce that makes it worth sticking with.

as it happens moment.js is already in the core and can be accessed via jsonata in the change node so you may not need the extra node (though it may be easier to use/understand).

(I can't recall which other library dragged it in - but as it was already there we decided to expose it via the change node... BUT... it is currently in the process of being deprecated so yet another pain to be sorted at some point)

Thank you dceejay, I will take a look.

Been eyeing JSONATA from a distance, know it can be good, but adds another layer to be learnt. Need to find some time to concentrate so that I can learn and play.

Things just don't seem to go in as easily or as well as they used to! :roll_eyes:

1 Like

Yes, that is a serious pain in the neck. Don't know when/if I'll be able to make time to do something with the contrib-moment node.

"Premature optimization is the root of all evil.” -- D. Knuth

With that out of the way, here is some "philosophy" provoked by @TotallyInformation's post -- without telling you much that you don't already know.

You can't optimize without optimizing for something. The usual assumption is that we want to maximize speed of execution, but memory usage, stability, readability/maintainability, and other "ilities" can dictate using one programming approach over another.

You can't optimize what you can't measure. Speed tends to be measured subjectively until it becomes a disaster. Memory usage was a bit of a muddle, if I recall. With luck, the flow debugger enabled by pluggable message routing will feature some performance metrics (hint, hint). Meanwhile, it might be worth discussing what nodes or flows could be used to measure performance.

There is no free lunch. Each of @TotallyInformation's suggestions comes at a cost, most of which he has identified. I read his list and comments more as a set of places to look if you need improvements than a guide for what to do when starting from scratch.

There are no rules. Precisely because of the trade-offs involved, we won't find simple, hard-and-fast guidelines. For example, "Prefer Function Node's over JSONata" followed by "Avoid Function Nodes" is not a contradiction, just a statement that each approach has pros and cons.

We are amateurs, mostly. The use of professional-grade tools outside the core development team should be encouraged, but ordinary users can easily build flows that bog down a system. Helping them (us) find and fix these issues should be a priority.

4 Likes