Talks

Going native with FFI

Going native with FFI

by Juan Carlos Ruiz

In the video "Going Native with FFI," presented by Juan Carlos Ruiz at Euruko 2021, the speaker discusses how Ruby, while a flexible programming language, can be enhanced by integrating with low-level languages like C for improved performance. Ruiz, a software engineer and mentor, highlights the advantages of using Ruby together with C, outlining various methods to create interfaces between the two. He elaborates on two primary approaches: MKMF and FFI (Foreign Function Interface).

Key Points Discussed:
- Introduction to Juan Carlos Ruiz:
- Ruiz's background and work with Easy Broker, his passion for open-source projects, and his history with Ruby and C.

  • Using MKMF:

    • MKMF generates makefiles for Ruby C extensions, enabling the compilation and linking of C code to Ruby.
    • The process involves wrapping C functions in Ruby, allowing Ruby modules to use these functions.
    • Example: Ruiz demonstrates with a small C extension that includes a simple function to print greetings and calculate squares, showing how these functions can be called from Ruby.
  • Adopting FFI:

    • FFI allows Ruby to call functions from C libraries directly without the need for additional C code or detailed bindings.
    • This method is multi-platform compatible and less complex. However, it might be challenging to maintain for more intricate C macros.
    • Ruiz gives an example of using FFI to manipulate PGM (Portable Gray Map) images, outlining the steps for implementing image inversion functionality through a C library.
  • Examples Demonstrated:

    • In the demonstration, Ruiz loads a PGM image, calls functions to invert colors, and saves the modified image using the Ruby interface tied to the C code.

Conclusions and Takeaways:
- The choice between MKMF and FFI depends on the project requirements:
- Use MKMF for greater control over C code or when wrapping smaller libraries.
- Opt for FFI for ease of use, especially in multi-platform projects where you want to avoid writing extensive C code.
- Ruiz encourages viewers to access his GitHub for the code examples shared during the talk and invites them to reach out for further discussions.

00:00:00.480 Juan Carlos Ruiz is a software engineer, community leader, and mentor who loves knowledge sharing and teamwork. He is part of the Easy Broker product team, a multiple listing service that helps real estate agents provide the best experience in finding a home. He has participated in various technological communities, trying to help others accelerate their careers in engineering and software development.
00:00:26.800 Juan Carlos has served as a mentor in the international Ruby Me 2019 program, assisting others in building a professional development experience through working on open-source projects. Today, he will demonstrate how Ruby can operate with C language, aiming for better performance.
00:00:42.320 In this talk, I will show you how to use Ruby to create an interface for compiled languages like C. If you have any questions, please open the stream on the left side panel and leave them in the stream chat. We'll get back to you later. Juan Carlos, please take it away!
00:01:10.240 Thank you! First of all, hi everyone, welcome to my talk titled 'Going Native with FFI'. My name is Juan Carlos Ruiz, and I'm super excited to be here. This is my first talk in English, so I'm a little nervous.
00:01:24.000 Currently, I work at Easy Broker and have been working remotely since 2013, which is before remote work became popular. I love open-source projects and really enjoy contributing to communities and creating new features. I am presenting this talk from Guanajuato, Mexico.
00:01:48.080 Today, I'll discuss how Ruby can work with the C language. I feel a bit like Milhouse trying to show his emotions about the old fox because C was the first language I learned about 12 years ago in college. Although I don't use it often in my work, it provided me with the fundamentals needed to advance my career.
00:02:14.080 Now, let's explore the options available to manage native code in Ruby. The first option is MKMF. MKMF is used by Ruby C extensions to generate a makefile which will correctly compile and link the C extensions to Ruby.
00:02:30.560 The process involves having a C library and wrapping it with Ruby code to generate the makefile. The result is a bundle file, which we can then use inside a Ruby module. Let's jump to the console and try this.
00:02:50.720 I've created a small project that includes a C extension. Let's take a look inside the C extension. I will open 'vim text', and inside the 'x' directory, we will explore the 'very edge' file.
00:03:04.640 As you can see, this library is very small, containing just two simple functions. The first function, 'hello', takes a name as a parameter and prints 'Hello, [name].' The second function takes a number and returns the square of that number.
00:03:30.720 Now, let’s see how to wrap this library in Ruby. The code for this is located in my Ruby extension file. We need to import the Ruby Edge library and create new functions to wrap the existing functions in the C library.
00:03:51.440 The Ruby code required to create the extension is quite simple. After compiling it, we generate a small bundle file that can be used inside a Ruby module.
00:04:02.720 We can call my helpers, which is the namespace I defined in the C library, and that includes functions like 'c_hello' and 'c_square'. I wrap these functions within the module using 'self.hello' and 'self.square'.
00:04:15.840 Let’s open the admin console to test this out. Inside this console, I can use 'rubyx.hello' and pass a string parameter. For instance, I will call it with 'Juan'. This should display 'Hello, Juan!'.
00:04:33.280 Similarly, if I call 'rubyx.square' and pass a number, I will get the result from the functions defined in the C library. Let’s go back to the presentation.
00:04:51.840 The pros of this approach are that we can use C functions directly in Ruby code while leveraging the existing Ruby Edge library to create the extension. However, we need additional C code to create the bindings, and this solution only works with MRI.
00:05:15.680 The other option for interfacing with C is to use FFI, which stands for Foreign Function Interface. This mechanism allows a program written in one programming language to call routines or use services created in another.
00:05:42.720 Here’s a simple example taken from the FFI documentation. You only need to import the FFI gem, extend your module with the FFI library, and import the library you want to use—like the standard C library.
00:05:55.680 We will attach the function 'puts', which is the equivalent of the C puts function. Finally, we can see how the method is used, calling 'myleap.puts' and passing the parameters we want.
00:06:09.120 The advantages of using FFI are that we do not need to add additional C code. An FFI extension is multi-platform and supports multiple Ruby implementations. On the downside, complex macros can be difficult to maintain.
00:06:37.760 Now, I want to demonstrate an example of how to use the FFI gem to encapsulate a C library. I will create a simple implementation that deals with PGM images. A PGM image is a grayscale image stored in plain text.
00:07:03.840 The header structure of a PGM file includes a magic number, dimensions, and the maximum gray value, which represents white and black.
00:07:08.480 Now, this is the content of the PGM header implemented in C. The file structure includes four methods: load_pgm, free_pgm, and invert_colors, the latter being the focus of this presentation.
00:07:15.840 Once we have the library implemented, we need to compile it to create a shared library. This shared library has the .so extension, which allows us to include it in our Ruby module that extends from FFI.
00:07:48.960 Next, we need to add a PGM class that inherits from FFI::Library. This class is responsible for mapping the image structure defined in the C library, allowing easier interaction with the Ruby code.
00:08:17.440 For each function we want to use from the C library, we need to use the FFI attach function. The first parameter is the name of the function in Ruby, the second is the name in the C library, followed by an array of parameters and the return type.
00:08:54.760 Let's see how this works in the console. Inside my example folder, I have a PGM image file. I will open this file to show you that it is plain text.
00:09:09.200 Now, let’s execute the code in Ruby to process this image file. I will create an instance for both the input image and the output image using the PGM bindings.
00:09:54.080 Next, we will load the image into the PGM instance using the load_pgm method, passing necessary parameters like the path to the image. Now that it is loaded, we can call the invert_colors function.
00:10:26.400 After inverting the colors, we will save the output using the save_pgm function, specifying the output path. Let's check our disk to see the output image.
00:10:55.200 As you can see, we successfully utilized the PGM library within our Ruby code. This library implementation simplifies interactions with C functions, allowing for efficient processing of image files.
00:11:34.320 In conclusion, use MKMF when you require total control over your C code, or if you need to wrap a small library. Use FFI if you prefer not to write C code for creating bindings, especially for projects favoring multi-platform compatibility.
00:11:56.560 The code for this example is available on my GitHub account. I hope you enjoyed this talk. If you'd like to chat, you can find me as Juan CRG90 on social networks.
00:12:20.480 Thank you for watching! If you have any questions, feel free to reach out.
00:12:38.960 (Audiences ask questions and discuss topics relevant to the talk.)