Optimizing Load Time in Ignition Perspective

37 min video  /  30 minute read
 

Speakers

Elizabeth Reed

Senior Manager, SCADA & MES

DMC, Inc.

Casimir Smith

Project Engineer

DMC, Inc.

How can you ensure that screens load fast and actions are snappy when using Ignition Perspective to create bigger and better projects? Learn how in this presentation, which will discuss strategies for optimizing screen development, organizing nested views, and analyzing Perspective execution. You’ll also get a look at simple rules-of-thumb for bindings, complex custom svg components, and where to strike the balance between performance and maintainability.

Transcript:

00:00
Moderator: This session is "Optimizing Load Time in Ignition Perspective," and I'll be your moderator today. To start things off, I'd like to introduce your speakers today. This is Elizabeth Reed and Casimir Smith from DMC. As the Senior Manager of SCADA and MES at DMC Elizabeth has extensive experience architecting and implementing Ignition SCADA systems with a background spanning industries such as energy, automotive, chemical, defense, and food and beverage. Elizabeth specializes in optimizing system performance and efficiency. She leads teams to create innovative high-level architectures that streamline operations, reduce downtime and maximize performance and productivity. Joining Elizabeth is Casimir Smith, a project engineer at DMC. Casimir is one of DMC's leading experts in Ignition SCADA and MES systems. He has developed cutting-edge Ignition and Sepasoft solutions in industries ranging from battery manufacturing to automated farming. Project highlights include designing production and planning maximization algorithms, MQTT Unified Namespace data models and user-configurable dynamic HMI screens. He is always looking to push the boundaries of what's possible in a Perspective interface. Please help me welcome Elizabeth and Casimir.

01:41
Casimir Smith: All right, let's get this show on the road here. So today, as we mentioned, we're gonna be looking at how to optimize performance within Perspective and to lay the groundwork, I want to first ask kind of what performance, what does it mean in our context? So there's a couple of different types of things someone can mean when they say performance, but we're really gonna be looking at this first one: perceived performance. And that really is how long do paid things feel like they take to load? What is that kind of initial on page load? There's some other things that are equally valid, but we're not going to be focusing on here, which is kind of responsiveness. How long between when there's an action and when the result of that action happens, as well as resource usage, right? What's your CPU memory load on your gateway? So we'll be looking at perceived performance here, and we really...

02:40
Casimir Smith: This is why we're here, right. You've been there, I've been there, tapping, waiting for your screen to pop up. Going to click a button and then having something replace the button that you were about to click. Sitting there wondering how long will this loading bar be? And this is what we're trying to avoid. This really is how can we make our user experience better and how can we make things snappy and make them feel good to use. To do that, we've got a whole host of tips and tricks here on how to design your screens and analyze your screens to make them as performant as possible. We'll be splitting these into some fundamental techniques here. Moving down the list, reducing the amount of data and calculation you have, reducing the layout recalculation that the browser does, reducing initial load actions and reducing heavy weight components in your screens. Those are some nice kind of fundamental tips, and if you have tried all those and you're still needing a little bit of help, we also have some advanced techniques here like analyzing the loading, using some browser tools, and also how to hide the loading and make things feel better than they might actually be.

04:01
Elizabeth Reed: So starting off, we're gonna start with a fundamental topic for reducing the amount of data and calculation that you're doing in the backend of your screens. So, one big thing with screens is bindings. We use them a lot in our screen development. Those are things like bindings and transforms, and there are some bindings and transforms that are more efficient than others. For instance, a direct or indirect tag binding are the most performant options versus a tag expression or a runScript expression. On the transform side of format or a map transform are the most performant and expression transform is medium and script is the least performant. And so, really, we want you guys to know this information and so you're able to make informed decisions when you're programming screens. If you could do the same functionality using a more performant binding or transform, you're gonna see a more performant system overall. And here's an example of a expression binding that is using three different tag expressions. Here on the right you see an example of the same functionality, but instead of using a tag expression, you are using multiple indirect tag bindings where you have three different properties that are using a indirect tag binding, and then you have an expression that is linked to the property that's displayed on the screen that is looking at those custom properties there. So, a great example of getting the same functionality but in a more efficient manner.

05:32
Elizabeth Reed: The next thing I wanna talk about is query efficiencies. And so this is most relevant for screens that display large amounts of data that you're querying from a database. One kind of basic tenant is always include a range limiter on queries that select from large tables. And so, this could be either time based or counter based. And really this is important, especially as a system is used over time in scales, you don't want unbound queries that in two years past deployment, all of a sudden your screen locks up because you're just returning too much data to that screen. So, keep the long-term vision in mind and always include some range limiter so you don't run into that issue in the long run. Next one is avoid polling. And so, if you are returning large data sets, you probably don't wanna automatically refresh that every 30 seconds, 60 seconds.

06:22
Elizabeth Reed: It's much better to instead have a refresh button to refresh that query instead. Obviously, this is not as optimal from a user point of view. Users expect automatic refresh and so use it sparingly in situations that it's needed with an especially large data set. The next one I wanna talk about is named queries with caching enabled. Named queries are a great tool that are better. They are a more optimized way to query the database. You can cache the data. They also can save the execution plan. And so it takes less time on the database side to compile the query. And they also just have better diagnostics for troubleshooting as well. So big fan of named queries. And then the last one here is with any large queries, you might be doing a complex query with some joins and things like that, and it might not be something on the Ignition side that's the problem. It might just be a really complex query that you need to troubleshoot in the database itself. And so there are a lot of great database tools that you can leverage in order to troubleshoot the query and write a more efficient query. On the MS SQL side, there's things like the query store execution plan monitoring, and in Postgres there's third parties like pgDash or a pgWatch that you can use to help troubleshoot and do more advanced database query efficiency improvements.

07:48
Elizabeth Reed: Next thing is scripting efficiency. So, scripting is great, we all use it a lot. Ignition is super powerful and because of all of the scripting capabilities that you can do, but you know, don't script unless you need to. It does add some complexity that could increase the load time of your screens. So just be cognizant of that and balance functionality versus performance. Obviously, avoid duplicate logic in a loop. And something that we still see around every once in a while is people using individual bulk tag reads throughout a single script as opposed to just doing one bulk tag read and then referencing the values throughout the script. So, just a reminder, I'm sure we all know, try and condense your bulk tag reads as much as possible into as few bulk tag reads as you can.

08:39
Casimir Smith: Alright, well Elizabeth talked a lot about how you're gathering data in your Perspective session, and that is really great when you're looking at your bindings and your scripts and your queries. But there's a whole other side of this for how the browser takes the components and the containers on your screen and how it generates a viewable page off of those. And that's what we'll be looking at. Next is the frontend side for reducing layout recalculations. So when we talk about how the browser renders a page, there's two concepts that I think are very important for Ignition and they are reflow and repaint. On the bottom here we can see a chart. It's not that important to understand the whole thing. We're starting with our raw HTML and CSS and in the end we have two steps here, our reflow and then our repaint. And reflow is anything that is recalculating the position and geometry of things on your page.

09:39
Casimir Smith: And for the browser, this can be an expensive process if you have to do it over and over again. In contrast to repaint something that affects the visibility but not position or layout, like changing the color of a component, that can be relatively quick. So if you have the choice between when something is alarmed to change its size or to change its color, often changing its color can be a way to reduce load on the browser. And really we just want to avoid bindings that alter that page layout over and over again. That can be things like a binding that changes the basis of something or the display property of something or its size that really is what, when overused can cause strain on the browser. So, we'll take a couple tips on how to reduce the layout recalculations here. One that is a bit of a niche topic, but one that I think a lot of people could use easily is persistence.

10:38
Casimir Smith: When you put a binding on a property, its persistence will be automatically set to be false here. And what that means is that instead of starting with a value, when the page loads, it starts to null when it loads until that binding loads and evaluates. And when you have something starting at null and then getting a value, that can really shift around if that property affects the layout of your page. So, oftentimes we like to set that persistence back to true, give our property a starting value, and then that can help us have our page initialize and not have to recalculate as much when the binding evaluates. And that can avoid some of that pop-in on screens that can also avoid some of those red overlays on initial load. Another thing that we see all the time, again, a favorite tool of people that can be overused is nesting embedded views.

11:39
Casimir Smith: And so, this animation on the right here shows just each level down is one extra embedded view deep. And I don't think anyone here is using eight embedded views nested deep, but there is even a real difference between something like two versus four nested views deep. So we try to avoid nesting more than three layers deep. We try to pass in things like tag paths instead of passing in large data structures so that we use our indirect tag bindings on the very deepest level and we try to play around with both with-parent and after-parent loading behavior. Unfortunately, there's not one clear answer on which one is better between this, it really depends on what you're looking to achieve and the structure of your view. The last thing I'll note on this is that this is a case where performance can sometimes contradict or compete with maintainability. So, if you have one core embedded view that gets used across a lot of templates, it can be really nice to use that one extra layer of nesting. But that's something where you need to balance it between do I want to have this one core embedded view and maybe suffer a little bit on performance, or do I want to spend that extra gain, that performance gain but not have to, or but have to maintain the same thing in a few different places?

0:13:09.6
Elizabeth Reed: All right, the next fundamental topic we're gonna talk about is reducing initial load actions. Now, what do I mean when I say reducing initial load actions? This is when a user pulls up an HMI on their screen and it first loads. So, how do we make that initial load happen faster? So the first thing is talking about calculations that might be intensive and moving those to a gateway scope. So, on the left here you see a more traditional architecture. The user opens a view, maybe there's a binding that has a script transform on it, that script transforms running some pretty intensive data calculations, and then it's writing that value back to a property that is then being displayed on a screen. So, this is executed every single time a user opens a view. And so if you know five people have that screen open, it's executed five different times and it is triggered based off of when the screen is opened.

14:06
Elizabeth Reed: So after the user navigates to that screen. So, an alternate way to do that that is a little bit more performant is moving that calculation instead to a gateway scope. So, thing like a gateway timer event, that gateway timer event will run the script to get the data, it'll then write that data to a tag. Then from the user point of view, they open the view, they read the tag from an indirect tag binding and they write the data back to the property. And so, it's a lot faster. A general note on this is that you can't use this in every single situation. For example, if you have a filter on the screen that the user puts in that alters the data calculation in any way, that would need to be client specific. And so, you'd still want to do that more traditional method where you're running the script directly on the screen. Maybe there's a way you can get that done still in the gateway scope method. But just know that there are some instances where you'd still want the other method there.

15:07
Elizabeth Reed: And then what does this actually look like? So right here we have the traditional workflow. The user opens the screen, there's a tag binding that's linked to the current tag here. And so, this is linked right there. We're running a script on this binding here that looks at the tag history and calculates the average over it. You can see there's about a two second delay between the tag binding loading here and the run script transform loading right there, which is pretty significant difference in load time. On the other side of it, this is using a tag right here. So still you'll have the average or the value down here. Instead we have an average value that is being written to by a gateway script that is running the same script that we ran on the screen, but at a gateway level. Then when you load the screen, it's instance, the difference between loading the current and loading the average is the exact same, which is why we do not have a GIF because the GIF would be instant for both of them. So not a very good GIF.

16:11
Elizabeth Reed: And then the last fundamental topic we wanna talk about today is reducing heavyweight components. So, Ignition has a lot of really great built-in components that we all use all the time. They have a lot of functionality. Both all of that functionality, they sometimes are a little bit heavier to load than a lighter weight component. And so, kind of a bare bones thing you can do if you're before Ignition one or 8.1.31 upgrade Ignition, in that version, they introduced React 18, which is much more performant. And so if you're seeing some slow load times, highly recommend upgrade to the latest version, you'll get react 18 and probably see some improvements there. So, that's a easy low hanging fruit improvement. If you're still seeing issues you can consider things like a custom lightweight component. And so this is a embedded SVG graphic.

17:05
Elizabeth Reed: And it's really helpful in the use case where you have a lot of repeated components that you're using on a screen that take a while to render. And so, the Ignition Exchange has a lot of really great examples available. Things like gauges and charts that you can use as a starting point. And so, although you are building a custom component, there are still some good resources that you could use to decrease your development time with the Ignition Exchange. And some particular heavyweight components that we, in the past have replaced with embedded SVG graphics or the XY chart, the gauge and pie chart and the markdown. Especially the XY chart, it is such a powerful component and it has so much functionality built in, but if you're displaying five, 10 of those on a screen, it could really add a lot to the load time. And so, in instances where you don't need all that functionality, taking some of that functionality out using an embedded SVG graphic instead can make that screen a little bit snappier with the load.

18:11
Elizabeth Reed: This is an example here of a, out of the box gauge Ignition component. We have it in a Flex Repeater with a 100 different instances of it and you can see how slow the load time is. It's pretty significant the amount of time it takes to load all a 100 instances of this. To counter this, this is the lightweight SVG component. And so it's obviously a lot faster. It does have less functionality, but for what we're using for it in this dashboard, it still is able to get the same data across. And again, just to go back to really emphasize the difference in load times here, the heavyweight out of the box component and then the lightweight SVG component here. So this is a great example of when you care more about the fast load time versus maybe some of those added functionality of the built-in components.

19:13
Casimir Smith: All right, well that was a great look at a lot of the kind of tips and tricks you can use while designing Perspective screens and projects. If you have gone through every single one of those and you're still wondering what is going on, you might want to look at some of these advanced techniques here. So we've got two that we're gonna talk about today real quick. First one is analyzing the loading in the browser using a browser tool like DevTools. So this can be really useful if you want to create two variants of a page and compare them against each other. Or if you want to look behind the scenes at how the browser renders your page to figure out why is it still slow, even after I've done all of these other optimizations. Next we'll also look at ways to cleverly mask or hide the loading within Ignition. This is if you have really optimized as much as possible, but you still have pop-in or screens that look strange when they're loading in. Or if you want to improve the page feel, regardless of what level of loading times you have. So to start looking at analyzing our loading, we are going to talk about Chrome DevTools. So a lot of browsers have tools like this. Chrome is just one of them that is very popular. You can get to it by just opening up Chrome.

20:38
Casimir Smith: It's built in. You don't have to do anything else. And opening up your screen, and then pressing either F12 or Control-Shift-I, or Inspect Element if you're used to that. When you do that, your screen will get smaller. You'll have tons of other information and controls on that top and side. And you can use this to test a lot of different things. One of the basics that we use all the time is not strictly related to performance, but instead to responsive design. And that is to change the dimensions of your screen, either just custom dimensions or pick a certain device. And that can test the mobile friendliness of your design or how it looks on different devices. Another one that we don't see as much is right next to it, the Throttle drop-down, where you can test how your screen might perform under different network loads. So you can really simulate what a slow client or a bad network might look like. The star of the show, though, in our minds, is over on the right here. We'll just look at the performance monitor and how you can use that to take a quick look at what's going on behind the scenes. So we've got an animation here.

21:56
Casimir Smith: I'm gonna let it run through once and then look at it kind of step by step. But again, we just need to open up DevTools, switch to our Performance tab, and then we can start recording, play around with our screen, and then stop recording here. And I'll go with it on this next one. So we can open up DevTools with F12, go to the Performance tab on the right, click that Record button to start recording, and then navigate in our page as normal. So we can click around, go to multiple pages, even do button actions and other things, and it will be recording that whole time. Then once we stop, it will generate an analysis of our performance that we can take a look at. And when it generates that analysis, there's a lot of stuff going on here, and it can be a little bit overwhelming, but there's a couple key things that we can take away from it. Starting at the top, we have the timeline. So that is where you can see the entire time that it was recording, and what the browser is doing during that time, and what the load of the browser is.

23:07
Casimir Smith: This also is very key, because you can highlight over it if you drag your mouse over it, and that really works well with the screenshot on that right side. So the screenshot is where if you hover the timeline, you can see the page and what it looked like at a specific time. Using those two tools together, you can connect the quantitative measurement of what the browser was doing at a given time with your qualitative measurement of what the page looks like while it's loading.

23:38
Casimir Smith: And you can really connect those two things and kind of unify them in your mind when you're doing your analysis. One other thing to look at is that process gauge at the bottom left. So, that shows just what time the browser spent doing certain actions. And because JavaScript and the browser is single threaded, it's only doing one thing at a time. So you can see when it is spending a lot of time doing that scripting, and when it's spending a lot of time doing that rendering, which relates to the reflow stuff that we talked about before. So, those are some really useful tools. There's also the stack trace, which in my mind is a bit too complicated for someone if you're just looking at how to analyze your page. So we tend to skip over that part and focus on those other three items here in the performance monitor. Lastly, we're going to look at how you can hide the loading. So how can you either incorporate animations, loading bars, other tips and tricks to make things feel better. And really, that's the key here. A user does not have a stopwatch when they pull open your SCADA screen. Things don't need to be fast, they just need to feel fast. And some delays are unavoidable. Maybe you have a Sepasoft script.

25:00
Casimir Smith: Maybe you have a remote tag provider binding, which has a one second baked in delay. So there's a lot of things that you need to work around. And some strategies, we're gonna look at avoiding overlay flickers with persistence and hiding them with fade animations. So, persistence, this is a quick one. We talked about it before, but again, this sets a starting value, a temporary value, while the binding loads. This is an example of what a screen might look like if there's not persistence. While all of those bindings are loading, this is in the case of a remote tag provider, we're getting red overlays while it waits. And that can be a little bit annoying. But if we have a persistent value where it defaults to a known value, let's say zero, it takes the exact same amount of time to load, but you don't have those scary overlays, which again, makes things feel a bit better. But we'll go one step further, and we can look at making a style class that animates our fade in, and can give our pages a really premium feel. We'll be releasing this so we don't have to copy down every single part of this style class definition, but what we're doing is we're creating a class that animates from zero opacity to one, and we can make multiple different classes with different delay intervals. The end result of this is a screen that can look really premium.

26:30
Casimir Smith: Wow. And it looks a lot like some of the more advanced web applications that you might be used to, where you have a skeleton that loads in, and then your applications load in on top of it. And so, to me, this can be a really nice way to make your loading feel like it's a process, and feel like it is not just slamming in all of our components on top of each other, but that we have a structured, nice way for things to come in, and the user oftentimes will like this, even if it's not any faster.

27:07
Elizabeth Reed: All right, so we talked a lot about a lot of different tools that you can have in your back pocket when you're trying to optimize the load time of your Perspective HMI. So, to kind of summarize the fundamentals, we talked about back-end and front-end improvements, including optimized bindings, transforms, queries, and scripts. We also talked about the toll of browser layout recalculations and how to avoid them, when to have scripts run on the screen versus a gateway event, and how to use embedded SVGs to create lightweight screen components to replace some of the more heavyweight, out of the box embedded ones. On the advanced side, we talked about some advanced loading analysis tools to have in your back pocket. And we also talked about how to hide loading when you tried all other optimizations and you're still seeing some lag in your system. So these are just the ones that DMC has found most useful in our development. I'm sure there's many others that you guys also have used in your development. But one thing I do want to emphasize with any code development, it is always important to balance performance versus maintainability. A very famous quote is: "Premature optimization is the root of all evil." So, don't try and solve a problem that doesn't exist.

28:19
Elizabeth Reed: Gather user feedback, then consider optimization. And many of the topics we went over today, it might introduce custom or duplicate code that makes it harder to maintain. Going back to a point that Caz made about the nested embedded views. Sometimes you want to use that library object, three or four layers deep, because it's easier to maintain and you don't care about the performance hit you're gonna take with that. So, just a reminder, don't go ahead and use all the tools we showed you today just because they're snazzy and nice. Do consider the overall architecture and the performance of your system before prematurely optimizing. And that is all we have. If you have any questions, feel free to contact us and also be Q&A.

29:07
Audience Member 1: I have a really quick one for ya. How many seconds of page load do you go, okay, no, we just need to start optimizing? Two, three?

29:18
Elizabeth Reed: Depends on the client. I mean, like some customers don't care about like a fast load time. Sometimes it's just an HMI that is on the production floor and they never move away from that HMI. So, is a fast load time as important for that application than somebody that's clicking around a lot, probably not. I don't know, what would you say?

29:43
Casimir Smith: Depends on the screen, right? If you have a screen that you use once a week, is that as important for it to load fast as a screen that you use once an hour? But I think for those snappy ones, really you want it to be within one, two seconds, but for the longer ones, that's when you start to see different needs.

30:05
Audience Member 1: Thank you.

30:08
Audience Member 2: So you talked about embedded views, and I agree it's not a good idea to use them, but what about the actual components and the... When I'm nesting components, if I use Flex container, I can get eight, nine layers deep, or I can just throw the XY container and have just one layer deep, basically. So what's better? Is there any performance penalty as I'm adding more layers to the component hierarchy?

30:39
Casimir Smith: So a couple of notes on that, in terms of balancing the niceness of the Flex container versus the performance of the XY. As far as I'm aware, at the current time, the XY is more performant, but the difference, especially in later versions of Ignition, is not a large difference. For us, we tend to focus on using nested Flex containers because of the added benefit for maintainability and readability. But there are some times where we will use those coordinate containers. I would say that's a case where the maintainability of the Flex container outweighs the performance of the percent XY container.

31:33
Audience Member 3: Is there a performance difference between buttons versus SVG, and especially if you need events or actions to be triggered off of the SVG?

31:42
Casimir Smith: Are you referring to where the SVG has individual zones for clicking within it, or just the entire thing as a whole?

31:52
Audience Member 3: Like your example with the hundred different things, if they needed to be able to click on those SVGs, versus just show some text on a button, what would be more performant?

32:07
Casimir Smith: So, an SVG with a click event, as opposed to a button with no graphics and a click event, the button is going to be faster. Because it doesn't have to pull up that SVG rendering engine, but it's not gonna show the chart, of course. Right, so anytime you can simplify, that is better. You don't have to bring in a rendering engine, you're just showing text, that is going to be faster. And that is kind of the same approach between why an indirect tag binding is better than an expression binding or a script transform. It's the system having to load in extra frameworks to use.

32:47
Audience Member 3: Okay, and especially because that's a standard component as for web, web technology is easier to generate, but as for the event piece, is there any overhead hit by triggering an event off an SVG versus a button?

33:03
Casimir Smith: I'm not aware of a significant performance difference between a click event on a non-button versus the on action performed on a button.

33:12
Audience Member 3: Okay, thank you.

33:21
Audience Member 4: Are there any approaches to reduce the initial Ignition load, especially where it's there saying loading resources and just doing stuff? Is there anything that can be done to reduce that initial Perspective load?

33:39
Elizabeth Reed: Good question. I don't know. I mean, that's like loading all your resources in your project, so probably not, although we didn't do a lot of research on that one. You have a better answer?

33:54
Casimir Smith: I don't. I know that it's module dependent and things like Sepasoft or other third-party modules can introduce more loading at that step, but in terms of do the components on your page affect that initial load, we'll have to do some research and get back to you on that.

34:18
Audience Member 5: I pretty frequently make use of the global script library for very repeatable scripts to retrieve data or format it somehow. Have you noticed a difference between a script binding and calling a runScript for whatever you need to do when a page loads in?

34:38
Casimir Smith: So, in terms of a script, are you referring to the runScript expression?

34:44
Audience Member 5: Yes. So, a binding calling runScript with a saved function off in your script library versus a coded on the binding, script binding.

0:35:00.8
Casimir Smith: We tend to avoid runScript expressions whenever possible, especially in older versions, but I think still in newer versions. RunScript tends to be worse than other ways of calling scripts on a page, like events and even, I think, script transforms. But in terms of calling a script from your project script library versus trying to run it on the action itself, we will always recommend having a script library no matter how small it is, because that just makes maintaining your code that much easier. Don't give him the mic. Oh, no.

35:42
Audience Member 6: I can't let that one go by. I've actually tested the performance of runScript versus script transforms and documented it on the forum. RunScript as the primary expression operation is somewhere on the order of hundreds of microseconds faster than passing a value than running a script transform when either one of them is calling a project library.

36:09
Casimir Smith: Thank you a lot.

36:10
Audience Member 6: RunScript blows the doors off of script transforms.

36:17
Audience Member 7: And as a farther note, if you turn them into classes instead of functions within the Python library, it loads faster as well, because of how Java handles classes. They're pre-loaded instead of at runtime.

36:33
Audience Member 8: Well, I have a question, if nobody else does. So DMC is competing in the Build-a-Thon. Are they gonna win tomorrow?

36:40
Elizabeth Reed: Fingers crossed. It's our redemption from two years ago.

36:44
Casimir Smith: That wasn't in my bio. It was Build-a-Thon runner up.

36:49
Elizabeth Reed: We didn't bring the race suits this year, though. So we were tired, though.

36:55
Audience Member 8: Thank you.

36:58
Elizabeth Reed: Alright.

Posted on November 27, 2024