Embedded first development principles
New to Web development on an Embedded device? These set of simple guidelines are created to help you be more successful!
What is an embedded device?
An embedded device is an object that contains a special-purpose computing system. Typically with a System on a Chip (SOC) processing unit that integrates all or most computer components into 1 integrated circuit. Limited in processing resources and with a much different life span then your typical consumer PC or mobile phone.
Examples of an embedded device are: A smart TV, a TV set top-box, a game console, a in-car entertainment system or a smart washer-dryer.
Why does it matter?
Web development is typically done for PCs, mobile and tablets. All 3 of these devices are very powerful compared to embedded systems and have a relatively short life span (how old is the phone you’re using right now?). Ensuring they are refreshed often, bumping performance a long the way. Using a browser / JS Runtime is fantastic as it abstracts all the low-level primitives of writing software on a bespoke device. Making it almost transparent to whether or not your WebApp is running on a desktop or embedded device.
However the 1 key difference is the physical resource limitations. Embedded systems have far less processing power, less memory and tend to have a longer lifecycle. Meaning you’ll have to work with much older hardware then you are typically used to. In order to deal with these differences let’s talk about a few development principles that help you or your development team to be more successful on an embedded project!
The principles laid out
- Know your target device
- Be mindful of resources
- Measure often, cut once
- Take great care with external dependencies
The above rules should be constantly kept in mind when working on an embedded device and this requires a slightly different mindset than developing for mobile or PC targets. Realizing too late that your WebApp is resource hungry or that you’ve pulled in an external library that has aggressive processing can be disastrous for your timelines and will diminish your success.
Know your target device
Like an author for a story, you need to know your audience. Although in this case your audience is a device, you need to be familiar with the device. Understand its limits and be able to cater for that in your project.
Having a test device is required!
No device, no service! No seriously, you can’t be successful without having an actual device to test on. This needs to be the lowest denominator in device performance to begin with, as its easier to scale up in performance than it is to scale down. Anyone that tells you don’t need one is wrong and feel free to push back on this. Developing on a reference device without having the actual real device will cause issues down the road. You can’t solve bugs you can not reproduce, you can’t prevent performance issues that you can’t detect early on.
You need to understand:
- How much memory does the device have to run my application in? (ie. Linux system memory for the browser)
- How much Graphics memory does the device have to run my application?
- What are the processing limits, how much can it handle in parallel?
- How is the rendering performance?
This will drive how much animations, images, effects you can apply in the screens you are developing.
Does that mean I only need a slow device?
Starting with the slowest is good for a low-end performance baseline. However having a mid or high end device is recommended too, typically you want to see if you can scale up the animations, effects and handling of the user experience as the device has more resources available.
Additionally having a faster device means the timing of how data/images/screens are processed changes and its key to ensure those are in a good working condition as well. Be wary though to not only test on the high end devices it’s very easy to never look back. I get it, it’s very smooth and sexy compared to the low-end devices and therefor much more pleasant to work on. However the low-end device is where the troubles will be, lose it out of sight and you start creating issues you won’t see on the other devices.
Solving performance or memory related issues can be painful, especially when you catch those very far down the development cycle. Being able to catch them early means you’ll be able to more efficiently deal with them.
Be mindful of resources
The main crux with embedded devices are the availability of resources. The embedded system just has way less CPU processing power way less memory to run the application than what you are used to. In fact it is so hard to express the performance difference between a 2019 MacBook Pro and a 2019 Embedded 4K SOC STB that they’re not even on the same scale.
To compare a '19 4K SOC Embedded STB get’s close to an older iPhone (though iPhones get very fast GPUs at some point, PowerVR is fantastic). But in terms of CPU processing they rate equal to a iPhone 6/7 with only a fraction of the available memory. Your typical available memory to the browser JS engine is less than 500 MB and the available graphics memory is 200 MB or less.
Imagine developing for an embedded device as if you’re building something for a relatively old mobile phone, limited in CPU processing with very limited available memory. This means that the code you write, the dependencies you chose to use and the way you handle your data/images on your project need to be smart about the impact on the device’s available resources.
This varies greatly from developing for modern mobile phones or desktops, where the resources are nearly infinite and you never really worry about the memory impact of your project. You trust every framework out there will do its job without even hesitating to think about their footprint or CPU impact.
JavaScript is very forgiving with memory leaks and inefficiencies. On desktop/mobile this can usually be handled with the large amount of resources they have to mitigate those. Unfortunately an embedded environment is much less forgiving and you will need to measure, hunt and solve those memory leaks early on. Keep the code footprint light and be embedded smart about allocating resources for your application.
Measure often, cut once
Measuring is knowing! It’s important to measure the performance / overall health of your project often on your target device. Establish a baseline early on as soon as screens start coming together. Seeing a natural rise in memory / graphics allocation as more functionality is added. However having that baseline will help catch outliers early and deal with them swiftly.
Pulling in a new dependency? Perfect test to check what the performance measurement impact is, let’s test!
Adding new animations/design aspects? Let’s validate if they don’t affect the performance negatively.
Having a baseline is also important for feedback on the project. Getting sudden results that the performance is low but you haven’t changed that section of your project? Maybe the firmware changed or someone introduced a GPU driver bug? Less likely but if you have a baseline and know what your changes are it makes it a lot easier to pin-point sudden changes in performance.
How often should you test & measure?
This is entirely up to you, at least once for a release is the absolute minimum to continue to establish a baseline. But testing every single change is excessive and will slow the development cycle down. Try to find a balance between testing on your development machine (laptop/desktop) in Chrome and when its a good time to measure the performance on the device. Personally I try to test once every big feature/change, which can vary to once day or at the end of a sprint.
The worst possible scenario is a waterfall approach where stuff is designed, implemented and only measured for performance at the very end. This is an absolute nightmare as trying to find what code is responsible (if not all of it) for the low performance. Its very much like finding needles in a haystack. It’s important to measure frequently, at minimum every release, preferably every sprint and start measuring as early as possible in your project.
It is important to get a feeling for when it’s a good time to test. You may want to hold off testing on a device until you reach a certain maturity of the code that you are writing. After all what’s the point of testing something that will be changed a lot anyway?
Though once the code starts settling in and your app/feature or section you working on is getting established. It’s probably time to give it a spin on your low-end target device to see how it holds up.
What should we measure?
Probably Frames Per Second is the most important metric for performance. Lightning has a built in FPS counter then you can use, find it here.
On top of the FPS you’ll want to keep an eye on:
- Average memory usage
- Average graphics memory usage
- The average CPU usage
Those values will fluctuate as you navigate around and can be within bounds of the baseline with a certain % margin. Be on the lookout for sudden and prolonged high spikes on the CPU combined with a FPS drop. This means you’re blocking the CPU somewhere and it is affecting the performance.
Make sure the memory usage stays stable, it can fluctuate but should never indefinitely increase. If so there is a memory leak somewhere and it is time to start hunting that leak down. Having previous baselines/test results help tremendously in narrowing down which change set introduced the leak.
External dependencies
Web Development or writing applications in JavaScript is great because there are loads of frameworks, tools and libraries out there that you can use to speed up and ease your development cycle. However if your target device is an embedded device with limited resources you may want to be weary of the impact of your new shiny dependency on the device’s resources.
Most popular frameworks you find on the internet are written for desktop or mobile. That means their target device is much more powerful than yours and you need to understand that may impact you indirectly. For example they might create loads of objects/functions during runtime to provide a full blown fully featured framework, however by doing that they also take a large chunk of your available memory. Similarly they might have aggressive timers or intervals to clean their caches or do their processing that otherwise work great on a desktop/mobile but hog the CPU of your embedded device.
Once aware you need to consider a few things:
- Test & measure it! Take the new framework/dependency, load it up with a sample application and run it on the device. How does it hold up? What is the footprint of the library? Does it leak memory? Does it fit in my project?
- Do I really need it? Or is it just “convenient”, yet brings a lot of risks? You may want to find another project or approach.
- Does the author care about embedded usage? Most of the frameworks don’t, but some get very serious about processing power/memory consumption.
- Do other projects use it successfully on embedded devices? Helps identify risks and experiences by other teams.
I’m not a developer, how can I help?
The above guide is mostly written from a Software Developer perspective, but it surely isn’t limited to a developers role!
As a Team Manager ensure your team is well versed in working on an embedded system. Find an appropriate training to get newcomers or low-experienced crew members up to speed! Enable your team by making sure there are enough devices available for your team to develop on.
As a Project Manger schedule time for validation, account for separate performance exercises and do not discount the importance of such tasks. It might seem like an easy thing to defer over “features” for the sake of making visible progress. But it will come around and bite you in the end and in my experience will take longer to solve then it would’ve taken you in the first place when you made the choice to defer. Be strict about it! Report the steps in improving the performance as a way of reporting progress (over just features) e.g. we’re now running stable at ~45 FPS (up from ~15 FPS) on the lowest device is an otherwise very notable achievement. Make sure you have a STB as well, run the app they are developing and see for yourself what the output of your team is.
As a UX designer team up with your software developers to help optimize the animations and effects as such that you’re both setup for success! If you are one of those UX Unicorns that designs & develops then more power to you. Otherwise it’s great to get feedback from the SW Development team and see if minor changes in your design yield a higher performance and try to find that balance between eye-candy and high performance/FPS.
As a QA engineer oh boy! Measure that performance! Enable the FPS counter, record the output, document the average FPS/memory usage/CPU usage over your test window and store that for every release. Create graphs/overview of how the performance changes per release and challenge your SW:Dev team!