DEV Community

Cover image for Performance optimisation - a brief overview
Dan Silcox
Dan Silcox

Posted on

Performance optimisation - a brief overview

Introduction

Earlier in the week I was chatting with a colleague about the HUGE topic of performance optimisation and what can be done to get the most throughput from a system in the most efficient way. I thought there were some interesting points in our discussion and so decided to turn it into a post.

The first thing to mention is that I am not specifically a performance engineer, so there almost certainly will be things I've missed or misunderstood. Also, this whole concept can take years to perfect and with the ever-changing tech landscape, there are always new ways to screw up performance, or to improve it.

However I believe that the approaches mentioned will help scalability and/or performance in most contexts, tech stacks and environments, so with that said, let's dive in!

Get the basics right first

There are a few key things that I believe will make almost any software perform faster and more efficiently.

Only do work which must be done

The first step when optimising is to eliminate altogether anything which you don't need to do - this is sometimes clear cut (e.g. removing a block of old code that has become redundant) and sometimes less so (e.g. knowing what level of logging to output in which environment)

Minimise what has to be handled at run-time

When I say run-time I'm not just referring to runtime as opposed to build time, I simply mean the time during which your customer/app user/consumer is waiting for the app to do what it needs to do. This could mean outsourcing background operations to a separate background worker, parallelising API calls where possible, pre-warming caches or any other number of things to prevent users waiting for things to complete.

Watch your loops

By definition, anything inside the code block of a loop will almost definitely be run multiple times, so you need to be aware of what steps that loop is doing and what they are likely to cost (in terms of CPU calculation, memory usage, time, etc - as opposed to direct financial cost). Generally, loop-based optimisations can be expressed with big O notation - in simple terms:

  • O(1) means whether there are 5000 items or 1 item in the collection you're looping through, it will take the same amount of time
  • O(N) means the time is directly proportional to the number of items, e.g. the loop will take 5000x longer if there are 5000 items than if there is 1 item
  • O(N^2) means the loop will take the square of the number of items to run (in relative terms) - so if looping through 5000 items, it will take 25,000,000 times longer than 1 item!

Where possible, minimise putting "expensive" things like database queries or API calls in a loop if you can; perhaps the API supports batch requests, or perhaps you can convert your many similar where ID = ? queries into a single where ID IN (...) query.

Nested loops also have a reputation for being particularly nasty if you're not careful. The number of "nestings" are effectively the power by which we multiply N in our "big O" function - e.g a single loop nested in another loop (2 layers) is O(N^2) whereas 3 layers is O(N^3) and so on. That's what I mean when I say they are "particularly nasty"!

Cache me where you can!

Wherever appropriate, consider caching data, to avoid further "downstream" calls - bear in mind that cache invalidation is one of the hardest things in computer science, so you need to be ready to manage that increase in complexity if you add caching to your application.

Test, test test!

There is no silver bullet, or magic way to improve performance - so you have to validate every change you make to ensure it has, you know, improved performance...

Start with a base line, no matter how bad it may be - and then re-run the exact same test after each change to see if things have improved - if they have, great! If not, you can go back and try something else...

Important note - you cannot simply test the response times and throughput - you have to actually validate that the app still does what you think it does! e.g. I did some performance tests once where the response time literally went from 200ms to about 30ms and I was over the moon - until I realised that for some reason it had started returning 500 errors which was why it was so much faster!

Architectural perspective

It's not only at the actual code level that optimisations can be made - also look at your architecture. Should you be looking to scale up or out?

Could you split out some functionality into a separate service and focus your scaling on sub-components rather than blanket-scaling the whole app?

Would it even make sense to rewrite your code in another tech stack for your context? Rarely is the answer to this last one "yes" - but occasionally you might want to prioritise a certain attribute of a language (e.g. a faster "cold start" from, say, Node.JS vs .NET Core for an infrequently used but time-critical AWS Lambda function, or better async support for parallel background API calls from Go, vs PHP).

Scalability

It should be noted that there's a difference between performance and scalability, though they are inextricably linked. 12 Factor has some great tips for highly scalable web applications and many (though of course not all are relevant) of the approaches can be tailored to "non-web" apps too.

Summary

I hope this has been useful, of course it's really only scratching the surface and there are countless other more specific / detailed approaches that can be used.

Is there something in particular that stood out to you? Or is there something you feel I've missed? Why not share it in the comments, let's have a discussion and I'm sure we can all learn something!

Top comments (0)