 Thanks for coming to see F5 versus the Flying Robots. Tough choice, I understand. My name is Jeremy Heingartner, and I also go by copious3time on Twitter and copious3time.org. And I'm here today to actually confess. Just repeat, we're all here for Ruby. We're gonna talk about Ruby. And this may be a deeper confession, I'm not too sure. Some of you may join in on me, some of you may not. I like C. You can shame me, all right, all right, all right. My people are here, this is good, this is good. So basically what we're gonna talk about today is Ruby and C, but not in the traditional extension sense. So let's do a little couple of quick surveys. Start off with, raise your hand. Here's a Ruby developer, raise your hand. Keep them up, I should see every hand in this place up. All right, all right, keep them up. We're gonna go through some nation rounds, see what happens. Who's written a library? Doesn't have to be public, it can be private. Okay, does that library have an extension in it? Keep your hand up if it's still the case. We've still got maybe two dozen or so. Is it used by more than one Ruby engine? We have three, four. So I assume your engines are regular in JRuby. Yeah, all right, cool. What's your library? Yeah. Tornado? No good here, well, of course, yes. There were two other hands. Lived Rizzles. Lived Rizzles? I haven't heard of them, I'm sure you haven't heard of them. So our goal with Ruby, it helps us make extensions in Ruby gems that can be used across engines essentially. So to start with, who's actually knows what FFICE stands for? Everybody, all together, foreign function invocation, yes. Yes. And this is the library that allows one library written in one language to be invoked by code in a completely, I'm actually not sure how it does it all the way down at the low level with the symbols and everything that happened down in shared library land, but for us, here's our goal. We wanna be able to create extensions in gem, in terms of extensions, that can be compatible with multiple Ruby engines all at once. So we can say, hey, instead of having to create a specific gem that's for JRuby, a specific gem that's for MRI, another specific gem for MRI on Windows, because if it's Windows, you need to ship the X-Ruby. The library might not exist, and there may not be a compiler. So what we're trying to do here is make sure that any extension that you write can be used on multiple engines all together. So we'll go back a little bit. LibFoo, everybody's used LibFoo. Yes, yes, it's a great library that's everything you want. Perfect, it's the absolute perfect library. And you want to use this in your program, not necessarily a Ruby program, but you wanna use it in your, so say C, for example, you use the include header, you compile against it and it links, and you've got your binary that runs and it links against LibFoo. So we have this. Now, what LibFFI does is it kinda slides into the middle here between these guys. So if you have a library, LibFoo, and your program wants to use that, a function in LibFoo, but maybe you don't actually have the headers, or it's some, well, one example of it actually linked against code that was only object code and there was no way he could actually compile against it, so we used LibFFI to describe the interface, which is, we'll get to that in a minute, and was able to do it. This was all C to C type stuff. But the benefit of using FFI here is normally when you're compiling, your program needs to link against the library as that final stage and the final linkage with FFI, your program links against LibFFI, and then when you run, FFI searches out the function based on, searches out the library and the function based upon runtime information. So instead of linking, instead of that final linkage stage, it actually happens all at runtime. So if we take this paradigm, which should be fairly familiar with most people, and change it to what we do in Ruby. So we have LibFU, which is this amazing library that we wanna use in our Ruby program because it's just awesome. So someone, some brave volunteer, goes out and writes RubyFU C extension. So the C extension links against the libfu.so, does all this kind of stuff and exposes it, hopefully for you, Jim, and you can load it in Maths Ruby interpreter, and you can use it in your application. So everybody's leaving the extension. Now we bring two more members to the party. So we said Ruby, Revinius, maybe even Maglev, Iron Ruby, any of these other Ruby engines, and you could even consider for this type of thing, the Ruby. What do we do in these cases? But JRuby can't. So what do you do in this case? So you can either write the same extension again for JRuby, write it in Java, and then you have different gems. You have the JRuby gem for JRuby, and you have the CRuby for Math, but then say someone does it. Well, then they have to be different. So what we do is we're gonna just get rid of that straight extension and put all of the interpreters on the same level playing field. What this means is they're all gonna be considered exactly the same in this. Insert into that stack libffi, or the ffi gem, which you can use in the Math Ruby interpreter. And what that does is basically, gives you a gem that exposes the ffi library to the Math Ruby interpreter, and then Ruby code will be in just a minute. So then you actually write your Ruby code and use the ffi drive of the library you're gonna add to your API on top of that to show it even further. So we add a new layer to the stack. Everything is all right with the layer of the directory, so it's all good. So this is all kind of abstract stuff. We're gonna use a simple library. How many people have used high times before? Sweet, three people, yes. This is a library that does timing, and one of the sub-features of it is it keeps track of stats. And I extracted the little stats piece out and made lib simple metrics. It's a straight C library. It's up on GitHub, but check it out. Really, really dirt-simple, but it provides a really good example for using ffi. So metrics. Three C, some of the squares count. I wanted to add some more sub-features. And basically have a C-based object-oriented API. We have new, we have free, so your constructor, destructor. Update says I wanna add a new value to this metric. And then we've got our min, man, min, mean, max, sum, count, standard deviation, and rate. So five of those just access the value in the struct, and the other two actually take values in the struct and calculate a new value for return. So this is the library that we want to expose to Ruby. And we're gonna do it in a couple of ways. We're gonna do it with the extension way, we're gonna do it with the Ruby ffi way, and then we're gonna do a little compare and contrast between the two of them. So what we want here is to take this and convert it into this. Our Ruby interface is a metric. I'm losing anybody yet? Yeah, it's fading in and out, and I'm not sure why. Okay. All right, let's go with this one. That sounds better already. So we want to take, this is our Ruby interface. It works really well. Is there a volume on here? Huh? No, it's off. Powered off. Okay, we'll try this. So what we want to take is our Ruby interface here. This is the interface we want to have from that C library to this Ruby interface. So it's kind of a nice little mapping. So this is just a section. The next one, anybody share me still? Okay, so the next one here, this is the final piece of code to do this as a C extension. Does it look familiar to some folks who've done C extensions? You've got your init method here, and this basically defines in C the Ruby class and the methods. So the rest of it's above it. And one little thing you can see down here if it shows up. This, the wrapper for this really simple library in Ruby C extension, the 167 lines of C in comments. FYI, the actual lib simple metrics is 105 lines long. So it's kind of a little complex. Now it's a simple library, so it's not really a good comparison. But it takes a little bit of work to get a C extension in Ruby, but it works out really well. So the next thing we're going to do, this is how it looks as a C extension. This is how you describe it in FFI. So another way to say what FFI is, it is a DSL for describing interfaces. So what we're saying is we have a module, simple metrics, module FFI. I'm gonna skip that struct piece real quick and I'll come back to it. We extend the FFI library. This is actually what pulls in the FFI gem. And then we say, hey, the library that we're gonna work with is called lib simple metrics. And those two lines right there include FFI and then that basically says, here's the physical system name for the library that we're going to work with. And then all we do here is describe each method. So we're gonna say I'm gonna attach a function called new. Let me add some new things here. This is our library function as a symbol. It's basically attached function takes three parameters. The first one is the symbol name of the function you're calling. The second one is an array of the arguments. And the third one is the return. So pretty simple. So we have here new. Doesn't take any arguments, returns a pointer. Free takes a pointer, returns a void. Update takes a pointer and a double, returns void. So all these kind of basically map directly to the header file with a couple of slides back. Now the struct up here says, how does the struct layout in memory or in the actual physical layout of struct? We had a min which is a double, max which is a double, sum which is a double, sum of squares is a double, count which is a lot. So these and these need to be, question? Okay. And so these basically lay out the structure as they were in the header file. And I'll do a side by side here in a little bit. The other thing we wanna do here is like, wouldn't it be really neat if we could actually just tell Ruby to garbage collect the structure when we're done? Well that's what that release pointer does. So when this object is done, libffi combined with the garbage collector will remove this from your system. So you'd actually have to allocate and deallocate this structure, which is very cool. So we instantiate an object, it's allocated. No more references to it, garbage collector comes by. Boom, gone. That's very cool. And then this is our Ruby site. So we basically have the ffi and then our Ruby class, the stacks on top of it. So this is our metrics class. Our opt, not our optimize, but our metrics class that we want to actually use in our program. And I've added additional namespace here because we've got the module ffi, have a module ext to do a side by side comparison with them. And simple metrics is just some common stuff between the two different metric implementations. But basically we say, hey, include simple metrics ffi, which is what we just defined on the previous slide. Initialize with the name, cool, that sounds great. And then we implement our update method, calling the simple metrics update function that was defined on the previous slide. And then we just add accessors. So we just do some little module eval, mainly that's here, just sort of off that on one slide. So now we do a little side by side comparison. We have struct, this is the Ruby side. There's our C side, kind of look really similar. It's kind of cool. And then here are the actual functions. So from the Ruby side, we've got all of our functions, simple metrics new, all the different parameters. And down here is the C side. And you can kind of see just how they map. So in our case right here, just using Ruby FFI was really not much more than writing the header file for the library we're talking about. It makes it pretty simple. And we're not actually doing any C whatsoever. So some meaningless metrics. When I developed this the first time, I was like, okay, let's do live simple metrics. Let's do an extension. Let's do the Ruby FFI version. And I just kind of timed my own personal time on how long it took me. It took me 40 minutes to do the FFI, basically that one. And that was a lot because I'm looking at the man pages or the documentation and checking things out. It's one of my earlier ones I ever tried. The traditional C extension took me over an hour. And I've done several C extensions. So I kind of knew how everything flowed. So that's it. One million calls to update. Hey, FFI took a second and some change. And actually this is probably on a different machine. So these don't apply much anymore. And the traditional C extension took 40.42 seconds. So there is an overhead cost for using FFI. And I'll show you an example here in a moment. And then the best part is, hey, this just runs on JRuby, flat out, we're done. And we'll do a demo. So we're actually just gonna do a quick little benchmark. So this is the benchmark screen to pair the traditional C extension versus our FFI extension. It's basically where I got those two numbers before. This is just loading up the library, include the benchmark. We're gonna do, we'll do a million. Require the library. And then if we have the extension, load it, if we have the FFI load it, and then basically just run the report. Everybody used benchmark before? It's a nice, happy little library. So, and then we'll print out the comparison and make sure everything looks pretty evil. So, 187, okay, so all the numbers look good. And here's our, oh, let's do a million. So you can see from the comparison between the C extension and the FFI extension on my MacBook Pro, it took about twice as long. And I wouldn't consider this a performance check at all. What we're essentially measuring here is the overhead of using FFI. The update method adds two numbers together. It couldn't be considered any type of intensive calculation whatsoever. So essentially what we're measuring here is the overhead of using FFI in a million calls and taking 0.42 seconds of extra time to run versus the extension. So, I think that's kind of interesting to know. And for everyone else to know, there is a little bit of an extra overhead in using FFI because it does have to do an additional layer of method calls. But, here's an even more fun thing. Same Ruby file, I just need a JRuby instead. So that's kind of interesting too. I ran it 10 minutes ago and it was much faster. What we have here is this is actually the exact same code. Just running in Ruby, accessing lib simple metrics, but it's running in JRuby instead of MRI. So, there was no code change whatsoever used. It could not use the extension, so that failed the load, so it didn't even use it. But the FFI run did pretty well. It kind of fell in between the two. So we had, what was the other one? All right, I'm gonna kill him. So it came in, but as with Java, the actual run took less time than the prep run just because Java compiler. And it came out with pretty good numbers, but I find that really amazing. You got the same code running in JRuby and MRI that's actually accessing a C library. That's a pretty powerful mojo there. So, hey, Noko Geary. These are some of the FFI based projects that are on the Wiki for FFI. Some of the more amazing ones that I think are pretty cool are FFI OpenGL and FFI OpenCL. So everyone knows about Snow Leopard. We have OpenCL on these MacBooks. There's already a library to access all of the OpenCL functions in Ruby. It may be one of the first languages to be able to access OpenCL besides C and Objective C, which I think is very cool. Another one is Ruby Game. It's in red down here, because I wanted to remember to make sure I mentioned it. Ruby Game has essentially, they use SDL, which is the simple something. I forgot what it was. Basically, it's used for a lot of graphics, audio, to do two dimensional graphics, little play-by-play games, all sorts of interesting stuff. Ruby Game has completely altered their entire infrastructure to use Ruby FFI. So you can have games written in Ruby using the SDL libraries, and they run on Ruby and JRuby. Flat out, no problem. It's pretty cool. And because of the Ruby Game one, it's actually created the Ruby SDL FFI, one of the other ones here. Another notable one is, how many people have used Tokyo Cabinet, Tokyo Tyrant? We've got a few. The Rufus Tokyo Library, access is Tokyo Cabinet and Tokyo Tyrant via FFI. It's still, the entire interface isn't implemented, but it's close enough for probably 80% of what you need to do. Let's see. So right now, I can take a look at anything else. You guys wanna look at more in-depth questions a little bit? Yes? Composite structs. Composite structs. It'll do them. You basically can create a structure and then one of your, actually it can go back a bit. So your layout here, I believe you can reference previous classes in your layouts, and it works that way. Unions, I don't remember if it does off the top of my head right now or not. I think it probably will. A couple of the other more advanced type C things, it does callbacks, function pointers, function pointers, returning function pointers, all sorts of things like that. But it does do all of those. Sure, FFI swig generator. I think it uses the output, oh it's not on, it's not listed on the wiki, but yes, there is the FFI swig generator, which can use, I think, a swig.ifile and just generate the Ruby code for it, which is pretty neat. And I think that's actually how the FFI OpenGL one is created and maybe the OpenCL one. Yes. No, it's actually, I'm not, well we can talk to Wayne, if he's here, I don't know if he is or not. But the way it works in JRuby is libffi is just built into JRuby. It's just compiled straight in and it just exposes all of this type of stuff to it. There's actually a fake JRuby FFI gem that just inserts the dependency here. It doesn't actually do anything because other people that are using, making gems that use FFI, they say, oh, I depend on FFI. Let me try to use it in JRuby. It fails. But yeah, there's a bunch of really good examples on the FFI wiki and let me see. Here they are. Here's the links. These are the links. GitHub, they actually transitioned away from the canine location onto GitHub. And so these are the best places right now. There's been a lot of activity on the Google groups. And Wayne has a blog, rarely blogs, but notable stuff gets put on there. So keep on coming with more questions. I can go more in depth into some examples. Yes. I can actually make the gem file itself like it's more or less the same as a normal C extension? There actually is no C extension. It's just Ruby code. So let's go look. I don't actually have this gemified, but I can show you basically what it looks like. So this is the full FFI that's used. It's actually pretty much what was on that slide that I showed you. This file right here, you just require at the top level. Well, let's take one step back. So normally when you have a library, you have your code. You say, I am gonna require simple metrics in this case. Simple metrics goes and requires all the different structures that are necessary in your system and loads everything up. So in our case, in the top level, you would just say require simple metrics FFI, which would define your interface right here. Just by including, just by requiring your definition file, this is DSL, and it requires FFI itself. And then by requiring FFI, you get this FFI managed struct in its namespace and extending the library makes this module essentially attached to that library. And when you actually use it, I'll show you that for just a second. This basically defines it all. So this is really all you have to do is just this one file and then require it. And then you're using it, you would say. So actually using it, you just call it. So in this class, this is our top level metric class. And it included simple metrics FFI, which is this one right here, just this module. I happen to have them in the same file, and we've been using it for a long time. So this module defines the simple metrics, essentially the API, we can call this the API module. And this will basically define these functions or these methods on it, and then you can call them directly. So right here, we're allocating a new implementation. So this is essentially attaching to the API thing. Get me a new instance of that API. And then whenever I want to call it, I call simple metrics update. This actually is the, this one invokes the lower level C methods and gets you a new simple metrics. You store it here, and then right here is actually calling simple metrics update. And this is calling that method right there. And you're passing it in essentially the structure, which is, which is this. That's the ad implementation is essentially the allocated memory for that structure. Really, really simple. Yes. Five dot simple metrics new, but below you're doing simple metrics updates. Why is one's module method, you know? One of them is this right here, seeing this simple metrics update is a method in this, or it's a method in this module, you include right here. So we're calling it. So those methods get injected in here, but one of the parameters for simple metrics update is essentially the structure. They all take a pointer as the first parameter. So you're calling this one and you're saying, okay, here's my parameters that I'm calling it with, but you still have to allocate the structure that gets passed to it. No, no, no, no, no, no. Yeah, I'm including the module up here. So here's the module simple metrics FFI, and it gets included right here. And so that attached function attaches the C function into this Ruby module. And so, actually, you know, let's find out. It all works. Thank you. It's good to learn something in your own presentation. Yes. The JNI thing. Okay. It uses libf5, but it does have a little JNI wrapper around it. And with the JRuby binaries, we ship 32 and 64-bit versions for Windows, Linux, Darwin, 3BSD, and Z-Linux for all people doing Linux on mainframes here if you're gonna want that. Yeah, it does have a little JNI shim that actually just puts the five-step. Cool. Awesome. Yes. I'd also say about JRuby, there are JRuby platforms that can't run FFI, so that's also a good kind of effect. Say again? There are JRuby platforms that can't run C extensions. Okay, well then, if you want to cover your basics of JRuby correctly, you probably have to write a job on it. But for most people, are using JRuby as like a VM instead of MRI. Everybody hear that? So I'm actually much faster than I thought I was gonna be. So question, comments, we can keep up the discussion or we can have an early break, whatever you like. Or we can talk about something else.