 To není, to je slišet, skouška. A to je tady za příjde, takže děkajte síti a děkajte Petr Chalupa. Děkajte za přijde. Máme Petr Chalupa. Já věděl jsem na OREC Labs, který je všechno research group v OREC, a já jsem původný tým, pracujem na největné úplnější implementace Rubi language, který je Rubi Plus. A příjde, bychom značil, že projekty, kde jsem vám říkával, projekty o řekovatě, nebo o rekelá produkce a nejdeši opinatě, co mám. Příjde se přičit ovojí výmu. Vyjím, jak se to přičit, když jste implementovatvá v největného programu. Na prvním příjde se, vyskávate něco tohle největné ideáře o největného programu, když se vytvoříte, takže vytvoříte párstr a vytvoříte abstract syntax 3. Protože je to nějaké hlavní líbice, takže můžete vytvořit hlavní líbice. A když můžete vytvořit párstr a vytvoříte abstract syntax 3. Vytvoříte nějaké komplaté líbice, takže líbice je vytvořit. To je, by vytvořit nějaké měštově komplaté líbice, potom vytvořit hlavní líbice, potom vytvořit hlavní líbice v assembláru a vytvořit hlavní líbice v desuvá reprážení. Zновu nemáte vytvořit. Vysvětvejte, že tě preskávě to nevíte nekdy vytvořit a můj s tebou optimální lidi, which usually are in mature compilers like for Java, GCC, V8 a similar languages. And, if your language is dynamic, which is most of them today, you also need to be able to do speculative optimálizations, to be able to assume things like that my methods in my language are the same, které byly nezvědětávali, nebo byli konstanty v měm líce, které byli konstanty, které byli nezvědětávali. A když máte spekulativné optimálizace, můžete mít optimálizací. Takže můžete mít do kompile, které je závědětá na nějaké závědětá, před návědětá, a v závědětá chvíli v taky vás zásvědání. Takže závědětá v tom návědětě závědětá si nevětné závědět. A takže je se poznáváním i gelatinový mět. A takže je se poznáváním i후ním, a takže můžete mít v tom závěděté, takže vůbec hovala, hebt疺ce a závědětá se. A takže je se poznáváná, které je se poznáváné, z výkladního Ještě. Ale děkáme, že když je nejvědnějším, budete první typů které je říká. Ještě jste nevědnějším probationou s takovým kvěř airflow a vypijáte vnostře, o kvěří technologie. A to je jeho viský brug. a v tom se musíš důležit to sto 150,this framework, and the graph compiler takes care of all the optimizations you would otherwise have to do. And before I go to show some of the,how graph and truffle works, I'll show you a few numbers. Maybe to convince you a little that this is not just a research project, but there are real implementations of languages which are already competitive. So, for example, there is javascript implementation which already competes a spandarmonky. The ground compiler, of course, can run instead of the C2 server compiler, so the same performance is the server compiler for Java, and it's even better for Scala, because Scala has lots of dynamic features, which the server compiler was not really designed for that. And another example is Ruby implementation, where there is no other really fast Ruby implementation, so there are also other languages in this graph to compare, so the first one is implementation of this benchmark in Java, the second one is JRuby Pastrafo, so it's just a little bit behind, it's a little bit faster than Node.js, and this is another alternative implementation of Ruby, it's about 20 times slower, and the referential implementation MRI is about 50 times slower than Java implementation, sorry, than JRuby Pastrafo. So this system really can create or support implementations of dynamic languages really fast, so now let's talk a little bit about how it's done. So the first piece of the puzzle is Grail compiler, which is high performance optimizing just in time compiler for Java. What's for VM, what that means is that you can use it as... The JVM has two compilers, clean compiler, assemble compiler, and the Grail compiler can be used instead of the server one, so you get the same performance, but the main difference is that this compiler is written in Java, and it uses annotation and annotation processors heavily, so it's much easier to add new optimizations into this compiler than to the one which is written in C++. And another key part of it is that since it's written in Java, so it's just a library, and you can use it as a library, configure how you want your methods to be compiled. So, for example, if you want them to be compiled with speculative optimizations or without, and you can tell it what you want to compile, which is very important for the second piece, the travel framework. It's designed for the speculative optimizations, and, of course, it supports the optimization, designed for exact garbage collection, and has aggressive high-level optimizations, partial evaluation and partial escape evaluation. The partial evaluation is really important for the travel framework, I mentioned it again on the next slide, and it's open source. The second part is the travel framework, which is a language implementation framework, which uses the Grail compiler, and basically it's a self-optimizing abstract syntax interpreter, which means that when you implement your language in this framework, so you create the AST nodes for your language, and these nodes can replace themselves with specialized versions based on the program which is executed. So, for example, if you have somewhere addition, and your language has dynamic typing, so you can specialize to adding just two integers, which translates to just one instruction. It uses a custom method inlining, which is important when you have an abstract syntax tree of your language described in the travel framework, and when this method becomes hot, you ask Grail for a compilation of this tree of your method, which is implemented by all execute methods on those nodes. So, we have a custom method inlining, it basically inlines everything, so if you give Grail, for example, 200 nodes, which is representing some high-level methods in your language, Grail first thing it does, it inlines all of those Java methods implementing your method together and creating one big compilation unit, and then it applies partial evaluation, so it evaluates away all the constant parts which are coming from the nodes implementation, and only the most basic parts will remain. There is also support for the assumptions I mentioned before, to assume that the constant is still constant, that you have constant number of fields in your classes and stuff like that, and it's open source. So now, let's talk a little bit about how the travel framework specializes based on the types it sees. So at the beginning, we have a method, which is represented by these five nodes, and they are in an initialized state because they didn't execute it any code yet, so they don't know which types will flow through this method. Then, if you start to execute, sorry, just back in a minute, all nodes start in an initialized state and then they transition to a different state based on the types they see, so to string, double, or integer, and then in generic. And so, just its oriented graph, you cannot go back from double to integer, because then you could have implementation of the method where it would be switching for fanback between two different types, which is not what you want to, you need the nodes to stabilize, so you can compile them. So this particular method sees two integers, and one, for example, some generic object at the end. The nodes will specialize to three integers and two generic nodes. And after this method becomes hot, it's fitted to growl and compiled together in a single compilation unit. But later, if one of the integers will see a double, then you have to go back from the compile code, because the compile code doesn't know how to handle doubles. So you go back to interpreter and the node knows how the node at the bottom knows how to handle doubles. So we can replace itself with another node, which is handling integers and doubles. So the AST abstract syntax tree can respecialize, and after it becomes hot, it can compile again. So let's look at a little bit more concrete example of how it optimizes some mathematical calculations. So this one is to calculate the volume of a sphere. The example is in Ruby. So it just floats. This is a constant of P, and the last operator is the power of three. So a dynamic language to calculate this simple formula usually has to do all of the steps on the right side. So the red one is the actual calculation of values, or if it's execute three, it's just retrieving the constant. And the black ones are representing checks, which are necessary to perform in dynamic language to be sure that, okay, some particles still float, that they didn't change to string and stuff like that. And the other group of checks is checking that the P constant was not redefined by some malicious code, or that the multiplication method is still the same. Because in dynamic languages you can often change the redefined methods, so you have to check that somebody didn't do that. So at the beginning you have abstract text to be initialized again, and as you go and execute the node, the profile based on the type, it sees. So the blue one is the only one integer, the red ones are doubles or floats, and there is one generic format. And as the nodes replace themselves, they also know that they are, their children are also specialized to doubles in this example. So for example the division in this tree will not call to some generic execute method, which is returning Java object. It will call execute double. When you compile this, you see that all of these nodes are just working with primitive types. So all of these checks for that everything is float, go away. So you can reduce the steps to this. And they are just what remains, are the checks that the float cast is the same, pi constant is the same and stuff like that. So to remove it, we need the global assumptions, where assumption is basically just some flag, which you can have in your runtime, in your implementation of your language, that represents that constant is the same, cast does not have any new methods and stuff like that. And if you initialize assumption, it's in a valid state, so the flag is true, and you can invalidate it once. So interpreter is just a Boolean check, if it's true or false. Based on that it goes through different branches. But when assumption is compiled, it always assumes that it's true, which means that it goes away completely in your compiled code. There is no overhead for having assumptions in your code in the compiled path. And the way how it's done is when you are comparing code, which contains assumptions, it goes away because it's assumed that it's true. But also after it's compiled, it's linked with the instance of the assumption. So when somebody goes and redefines a constant, you then can invalidate the assumption, which was representing that this constant is stable. And the assumption will invalidate all of the code, which was depending on it. All of the compiled code, which was depending on it. If there is any threat in that code, it will be optimized and continue in the interpreter, where it can handle a constant definition. So we can then define a few assumptions for, for example, the power method that is still the same for math, p constant and stuff like that. And if we do, it removes all the other checks. So in the compiled code only the calculation will remain. But of course, because at the bottom they are just constants, so it will be partially evaluated shortened even further. So we've started with this and that's the result. This doesn't work just for some mathematical computations. It also works on higher level examples. So these are again in Ruby. So, for example, if you have three numbers and you want to get the middle one, you usually write something like this. So you will create an array of the three values. You will sort them and you read the middle one, which in most of the implementation leads to allocating an array, then probably calling a quick start on it and getting out the value. But in jerovel plus truffle if we see that the array is small enough we can replace the quick start algorithm with something simpler. So compiler can see through it and partially evaluate it based on the fact that the array has a constant length. So it will reduce to just these two comparisons. So there will be no array allocation or stuff like that. For example, if you have a constant hash and you apply a map function when you will be retrieving just values and reading the first value it will also evaluate to just one. And this also works for some dynamic programming like if you have a minus two and to via reflection you retrieve the object which is representing the method for absolute value. And if you dynamically call it it also evaluates to just two during compilation or for a dynamic call multiplication method again works the same. And it also works for much more complicated examples. So if you have a class bar and you are creating new instance and calling a method foo with three arguments but the class itself doesn't have a method foo. So it calls a callback which is handling when the method is missing. So it's called with the method name and the remaining argument. And this particular implementation checks if foo has a method foo if it does it calls it. Let's again a dynamic call. And the implementation of the method is just creates a hash from the argument maps them the values from the hash to array reads the first element finds the middle one and adds them together. But all of this can be compiled to just a constant during compilation. My system is not good just for implementing one language. It's so good for language interoperability because if all of the languages on this system are using truffle framework then all of the AST nodes from those different languages are showing the common parent. It's just a truffle node. So you can do language interoperability quite easily. And because they are the same nodes you can also do you can also inline across languages. So for example if you have a method in a JavaScript and you export it so it can be imported in a Ruby language then you can call from some Ruby method this JavaScript method and when this gets hot it can inline the body of the JavaScript method and it's compiled together. So the language language boundary is eliminated completely. I had a prepared demo about this but I have to apologize because I am doing this from a different computer we were not able to hook it in so I tried at the end maybe to show it with some bad colors but we will have to get to it later. And there is also another project called Throne which is a truffle interpreter for LLVM Intermediator presentations which means that you will be able to run C and C++ on top of this system. And this is very useful for languages like Ruby or Python where Ruby and Python are both written in C and where they are not fast enough people usually write C extensions. So if there is a different alternative implementation of the language it has a really hard time to be compatible with this referential implementation because usually it doesn't have the same CAPI or it doesn't use C at all. But on this system you can just execute the C extensions with your language and it will work. And as a bonus it will be even faster because it can inline the C functions to your normal Ruby or Python code. And last project I would like to mention is called SubstrateVM because another problem which is usual when you have alternative implementation for example if you have a Ruby which is written in C and that starts immediately. And there is alternative implementation of SubstrateVM so it takes some time to boot up the JVM. And this system works on JVM so it takes like a second or two. So this is not really good for development in some languages we have lots of small command line utilities in the language to help you with the development so if you would have to wait for each one of them to boot up for two seconds it would be really not good. On one there is a SubstrateVM project which basically takes your interpreter implemented in the Truffle framework and copies ahead of time. Again using Gral compiler just some different configuration. You turn off the dynamic speculations dynamic speculative optimizations because you cannot recompile it again so it will do just the normal optimizations. And when you have that it will start immediately as the normal C implementation. The one limitation currently is that since this is designed just to run Truffle languages so it doesn't have a usual Java unless there is Java implemented off Truffle but of course it can do all of the optimizations which are available on full Gral VM. So just to summarize on top of our different language implementations like so long is missing there Java script are Ruby Python and they are all using Truffle framework that can run on top of Gral VM or SubstrateVM and I would like to mention that we are looking for new people to help us with working with the Ruby plus Truffle implementation or maybe other projects so if you are interested you can talk to me. And that's it from me. Thank you for your attention and if you have any questions I'd be happy to answer them. Maybe after that I'll try to show you the demo but I'm not sure how it goes. So one thing I wonder about Gral VM it looks like it looks like a fairly standard optimizing compiler compared to C1 or C2 written in Java. Can you enable some essentially new optimizations or is it just that it's easier to write the optimizations in Gral than in C2? I think it's mostly about that it's easier to write them the way how it's structured then usually how it goes is that you have some really abstract definition of the node for the intermediate representation in the compiler and use lots of annotations to describe that it has these runships with other nodes and annotations processor will generate all the boilerplate for you so it's much easier to do. It's not like that it will enable something new for that I know. Thank you. So I have a question about the process. Is there any plan to improve that in the future or give some hints like before the first run application so that it doesn't have to take because for example the compilation or optimization of something to 22 it first needs to run the code to be able to optimize that. So if there was some process as part of compilation even for interpreted language so that I could package these information so that the customer or the user the end user would start with some optimization or hints that could be used. Is this plan for doing? Thank you. So currently we are not working on that because it would improve just like few first seconds when the code is interpreted and it goes quite fast through the compilations and to the compile code sorry when it's quite fast. So right now this is not a priority. It would be possible to make some static analysis ahead to for example pre-specialize some notes, but this is really so not an easy task to do for dynamic languages to do this correctly. And the game is just for few first few seconds. No more questions? Are you all waiting for the demo? Ok, let's see how it goes. Could you switch my computer? So now you know how to use the different computer because of the colors. So I have here a console which allows me to run pieces of code in different languages. So I'll start by switching to JavaScript and I will define a function for subtraction and I will export it under the subtract symbol. And then I can switch to Ruby language where I import the exported function and I store it in a constant. And now I will define some Ruby function which will be using this exported JavaScript function. Sorry. So the first one is in JavaScript exporting the subtract function and the second one is just or maybe I do this. I think it's not better. So now I define some function which will be using it so it's caused the JavaScript function here and I also enable some debug output for how the functions are inline into each other. So now when I run the test function 100,000 times it will trigger the compilation and we can look up in the it's not really in the output how it was inlined. So the first one is right. This times is the this is the same example I've used for how did I run the functions. So this times method is this first one then it inlines the test method implementation which is this one and continues inlines times inside the test method again, then inlines the inject method. You can see here it inlines its method and continues to inline the JavaScript interoperability root node and it also inlines the body of the JavaScript function on this line. So if we then go to a graph which is a representation of the intermediate representation in graph of the methods of the method we are compiling so for this example is the top level times method which contains inlines all of the test method again times inject and at the end the JavaScript method so we can look up that at the bottom there is the integer subtraction there so it's part of one compilation unit which is then optimized background at the beginning before all the compilations are applied. So that's it, it was short. Any more questions maybe? OK, thank you for your attention. Já jsem živat ze s tím nemnou problém. Já si myslím, že to je nějaký sabot erstý tady. Jsou to jako krátký, jako kurivka. Ze stranechny tersi. Jedno, dva.