Devreal

Scale By The Bay 2019: Evan Chan, Rust and Scala, Sitting in a Tree….

Scale By The Bay 2019: Evan Chan, Rust and Scala, Sitting in a Tree….

Recording: Scale By The Bay 2019: Evan Chan, Rust and Scala, Sitting in a Tree….

[Music] yeah I'm here to talk about rust and Scala just it's it's great to be here I love Oakland these these are this is this my this is my shirt the Oakland A's are my favorite teams it's greener go to the colors and so if you want to if somehow I was told that this forints render small on the screen so if somehow you can't see it or you want to follow along I posted a URL so that you can also look at a presentation on laptop or whatever but hopefully that won't be necessary I am the I lead the Oh file ODB open source project time series database I'm also a landscape photographer if you want to give me a follow on Instagram that'd be pretty awesome it's all about the Instagram follows right so I want to start by talking about how I got into rust like you might have heard a lot of things about what what this thing is rust so we were basically we've been building a time series database and in memory time series database which we believe is fairly scalable it is written in hundred percent scholar and it's capable of storing up to like 100 billion data points in memory and we can ingest something like a million samples per second on a single note and and this kind of thing is fairly difficult to achieve in any language land let alone the JVM right and we found a lot of things the hard way one of the things is that it's pretty impossible to achieve what we did if you just do a normal object graph in the JVM in energy of your heap what we found was that just the overhead from the object graphs alone was you know something like 50% of the data warner store which is for us you know it's it's really not acceptable right and the other thing was that we found that we were getting really long GC pauses because what happens if you store a lot of data for long time is that this data starts getting into from you know it goes from young gent Odeon once you fill up Oh Jen the GC really struggles and starts to choke you start getting something like no twenty second pause times you know something really really horrible right so this is something that we actually encountered so what do you what are you doing that I mean what do you do so we did what a lot of other projects like Cassandra HBase I've done which is we that that we started moving data off you now this works you can store data exactly how you want when you stored it off you you have complete control but it is also really really hard you're talking about managing memory manually you have to know exactly when you allocate and free that this is this is not you know this is this is not easy and we also found that working with low-level data manipulation and having to do it at the kind of rates that we wanted to do is also really really hard it works you know code works for the most part but it is this really really ugly right and and not very safe last year I came here to scale by the bay and and I attended a great talk by Brian Cantrell who was talking about rusts and at that time has already started thinking about you know maybe you know maybe this is not the best platform you know this doing stuff natively in the JVM wasn't necessarily the best platform and see if their thirst ways we could improve you know our safety and some other things and he talked about Russ as the language with certain values and those values some of them the most important ones that appealed to to us to me was performance safety and abstraction and he said you could pick three meeting you could take all of them usually you think about these kind of things they usually pick one you know or pick to like pick consistency or pick you know pick availability and you can have one of those right but you can't of all them and so it's like huh what does this mean that you can evolve them so this was really interesting and being a native language you have access to things like Struck's to allow for good low-level access with which which was good and some of the other things that was really interesting for one being able to prove that you have correct memory access and also if you look at the architectural trends that are happening you can see that CPUs are not really getting any faster right that this the overall clock speeds have stayed the same or pretty much started decreasing so so what do you do right so you have more and more cores you have to be really good at doing concurrent stuff now and you have to start taking advantage of some of the other cpu features like sim the GPUs and other stuff and so this kind of portents a trend towards taking advantage of the hardware and in a way that's friendly speaking that that JVM has not been the best at keeping up with in in in recent times so so this got got me started really looking at the rest and the rest of the talk I'm gonna go into looking at the things that make Russ very different like and kind of comparing them with how how we might do things in in in Scala and looking at the differences I'm going to start with memory model so the JVM memory model is pretty simple you have the stack right and so the things that you store on a stack this is something you don't really think about because it's this you know happens for you but basically what what you put on the stack and when you call a function is that you can put primitives on there or you can put object references and so everything that's not a primitive in under JVM is an on heap object right so fairly simple and this did not really show up but there space this is supposed to be a graph that so basically you know you have an object graph and you have a garbage collector that will clean up things for you which is really really nice because the garbage collector means that you will track all your usage of objects so if object one object to it knows you know when how many people are using object one object to when someone is no longer referencing object one then the GC knows they can clean it up this solves a lot of problems for you that you don't have to worry about allocation the allocation you have to worry about you know who's using what or if something is valid you know something is always valid because there's somebody keep a track of it so this is really nice the Russ is quite different Russ use this puts a lot of things on to understand and it is not just primitives but trucks which are like basically records or case classes go on there anything that is fixed size essentially goes on a stack as well as pointers and a heap misused for dynamic data for example maps lists strings now Russ does not have a garbage collector how does it manage this memory and how do you know when it's safe that that's one of the you know most interesting topics so one of the principles is that rust allows you to militate data only if someone has unique access which which means that only I can mutate something and if you rust program compiles then it is proven that it would not have memory corruption or data races that's it well I mean yes like how you get there is a really interesting part so let's look at an example so this this talk I'm going to put a lot of code under with which I hope you enjoy you know we're all like you know one to look at what it means if you look at this looks pretty simple so instead of Vow you have a let this means I'm assigning in this case X to a new string which is allocate on our heap the next one looks innocuous in just about any language I've assigned a new variable Y to ax but but in rust it actually it has a different connotation so what happens is that when we say let X equal string X has become the owner of the strength and ownership is the concept is how a rust keeps track of when it is when the data is valid and and the scoping and when it gets freed so in this case after you assign X to Y the ownership has moved to why so now why owns a string and X doesn't anymore so in this case printing out X after you do this and this seems strange but printing up X after you do y equals x is actually invalid and the reason for that is because after X is moved into Y X is no longer the owner so rust can no longer proof that accessing X is valid so access at that point has gone out of scope now obviously if you had a language where you could only move things around and you know that that would not be terribly useful right so in practice you there's a lot of what what's called borrowing so in this case with the same first line let X equal strings so X is again the owner but the second line looks a little different Y is equal to ampersand X which means that Y as a reference to X and it's borrowing it so when you borrow something you can borrow something in real life as long as you can return to the owner right so in this case it is the same in this case after Y borrows X I can still refer to X because X is still the owner and you know why yeah what why is borrowing at this lock this basically Russ will allow you to borrow x into y as long as you know X still round so you can have a reference you can have an unlimited number of mutable references which means other people can read the value but Russ will only allow you to have one mutable reference so there's only one person that can mutate it at a time let's look at oh sorry so what is really interesting is that Russell type system that has certain traits that enforce the correct usage of mutation and sharing things across threads so I won't go into these into detail but for example there is one trait that well actually allow for a multi-threaded ownership and sharing which not a lot of things in language have naturally so this is an important concept I'd like to cover this is called safety by default and and this is really interesting because coming from originally came from languages such as Ruby and Python and you like well things on JVM like Java and Scala are pretty safe but but Russ is actually yeah well what's this go under the examples I know explain what I mean so for example this is a fairly common call you have a vowel map and we're going to create a new mutable hash map right so what does this really mean it means that the this is what I call service level immutability it means that I cannot reassign the reference that map points at so map will always point at this mutable hash now however I can still mutate hash enough however one right there's basically no bounds on that so in this case the next line I I'm adding a new key and value into this map but it seems pretty normal code and but what it means is that what about the only thing about and enforces is that I cannot reassign it but it doesn't control the mutation right so if it wants immutability I have to choose the right collection to use which is immutable let's contrast this with rust so in this case what I'm doing is this is pretty much a seam code I am creating a new map hash map neo with this syntax looks a little different and I'm doing an insert which is the same thing as the plus in previous example so this actually does not compile because when by default the let is considered an immutable so the map is immutable and any methods which tries to mutate would not work it gives you a help message which says you can declare this as a mutable thing in which case this would actually work so let's do that so I'm gonna do let mute map and and now I can do the insert and it won't complain right so so there's a in in rust there's the difference between the objects and references so reference is a point at the object and allowed you to you know you can share them because it's so that's like the borrowing thing that I mentioned before in this case I've referred to map using ampersand in the third let imbue F equals and map and you see that in the origin example so map can I can insert into map because I'm the owner if I share an immutable reference whoever uses it then cannot insert so this allows me to separate out who can meditate things and who can read things if you pass in literal reference whoever uses it what this would also not compile it will they will give you a suggestion that maybe you want to change in your middle references which might not be what you want to do let's look a threat safety thing so this is something that has I know it has something to me and maybe you know this would seem familiar let's say that you could use actors actors are great I love actors so you say actors are threat safe pretty much right because you cannot have you can only receive messages essentially like in the not it might not occur in the same threat but basically you won't process two messages at the same time right so you always process one message and you say okay so that means I can use a immutable collection that's fine and when I got a message maybe I'll be with hate it and maybe one day later or something you know maybe your teammate will go in there and say oh I need to use some asynchronous thing right so I'm going to put it a future in my actor and I'm gonna map it and and then I'm gonna bill take the thing oops so what a suddenly happened is that and this is very easy to do because this is a really trivial example that is small but imagine if your examples real world in a hat like you know 50 100 lines it's very easy to do this where you know I'm kind of mutating a dinner structure in a callback and I don't even know it and then later on you discover that you know I have a horrible bug right so this I know this is not an actor but this would be something roughly equivalent and rust where I've spawned another threat and in order to in so I've created a hash map again and in this utter thread I am attempting to do an insert into this data structure the only thing new here is that there is something called arc arc stands for our atomic reference count and it allows you to it allows for us to keep track of who might be using a data structure dynamic in cases like threats where you don't know if one threat might end before another so so Russ will not allow you to do the mutation in another thread because Ark is something that does not permit basically multiple people to try to own and rotate it at the same time and to get around this there's different ways to do it one way would be you can use something called we rewrite lock you know which would allow motive people to to actually take a lock and mutate it at the same time so things like rewrite lock have have a special trait that informs the compiler that it is actually safe for multiple threads to to mutate this data structure another way to take care of this problem which is really nice is that russ has merged a new feature called async/await recently and what async/await allows you to do is declare a synchronous function so in this case my function I have a function called map insert and I put an async keyword in front of it this means that I can run it as a future and anything that are returned in this case this I'm not returning anything but if you were to return something it would be returned through a future now my main body loop what I'm doing is I'm calling map insert and notice that I'm giving it a mutable reference normally this would not be allowed but in this case the russ compiler is able to deduce that i'm calling an asynchronous function and i'm gonna wait for it to come back so in this case it knows that the main body cannot do something that also is mutating the map at the same time and it also knows that this map will survive because he knows the function will return so it's able to do that it's safe for you to pass a mutable reference and it's in the side there's something really cool about a single way which has anybody debugged stack traces that involve futures here rates and quite a few hands and you notice that there's really long stack traces with a bunch of gibberish about I don't know like something about you know futures or some concurrent thing right and you can't really tell like the previous points from which you've launched your asynchronous code so you have we have a synchronous stack traces in rust which means that in this case we know that fool caught bar caught blow up in an asynchronous way and it can track the callers which is which is pretty cool for debugging I'm gonna go over this this really quickly there are actors and actors is an actor library this is sort of like archetypes in which case you actually can handle different types of messages so in rust instead of defining a class where you implements one or more traits instead what you do is that you implement a trait for a a struct and you and those implement integers separate so I've implemented actor for simulator which is an actor definition and I've implemented a handler for the some message and and you notice that that there's a handle method which can take results and do stuff with it and notice that any method has a ampersand wheel itself which means that it's allowed to mutate destruct so you can control me to ability explicitly when you define methods this is one of my favorite things that I think it's pretty cool for processing data is that there are certain safeguards for numeric processing Russ will not course different types of integers like in Stu Long's so you have to explicitly cast them but I actually like this for very precise code because there are a lot of places where you might not realize that you're doing something and you might lose position or something and it also has signed in on site types which also find useful for ensuring the correctness of certain things such as right shift left that kind of thing so safety is nice but you know what about this like how fast can it go and what about like abstractions right or what about no functional transforms and things so this is something that we all use so this is fairly important so I'll just share this this is actually these are classes that are taken from file or DVD like there's that's the case classes we have a trunk query info that an embedded chunk set info which has a seek you know which has some other stuff so you know like I don't know maybe to two levels deep so and unfortunately my graph is not showing that but so we have an object graph here right the chunk Korean foe has a chunk site info and so each of these things in memory actually has a byte has a 16 byte header for each objects so in this case we end up with an overhead of about like a little bit more than 100 bytes for a single instance of a trunk query info assuming I have like say you know two two pointers or something if you have structs they are actually laid out like cease trucks so that they are in line so in this case the same thing in rust the chunks that info what the bunch of fields would be laid out right before this long data reader which is the nicks you know set of fields and so this allows me to eliminate most of the well there's no object or overhead so this allows me to save 100 bytes per thing and if you have millions and millions of these then you know that's something that adds up here's a simple functional transform example and we'll go into what this is actually doing under JVM so this counts the number of even numbers fairly simple right you do a filter and you know you figure out you know which ones are even anaconda lengths so what's happening under the hood is actually there's actually a lot of stuff that happens first of all all functions are actually objects in a JVM so the closures you need to allocate a scholar a function one for the inner closure then for each number because often times or most of the time closures are not boxed what this means that the number needs to be box into a java.lang integer it gets passed into the closure which has an unboxing at that and then you calculate it whether it's bullying or not this might get boxed again so some of the boxing could possibly be optimized by the JVM and some instances it really depends a lot on specific a lot of specifics like how simple it is how short a method is how many arguments you have but it's very easy to to quit basically form to this behavior if you mix does it really tiny change and you wouldn't really know it right this is equivalent transform in Rus it looks pretty much exactly the same except that it works on iterators but you have a filter method again and you count method the syntax is a little different but it's more or less the same code and in this case bruss is actually able to transform this into a very tight loop what inlining and you know there's no boxing because there's no objects which is pretty cool so where this points is that when you have the ability to optimize high level abstractions with little or no costs and to get access to some low-level things such as sim D you know it leads to really fascinated processing which is pretty promising so there's a whole bunch of really interesting projects some of you might have been at the well talked earlier well it is like an intermediate like an IR that's written for data transformations to speed up everything from spark and other libraries and looks very promising that one is written in Rus and there's some other stuff out there Apache arrow has a new query engine which is written in rust and there's some other time the data flow is kind of like a spark like distributed computer so there's a couple of interesting tools that have been written out there I highly recommend grep by the way if you want a really fast grep utility it is super useful for it will search through your whole codebase so what do we what did what did I learn looking at rust there's a couple of interesting lessons I mean first of all I think you have to expect that things will go slowly at first it takes a while to learn you know what does it mean what does ownership mean what does borrowing mean and the compiler errors there will be a lot of although the error message are useful but there will be lot of them the community is very helpful the it doesn't have a repo because it you know most native languages don't for me personally this is this is difficult right because I love to use the Scala rapidly develop and I think it's it's kind of a balance so I think for most people the JVM Scala is a very good platform and a probably will be you know more productive overall but I think there are a lot of cases where you know Russ would be really really good and where it's good you know it has like very few equals the build tool called cargo is really really awesome and so are so was the packaging infrastructure and I'm sorry it is quite a bit easier than SBT and and I think something that I maybe I didn't expect is that I think learning rust has kind of improved my programming everywhere on kind of the emphasis on safe API it's a little different you can kind of emphasize places where that are safe but it's very different when safety is universal and pervasive like everywhere and API I think that makes a like a really big difference when you're basically forced to make choices for safety everywhere so so we see something that is very promising the question is how would we potentially explore use it in our ecosystem right so an obvious example is you can where you go is to use it as a standalone micro service but I would argue that one good way is to try to embed it so one nice thing about Russ is that it has a small runtime so you can create a dot Esso or something and and integrate it and that way you can reuse you know a lot of your Stegall code on JVM code and kind of use it for what it's good at and you just Russ for what it's good at there's a couple different ways that you can do it I've used J&R which is JRuby projects you basically it's really simple and fast the most promising way that I've seen though is is growl VM ware which promises to integrate LOV embed code into the whole platform give your uniform way of debugging cold weather it's jvm our native that's kind of something to really keep on horizon and finally I've taken some hints from Russ back into our JVM code into our Scala code I kind of created these classes for native pointer access based on similar ideas from Russ where you know your basic class is immutable you have to explicitly ask for a mutable version and you have to explicitly kind of change bit widths to get at you know different types which is just taking some of the ideas back and finally there's a if you are interested you can look at this later because of a list of links but there's some web frameworks and some interesting things that people have done and there's various IDE integrations IntelliJ vs code and and other editors and I mentioned also briefly that server-side Swift is also really interesting it's also something really interesting to look at so thank you very much I have a set of links that if you're interested IDs are just notes on different interesting Rus things that have kept in mind yep thank you having any questions I can give you a microphone yeah I'll be outside looks like no thanks again thanks Alan [Applause] [Music]