RubyConf 2015

Ruby también se puede utilizar para crear aplicaciones concurrentes

Jerry D'Antonio  · 

Presentación

Vídeo

Transcripción

Extracto de la transcripción automática del vídeo realizada por YouTube.

- I'm gonna go ahead and get started. First off, thank you everybody for being here. My joke a minute ago aside, it's always really nice to have people actually come by and want to listen to what you have to say, especially when you've not actually given this talk before.

This is my first time giving this talk. Hopefully, I think it's got some very interesting stuff in. Hopefully people learn a few things from it. The obviously inflammatory title, which was clearly intended to (chuckles) make a statement is "Everything You Know About the GIL is Wrong.

" I'm sure there's at least one person in the room sitting over there who knows way more about the GIL than I do, but for the most part a lot of people don't understand this particular thing, so we're gonna talk about it. My name is Jerry D'Antonio. I'm from Akron, Ohio.

How many people here have ever heard of Akron? Okay. Now how many people have ever heard of Lebron James? Right? (chuckles) Local kid, lives, with the high school, just down the street from me, pretty good at basketball. I work for Test Double. Many of you have probably heard of Test Double.

Some of you probably went and saw Justin's talk yesterday morning. "How to Stop Hating Your," excuse me, "How to Stop Hating Your Test Week. " Justin's one of the founders of Test Double. Of course, we're a consulting company out of Columbus, Ohio, and I work there.

Beyond that, the probably most relevant thing about me with respect to this particular conversation is I created this thing here. It is a gem called Concurrent Ruby. Who's heard of this? Anybody? Just out of curiosity. Okay, cool. Cool. So Concurrent Ruby is a Ruby gem that is intended to provide some suite of concurrency tools for Ruby to extend our options in building current applications.

Concurrent Ruby is being used by a number of projects, some of these you may have heard of. Oh, Rails. Sidekiq, Logstash, Dynflow, Volt, Hamster, Pakyow, Microsoft Azure uses it in their cloud. I know Sucker Punch is considering using it. It's really humbling to see these projects on the list saying that they're using our work.

But, there's a sad, unfortunate truth to all this and that is that I've actually been wasting my time, that this whole thing about trying to build a concurrency gem for Ruby is just a fool's errand. It's a complete and total absolute waste of time, why? Because everybody knows, let's say it, that Ruby can't do concurrency, right? Raise your hand if you've ever heard somebody say Ruby can't do concurrency.

All right, so let's just get that out of the way. For those of you who have had not heard it, clearly you don't have Twitter accounts because, you know, if you follow Twitter you will find that apparently Ruby cannot do concurrency, and if I heard it on the internet it clearly must be true.

So being that I know how to Google and I know how to use the internet thing, I thought before I give this presentation about the GIL, how about I actually look up a few factoids about the GIL? That'd be fun, right? So let's talk about what this thing, the GIL, is.

I did some Googling and I came up with a couple of factoids. According to the internet, here are a few things. First, Ruby has this thing called a Global Interpreter Lock, also called a Global Virtual Machine Lock or GVL. Right? Has everybody heard that before? Here's a couple other factoids I picked up on the internet about the GIL.

The GIL is a soulless, heartless, and pure evil. The GIL hates you and it wants you, personally, to be miserable. The GIL eats babies for breakfast, no, seriously, I read this. It eats babies for breakfast, kittens for dessert, and puppies for midnight snack.

I'm pretty sure that the GIL is the sole cause of climate change and I also think somewhere I saw that if there was no GIL there would be no war. So pretty much, you guys have all heard those? Let's just, for fun, I mean you're here, you're already sitting down.

I've got, like, you know, 40 more minutes so let's take a look at some code. All right, so we're gonna look at some code. This is a quick sample program. Hopefully, everybody can see this all right. What this is is this is sort of one of my go-to's for showing concurrency stuff.

What we're gonna do is basically I'm going to go out and hit an API and I'm going the. . . Um, I can't because this is actually PowerPoint, and I apologize for that. Normally, when I do this color scheme it shows very well and so I'm sorry it's not. So I will try and explain it the best I can and I'll put this up on the web later on.

So I apologize for that. What I'm gonna do here is I'm gonna go out and hit Yahoo!'s Finance API. And I'm going to, I picked 20 stock ticker symbols. I got those from Bloomberg. I had to pick 20 and they had this list of here's 20 that really did well. So I'm gonna go and I'm gonna hit this Yahoo! Finance API.

I'm gonna bring back, for all 20 of these ticker symbols, I'm gonna bring back the data and I'm gonna pull out of that what the stock price was at the end of 2014. It's just an arbitrary thing. It doesn't have any real meaning. So my top function is called 'Get Year-End Closing' and it just does that, it's just that.

Then I've got a function called 'Serial Get Year-End Closing. ' What I'm gonna do with that is, in there I'm gonna take that list of stock ticker symbols, I'm gonna iterate over that using the collect method, and I'm going to retrieve those one at a time and put those in an array.

So at the end of this I'll have an array with 20 prices that I pulled from that API. The next method is called 'Concurrent Get Year-End Closing. ' I'm gonna do the same thing but in this case, rather than doing it serially, I'm gonna do it concurrently. That thing I'm using is called a 'Future.

' It's from the Concurrent Ruby library. This is not meant to be a sales pitch for Concurrent Ruby, but I can do that in one line of code and it's very easy to read. What's happening is I'm gonna do this thing called 'Concurrent Future. ' I'm gonna fire this thing off and say, "Go get this thing.

" I'm gonna fire 20 of these things off. They're gonna go onto background threads that run on a thread pool. What's gonna happen is I'm gonna collect up those future objects. They're stateful objects. They will have their state updated when the task is done.

Then I'm gonna do another 'collect' statement to go and actually retrieve those and get the array. So at the end of that I will have the same array that I'll have for the serial one, right? One extra line of code in order to do this, but I'm gonna do it concurrently.

The second part of the script is going to be some benchmarking. Has anybody here used the benchmark before? It's really cool, right? For those of you that haven't, what it's gonna do is this particular one, benchmark. bm, will actually do a rehearsal phase.

It'll run a bunch of the things I give it. It'll then determine how many times it has to do that in order to get adequate data. It'll then run it again and it'll give me the output and I can compare these things. So I'm gonna compare the execution of the serial method to the execution of the concurrent method.

Now don't say anything, but think to yourself right now what you should expect to see. Because as we know, Ruby can't do concurrency. Ruby has a GIL. It's a lock. It prevents us from doing anything really fun and interesting and having nice lives. So what we should expect here is that despite the fact that in the second case I'm gonna fire off all these things asynchronously on a thread pool, that the amount of time it takes for each of these should be the same.

Is that reasonable? I do it serially and it takes a certain amount of time, then I do it concurrently because, since we can't do concurrency in Ruby, it's gonna take the same amount of time, right? That's to be expected. So just because we're here, we've got the time, let's run that and see what happens.

So when you look at the output of this, clearly you can see right there that it took about four seconds to do that serially and it took about four seconds to do that concurrently because Ruby can't do concurrency, right? Does everybody see that? Does everybody see that on there? Raise your hand if that's not what you see in that output.

All right, I should see every hand in the room raised. Okay. So what happened here is it took roughly four seconds to do that serially and then concurrently it took less than. 3 seconds. It took about 1/10 of the time. Clearly something is wrong with my test, right? So just for fun, let's compare this same thing to I don't know, runtimes that can actually do concurrency like, I don't know, JRuby.

Let's run that same thing on JRuby. This is JRuby 9010. That took about four seconds, which, serially, that makes sense, right? It's still Ruby code. It's still I/O. But it actually took over a second to do it concurrently. Hmm. Interesting. Well, what about Rubinius? Rubinius, that runs in LLVM.

It doesn't have a GIL. It has, like, you know, it should be able to do similar, right? Well, it took that about four seconds to do it serially. Okay, that makes sense. That seems pretty consistent. But it took that about. 4 seconds to do that concurrently.

So MRI Ruby took as little time or less time to do that concurrently than the two runtimes that are actually supposed to be good at that. And they are good at that, don't get me wrong, but from what I saw on the internet through all those tweets was that MRI Ruby was not actually any good at that and yet in this particular case it seemed to do okay.

[ ... ]

Nota: se han omitido las otras 4.693 palabras de la transcripción completa para cumplir con las normas de «uso razonable» de YouTube.