Lazy Flutter performance | Session

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome to my session lazy flutter performance in this talk i want to argue that when you look at the performance of your app and the smoothness of your animations on the screen just as important to what slowing things down is what's just off the screen i want to look at lazily loading this content so that it's ready to go just when it's needed and how the flutter apis guide you towards doing this in a performant way by default out of the box so to explain a little bit with a real example of a complex app this here is the stadia app it's built in flutter and here we have a big list of games with some rich images and animations when the animation happens and we need that list to scroll at 60 frames per second but the hard part isn't what you see in the screenshot it's really just the tip of the iceberg the hard part are all of the assets that are lurking just under the surface ready to slow everything down these other items in the list need to be ready to go on the screen at any time that the user scrolls and it needs to do it at 60 frames per second this is a pretty hard problem but flutter makes it easy to solve it guides you towards writing a fast app but it also gives you the tools that you need to make your own custom implementations that are equally as fast so in this talk we're going to take a look at a few examples of all of the above so i am justin i'm a software engineer on the flutter team at google and i spend most of my time working on github contributing to the framework and helping move things along but one thing that i think about a lot in the scope of my work is how do we make widgets and flutter's apis both easy to use for the usual cases but also very customizable and powerful when needed i think this is a really hard duality to get right we spend a lot of time working on this at flutter and i think it applies a lot to today's topic with lazy performance so throughout the course of the talk we're going to look at three different examples the first is similar to the screenshot we saw of the stadia app we've got a one-dimensional list view that scrolls next we're going to look at a two-dimensional grid and how the same principles of performance and lazy loading can apply to different widgets like this and the third example that i want to look at is a fun one doing procedural generation to generate content randomly right when it's ready without having to keep any of that stuff in memory the first thing i want to do is uh try to reproduce the problems so i've got this marty mcfly animated svg asset here that i got from rive it's uh animated it's a little bit heavy so it's gonna help us slow things down and try this out and we're gonna put a bunch of these on the screen in a list so first of all we've got a red x here because if this is just the naive implementation of how you might do this in flutter so with a single child scroll view if we just give it a list of children it's going to render all of these flutters going to keep track of every widget that's created whether or not it's on the screen so let's take a look at the code and try running this so here's our list of marty's the marty animation is pretty smooth the scrolling also pretty smooth and uh we just have a short list there of 10 so let's bump that up to 100 and see what happens i'll restart it jump back to the app alright so i'm clicking to open that demo there we go so we're opening here we can already see that the marty animation is much slower and the scrolling is also very stuttery and we can see that there are a lot more items in the list uh this time with the gradient here so we are up to a hundred so if i bump this up even higher to a thousand and try that again here we go it's restarted and i'm clicking into that and then just as a spoiler here what's to happen is that this is going to wait for about 60 seconds it will either crash or it will eventually open the list of marty's and it will be completely unusable so i'm going to go ahead and kill that here and move on so here i have already taken a look at the uh profiling the memory impact for all of the demos that we did um 10 is roughly taking 15 megabytes 100 um in the range of 80 and a thousand crash and we couldn't even get grab that information at all so this is about what we'd expect between 10 and 100 we have almost an order of magnitude increase in the amount of memory being used because we have an order of magnitude increase in the number of assets that we're looking at minus a little bit for the rest of the application so now let's look at how we can do this in a performant way by using a simple widget that handles a lot of performance optimizations for us out of the box so we're going to look at list view dot builder and here we have a performant way of handling this in the code instead of a list of children we've now got an item builder and this is a function which is going to return a single item in the list and this allows our list view internally to call this only when it needs to render that item that it needs to display just in time so let's look at this in the code and try running it and see how the performance changes so here in the code now we've got listview.builder with an itembuilder instead of a child so here we're returning a single child which is the marty at the current index and back up to our item count again we're going to start here at 100 and you see it open quite quickly much faster than our slow naive demo with 100 marty's and the animation looks smooth and the scrolling also feels pretty good and you can see we've got uh a lot of marty's here we've got a hundred just like we said so let's bump this up now to a thousand and in the previous demo with single child scroll view we could not even run this but now that i've restarted let's see how it does with listview.builder and there we go it opened just about as quick as the 100 the animation still looks smooth and the scrolling also is pretty good if i get going really quickly there we go i can get a little bit of pop in there so we have some white screens as the asset is loaded because i'm loading all of these assets on their own separately for the sake of the demo and you can see how slowly the gradient changes because we do have 1 000 parties there so let's take a look at what was happening under the hood in both of these widgets so starting with the naive approach and the single child scroll view this illustration shows all of the marty's that were being kept track of by flutter off screen in black and white even though they weren't visible to the user and only the colorful marties in the center were visible we still had to pay the price for all of those parties even when the list was enormous maybe even a thousand parties long so with listview.builder what we did is chop off all of the extra marty's and we only rendered a few off-screen marties that were likely to be scrolled onto the screen in the very near future and this allowed us to get rid of the huge list of up to a thousand marty's and just handle the next few constant number of upcoming marties and so the performance that we saw there i went ahead and tried this out in the dev tools and grabbed the memory snapshot for each one of these and you can see that each run whether it was 10 100 or a thousand was taking just about the same amount of memory so that's also what we would expect because the number of marty's that were actually being handled by flutter didn't change with the number that were in the list because flutter was holding them just off screen and getting rid of all of the extras and shuffling them around as the user scrolled all right so we saw our one-dimensional list like the stadia app but let's take a look at a slightly more complex example using interactive viewer so here we've got a two-dimensional grid of marty's and what interactive viewer does is it allows us to grab the screen and pan around and view different areas of the screen that aren't visible at the beginning so this is going to have a similar problem that we saw before and the code is also going to look similar so in this naive approach here when we just pass a child to interactive viewer flutter is going to build that entire child sub tree in this case a big list of marty's rather a big grid of marty's and what's going to happen here is that all of these black and white marties are going to be handled by flutter just like we saw in the one-dimensional case we're handling way more than we actually need to show the user at any one time so similarly to how we improved performance by using the builder widget we can use an interactive viewer dot builder and have a similar kind of pattern here to improve the performance so with our builder function instead of having flutter handle everything for us interactive viewer is a little bit more of a generic widget it doesn't assume anything about the content that you're rendering it doesn't know that it has a grid so it's up to us to write our own builder in a way that's going to take advantage of any performance optimizations that we can make here we've got our builder with a quad representing the viewport so that's just four points in space that represent what the user can see at any time and we're just going to do a simple optimization and check if any given cell is currently visible or not if it's visible we'll display the marty if it's not we'll just render an empty cell all right so here we've got our naive 2d example so we have our interactive viewer and we're just using a child once again just like in single child scroll view so all of this is going to be rendered this entire widget tree it's a grid containing a bunch of marty's and we're basing this on column count and row count so up here we've got a 10 by 10 grid totaling 100 marty's so it's about as long as the 100 naive single child scroll view example and we can see the the performance of the marty animation is also similar pretty stuttery as that example and the scrolling as well so here we've got interactiveviewer.builder with our faster example we're using a builder parameter rather than a child so this is called with a viewport and we're able to use is cell visible here to decide whether or not we want to display the marty if the marty is visible on the screen we're going to build him if not we're building an empty cell and once again up here at the top we've got a row count and column count of 10 so we're doing 100 marty's and let's go ahead and take a look at the running app so down at the bottom the fourth we're looking at the interactive viewer builder example and i've clicked that and here it is it opened a little bit of a delay but still much quicker than our 100 marty all at the same time example this one as you can see there's some pop in as i scroll so this is loading more marty's as i scroll and disposing the old ones as i go and the performance of the animation is still quite good besides the pop-in that happens here as i load the new marty's as we pan around so it's not doing quite as complicated as the listview.builder with the shuffling of the cells in and out as the user pans but it is giving us this great performance uh here when we just look at the animations and pan within the area visible so it is a large step forward okay so i've gone ahead and grabbed the memory snapshot for these two examples as well and as you can see the naive approach uses about three times the amount of memory as the interactiveviewer.builder approach which again is about as we'd expect as we're rendering a lot more marty's on the screen but we're also having a big savings in the interactive viewer.builder by getting rid of the rest of the 100 in the total grid so we've got a roughly a proportional level of memory use compared to the amount of marty's we're saving by not rendering off the screen so to take a closer look again at what's happening here in our naive approach we have all these black and white marties which are representing what flutter is rendering internally and not displaying to the user and in our interactive viewer.builder approach what we do is we just say if that marty is off screen we're not going to render it but we are still rendering the empty grid cell so it's not quite as optimized as what we saw in listview.builder because we're not shuffling around and generating these cells as they're needed we're just not rendering the contents of them so this kind of approach it is a lot more custom but it gives us the opportunity to do some fancier things as well so how big can we go with this approach if we do something that is generated only when the user needs it how far can we push this kind of performance so this brings us to our procedural generation example uh i want to build a map for marty and i want to make this map as big as we can ideally infinitely big and i want to generate this based on some seed only when the part of the map that we're looking at is needed so as a warning we're going to have some programmer art here such as the grass that you see fortunately we've still got our great marty asset from rive but there's going to be a few assets that i had to draw myself so i want to look at the performance optimizations that we've been able to make here we're going to break up this map into a bunch of nested tiles so the green field that's visible itself will be broken up into tiles where each tile has a few pieces of grass in it and then we're going to group those into bigger tiles as we see here in black and white that we can manage outside the context of what's visible on the screen so the first optimization that we can make here with this approach is just to not load any assets that are not on the screen so just like we did before we're going to check if the asset is on the screen or not and if it's not we are going to leave it out the other thing we can do similar to listview.builder we're going to get rid of any tiles that are far enough away from the viewport that they're not likely to be immediately panned into the screen so we're just going to render a 3x3 grid surrounding the viewport and as the user pans around this much larger map we're going to adjust what tiles are visible so we can dispose the downstream tiles that the user's scrolling away from and we can load the next tiles coming up and keep the 3x3 grid always surrounding the viewport wherever it goes and here we are in the middle of some grass and some water and we have a randomly placed marty here all of this being procedurally generated based on some seed so i can scroll around and just keep going as far as i want i've got a bunch of water here but then eventually we get to like another continent of land here and we've got another randomly placed marty and this kind of just keeps going on and on we can generate that there's another marty he keeps popping up with his hoverboard so we can just keep going in any direction and generating content and the swapping of the nine tiles is happening just outside of the screen as we do this to keep things performant no matter how far we go but i want to talk about an even more complex optimization that we can make here so interactive viewer does panning like this but it also allows us to zoom and zooming in works nice but zooming out is going to expand so far that our 9 cell grid will eventually be too small to cover the entire screen so what we can do here is to make another optimization and start grouping these cells into groups of 100 and simplifying them into something simpler so that we take our parent level and we make it shrunk down into basically another child level and we re-simplify everything so as i zoom out here on the continent i can replace the entire continent with just a big green cell with some clouds of more programmer art and as i continue to zoom out i can see we've got a whole planet here a bunch of ocean and continents and as i continue to zoom out i can simplify that yet again and just keep doing those levels of simplification as we go and really make this zoom as far out as i want to without running into the problem of having too few cells on the screen so um here i can see if i keep going we're actually part of a solar system here that was our planet but i can choose another planet and just zoom back in on any of these things uh choose a continent and zoom down on that and it will generate our low level content once again with uh nice animation performance nice scrolling performance all right let's take a look back at all of the things that we saw today so first we looked at an example of a list view that you might see pretty commonly in a lot of apps and how flutter allowed us to get some really interesting performance optimizations in with just a few lines of code we also saw how we can use the similar builder concept in a two-dimensional grid and how we can apply those similar concepts to get performance gains on a totally unrelated widget and then we took that a step further and built something totally custom with a procedurally generated map and saw how even a lot of the optimizations that are made in a one-dimensional list view can be applied to something custom like that so like i said in the beginning at flutter we think a lot about making things that are easy to use yet also powerful and chances are whatever experience you want to build you can probably do it with flutter and hopefully flutter will guide you towards doing that in a performant way out of the box all right i've got a list of the documentation here on the right for each of the widgets that we take a look at today and i've got the code for the demos that we ran on github with a live example running right now and i encourage you to check it out and if you haven't already be sure to take a look at all the other talks from io this year thank you guys you
Info
Channel: Flutter
Views: 31,414
Rating: undefined out of 5
Keywords: purpose: Educate, type: Conference Talk (Full production), pr_pr: Google I/O, flutter lazy load, flutter lazyload, flutter lazy loading, lazy load, lazy-load, lazyload, lazyload scrollview flutter, flutter infinite scroll, lazy-loaded content, lazy loaded content, flutter performance, flutter load, flutter developers, flutter development, #GoogleIO, Google I/O, Google IO, Google developer conference, Google announcement, Google conference, Google
Id: qax_nOpgz7E
Channel Id: undefined
Length: 18min 30sec (1110 seconds)
Published: Wed May 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.