#5 - Running a Dart Application In-Depth! - Dart VM, Dart Isolates, JIT & AOT Compilation

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous video we learned that dar projects are actually dart packages alongside i introduced you to the most common structure of a dart package and we briefly discussed all of its components like packages libraries lint rules and testing today i want to tackle a more advanced topic and that is what actually happens behind the scenes when we run a dart program without further ado let's get right into it beforehand though i want to send a token of appreciation to everyone supporting me as official youtube members especially to diana and michael aka aza on discord if you want to become a member all you have to do is to click the join button right next to my channel and pick your desired membership having that said let's continue with our tutorial okay so before we dive into the running workflow of dart we should first and foremost focus on what are the main phases a package goes through during its lifetime from my experience there are two main phases the development phase and the production phase in order to thoroughly understand how dart was designed to run a project we should first take a look at what are the particularities and what developers and users actually expect dart to behave like during these two main phases we'll go on and start with the development phase as it is the first phase developers encounter when starting a new package during this phase we as developers have high expectations from dar to deliver a fast and stable development workflow that is to benefit from a quick analyzer reformatting tools from fast compilation and most importantly recompilation times from code flow optimization techniques and last but not least from intuitive debugging tools workflows as you can observe this phase is primarily focused on the development and also the developer experience of writing running and debugging dart code i hope you can also realize that future users of the application won't have any input inside this phase they don't really care about how the app was developed in which way it was compiled how many changes have been done and so on and so forth on the other hand what they really focused on is when the app switches from the development process right into the production phase as opposed to the development phase the production phase oftenly referred to as deployment is primarily focused on the user experience of using and interacting with the app what users actually want from an application is for it to have a fast startup time to be useful reliable stable good looking and amazingly interactive however the job of developers isn't over yet primarily before releasing the project into production phase developers have only one intermediary but really important job to do and that is to test the performance and stability of the app in a real world scenario and not in development mode in comparison to the development phase you can realize that in the production phase nobody cares about the analyzers the debugging tools the compilation and recompilation times both users and developers only care that the final product has a fast startup time and is pretty much as optimized as it can get if we zoom out and compare both phases you will see that both of them have absolutely different perspectives and targets therefore dart has to have different approaches for both phases aiding the developers towards the development workflow and also providing enough optimization techniques to extract and eliminate the development tools from an application so that users can enjoy a fast and reliable experience as you might remember from my first video when i made a brief introduction into the git and aot compilers the git compiler was mainly designed for the development phase whereas the aot compiler was designed for the production phase now that we remembered and established all of these it's time to move over and talk about the running workflow of a dart app so how does dart run a program what are the invisible layers our code goes through from the raw state to a running state on a machine i'm going to start by introducing you to the dart vm which is a collection of components aiding towards natively executing our dart code some of the notably components include the runtime system development components like debugging and hot reload just in time and ahead of time compilation pipelines one thing to be noted is that you shouldn't think of the dark vm as perhaps a slow virtual machine running interpreted code dart vm is mainly a virtual machine in a sense that it provides an execution environment for our dar programming language this means that even native machine code which is the code found inside the production phase of an app is also executed inside a stripped version of a dart vm without the bells and whistles like debugging support and hot reload normally found inside the usual dart vm so we understood that the dart apps programs packages run inside the dart vm what is actually again worth mentioning is that any code within the vm is running within some isolate which can be described as an isolated dart universe with its own memory also known as heap its own thread of control called the mutator thread and its own helper threads the hip is a garbage collection managed memory storage for all the objects allocated by the code running in this specific isolate the garbage collector attempts to reclaim memory which was allocated by the program but is no longer referenced each isolate has a single mutator thread which executes the dart code but benefits from multiple helper threads which handle vm's internal tasks but the real important question now is what is the process in which our source code is subjected to in order to be run by the vm and what are the underlying components aiding towards this well the dart vm can execute dart code in the following ways firstly it can execute the code from source by using a jit or an aot compiler and secondly it can execute the code from different snapshots like aot and jit snapshots the main difference between these lies in when and how vm converts dart source code to executable code the runtime environment that facilitates the execution remains the same in today's video we'll tackle the first part on executing the code from source by using the jit and aot compilers following that in the next tutorial we'll discuss the snapshot alternative let's go ahead and see what happens when we run the code from source by using the jt compiler this is actually related to what happens in detail when we type in the dart run command inside the terminal we'll start right from the dark source code containing our main function and let's refactor this print statement so that it will only print hello world it's much easier to see it in this way from here our code has to be directed to the dart vm in order to be run however the thing is that the dart vm doesn't have the ability to execute raw dart code instead it expects some kernel binaries also called dil files which contain serialized kernel abstract syntax tree as known as kernel ast the dark kernel is a small high-level intermediary language derived from dart therefore the kernel ast is actually based on this intermediary language the task of translating dart source code into kernel ast is handled by a dart package called the common frontend or cfe now just for you to have an idea on how an abstract syntax tree looks like when translated from a piece of source code here is the euclidean algorithm and here it's the corresponding abstract syntax tree you can pause the video and take a more thorough look at it however obviously our kernel ast won't look like this i mean we can actually take a peek on how the corresponding file looks like for our package open up the dart cli type in the dart compile kernel command and the path to the dot dart file we want to run then we can go check out its content in notepad plus for example here's how it looks so this is the file that's being sent right to the vm in the next step once the kernel binary is loaded into the vm it is being parsed to create objects representing various program entities like classes and libraries however this is done lazily that means first and foremost it parses basic information about these entities each entity keeps a pointer back to the kernel binary so that later on it can be accessed if needed hence the name of jit just in time which is similar to just when it's needed the information about the classes is fully deserialized only when the runtime needs it keep in mind that all those entities lie inside the vm heap which is the vm's allocated memory at this stage the rest of the entities like fields functions procedures are read from the kernel binary however only their signatures are deserialized at this stage currently there's enough information loaded from the kernel binary or the runtime to start invoking methods for example this is when the run time may start to invoke our main function from this step the first time our function is compiled it happens sub-optimally that means the compiler goes and retrieves a function body from the kernel binary converts it into an intermediate language and then later on this intermediate language is lowered directly without any optimization passes right into pure machine code the main goal of this approach is to produce executable code quickly however the next time this function is called it will use optimized code and that means instead of directly lowering that intermediate language into machine code the optimized compiler based on the information gathered from the suboptimal run proceeds to translate the unoptimized intermediate language through a sequence of classical dart specific optimizations for example inlining range analysis type propagation and so on and so forth finally the optimized intermediate language is again lowered into machine code and run by the vm and that is it this is what actually happens when you run your dart program by typing in the dartron command inside the dart cli this git approach is used inside the development phase we've previously talked about since during this phase a fast developer cycle is critical for iteration therefore since the just-in-time compiler comes with incremental recompilation enabling hot reload multiple optimization techniques and rich debugging support it's the perfect choice for this job now let's move on to what happens when we run the code from source this time by using the aot compiler this is related to what happens when we type in the dart compile exe in the terminal what you need to understand at first is that the aot compiler was originally introduced for platforms which makes git compilation impossible so it was mainly an alternative to git therefore we'll discuss this approach as being kind of a comparison between jt and aot one advantage that arises from the aot compilation approach is that it comes with fast startup times and consistent performance whereas jt is the king of peak performance lacking in the startup time area mainly because of its long warm-up steps what you need to understand overall is that both come with amazing features and both are specialized into specific phases of the development as i have said since ait comes with the best startup time and consistent performance it's best to be used as a compilation method for the production phase this is what users actually want on the other hand the git approach will come in handy for developers thanks to having a peak performance and multiple other optimization techniques built for fast iteration you'll be surprised however at how similar the aot running flow is compared to the jit at the same time there are some subtle key differences worth noting compared to the jt the iit compiler must have access to executable code for each and every function that could be invoked during application execution to satisfy this requirement the process of aot compilation does global static analysis also called type flow analysis or tfa to determine which parts of the application are reachable from the set of entry points based on this analysis it will remove unreachable methods that code and the virtualized method calls the virtualization is a compiler optimization that replaces indirect or virtual function calls with direct calls these analyses rely however on the side of how correct and optimized the code was written in the first place as opposed to the git analysis which can rely on the side of performance because it can always optimize de-optimize and re-optimize the code to the correct behavior based on previous information one thing you might not know is that the aot compilation tool chain is based on the git compiler so it undergoes the parts of the kernel library method by method through the same steps as jt the only difference is that as i previously said there are no speculative optimizations that can be applied in aot mode the standalone architecture-specific executable file that was generated from the aot compilation can be run directly on your operating system right from the terminal and here we go it runs absolutely instantly as we discussed it is finally time to wrap up this tutorial in the next tutorial we'll discuss what happens with the second approach of running dart programs from snapshots as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye
Info
Channel: Flutterly
Views: 6,108
Rating: undefined out of 5
Keywords: dart, programming, dart course, dart tutorial, dart jit, dart aot, jit compilation, just in time, ahead of time, aot compilation
Id: NoVYI94MJio
Channel Id: undefined
Length: 14min 14sec (854 seconds)
Published: Wed Jun 02 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.