00:00:09
In this talk, I will discuss C++ extensions and give a brief overview.
00:00:15
My name is Charles Cornell. I've been working with Ruby and C++ for a while now. I've been developing web applications for about five years, although I have somewhat less time on web apps lately. Most of my experience involves backend development, specifically in semiconductor applications, which is my background.
00:00:36
So, what got me interested in both C++ and Ruby? I encountered some performance issues while transferring data to and from a SQLite database. In response, I created a C++ wrapper to streamline the process and wanted to utilize it across various applications.
00:00:49
I had a standalone C++ application that called this library, and I also wanted to integrate it with a Ruby application. Additionally, I had an Octave application to consider as well. For those unaware, Octave is a popular open-source alternative to MATLAB that is widely used in my field. Throughout this journey, I picked up various insights that I would like to share.
00:01:25
A common question is: when should you consider using a C++ extension? The most apparent reason is performance. However, there are other scenarios to consider. For instance, if there’s already a C library available that meets your needs, there’s no need to reinvent the wheel. Additionally, I wanted to call my existing C++ library from multiple scripting languages.
00:01:59
It's essential to identify which of these factors apply to your project, as they will inform your decisions on the appropriate tools to build your extension. Performance is one of the primary drivers. What can you expect in terms of performance? It largely depends on the coding choices you make.
00:02:39
If you make poor decisions, don't expect significant improvements. In fact, you might achieve far less than anticipated. Thus, it's crucial to determine what specifically is causing slowdowns, as this will influence what you optimize in your C++ extension and how you design your interfaces.
00:03:02
The interface definition can greatly impact the performance of your extension. For example, if you have a loop running multiple iterations, ensure that the work you push to your extension takes enough time to overcome the overhead of transitioning from Ruby to C++. I've seen performance improvements ranging from 3 to 4 times overall.
00:03:37
Simple advice for getting started: using tools like FFI, or Foreign Function Interface, is a good approach. This tool allows pure Ruby code to call C functions directly from any shared library without needing to compile anything, simplifying the process of integration.
00:04:07
The downside, however, is that FFI isn’t particularly well-suited for C++ because it doesn’t handle classes and C++ naming conventions. So, while it works effectively for C, it may not be the best choice for C++ integration.
00:04:42
Alternatively, another tool called Rice - a C++ DSL for interfacing with Ruby - allows you to define your interfaces in C++. This is especially useful if you prefer to leverage the strengths of C++ directly. Furthermore, SWIG is a widely used tool for generating interfaces that support multiple programming languages.
00:05:15
SWIG parses your C++ header files and understands C++ syntax, writing wrapper code to connect it to Ruby and other languages. It offers a range of advantages, including support for multiple languages, making it a strong candidate for any project that requires this level of flexibility.
00:06:01
Now, moving on to practical examples, let's discuss performance gains. Consider a test where I perform a hundred thousand inserts into a SQLite database. By using C++, I can significantly speed up this process by shifting the heavy lifting from Ruby into C++. This resulted in a 23.7 times speedup, simply by optimizing the data handling in the extension.
00:06:54
This improvement came from eliminating unnecessary overhead between Ruby and C++. Moving that loop into C++ meant there were fewer transitions required, which is something to keep in mind when optimizing for performance.
00:07:25
When it comes to building a SWIG module, the process is fairly straightforward. You run the SWIG command, specifying that you’re working with C++, and pass the interface files. SWIG generates a C++ file that includes the wrapper description, allowing you to compile it into a shared library.
00:07:57
The SWIG interface definition is relatively simple, focusing on defining names and utilizing some convenience utilities. SWIG has already prepared many common mappings for C++ to Ruby conversions, allowing you to work with standard STL containers easily.
00:08:53
For instance, if we take the STL vector as an example, in Ruby, you can interact with it almost like a typical Ruby array. You can push items in, check its size, and more—all while ensuring that you don’t encounter type errors thanks to SWIG’s type validation.
00:09:26
Another essential feature to discuss is exception handling. You can throw exceptions in C++ and catch them in Ruby, which significantly simplifies error handling. You can define base exception classes in C++ and set messages that will show up as Ruby exceptions when they propagate.
00:10:10
This means that integrating exception handling between C++ and Ruby becomes seamless, giving you a consistent experience. Additionally, you have the option to define various exception types related to different errors, such as SQL errors.
00:10:57
As we wrap up, I want to emphasize the benefits of using SWIG for building Ruby extensions. It automates much of the interface creation, freeing you up to write C++ code without worrying about managing the complexities of interfacing with Ruby yourself.
00:11:38
It’s a powerful tool that allows you to build robust, performant extensions while minimizing hassle. Thank you all for your time, and I look forward to any questions you might have.