Adam Keys

Summarized using AI

The Mechanics of Ruby

Adam Keys • August 11, 2013 • Earth

In this talk titled "The Mechanics of Ruby" presented by Adam Keys at LoneStarRuby Conf 2013, the speaker explores the inner workings of Ruby, specifically focusing on Matz's Ruby Interpreter (MRI). The discussion addresses the performance criticisms of Ruby and evaluates the design trade-offs that characterize the language. The key points covered in the presentation are as follows:

  • Historical Context: Adam shares the story of Ruby's creator, Matz, who transitioned from writing in C, Smalltalk, and Perl to creating Ruby. This history underscores the language's evolution and the pragmatism surrounding its design decisions.

  • Criticism of Ruby: The speaker contrasts common criticisms of Ruby, such as its garbage collection efficiency, concurrency limitations, and the complexity of frameworks like Rails. He encourages a scientific approach to understanding these elements rather than merely complaining about them.

  • Ruby as a C-like Language: Adam posits that Ruby can serve as an effective way to write C code, providing examples that show how simple Ruby programs can sometimes execute faster or as efficiently as their C counterparts.

  • C Extensions: The presentation delves into creating C extensions for Ruby, detailing the processes involved, such as including the Ruby C API and managing Ruby objects within C. This exploration emphasizes the language's ease of use while navigating complex memory management scenarios typical in C.

  • Performance Analysis: The discussion incorporates practical tools like DTrace, explaining how they can illuminate Ruby's performance metrics regarding memory allocation and garbage collection. This data can then inform optimizations for Ruby applications.

  • Comparison with Other Runtimes: The speaker draws comparisons between MRI and other modern runtimes like JVM and V8, illustrating MRI's design choices that prioritize developer happiness over sheer performance.

  • Community and Ecosystem: Adam advocates for supporting alternative Ruby implementations, such as JRuby and Rubinius, which aim to improve the performance of Ruby applications beyond what MRI can achieve.

  • Key Takeaways: The talk emphasizes the importance of understanding Ruby's internals and employing insights gained to enhance development practices. Keys determine that while Ruby may not be the fastest language, its unique features promote developer engagement and satisfaction.

In conclusion, the discussion of MRI’s design inspires Ruby developers to dig into the intricacies of the language, fostering more proficient coding practices through a better grasp of Ruby’s mechanisms and performance attributes.

The Mechanics of Ruby
Adam Keys • August 11, 2013 • Earth

Ruby is a delightful language to work with. And yet, for years, we've been hearing about how MRI is too slow and inefficient for "serious" use. What does that mean? What would a "serious" Ruby runtime look like?

To understand these questions, we need to dig into the design of MRI. How does it work, and what are the underlying principles? What does common Ruby code look like as its executed by MRI? Once we've discovered what makes MRI tick, we can compare it to modern runtimes like Hotspot and V8 to understand why MRI is slower. From there we can take a look at JRuby and Rubinius and see how they seek to close the gap between Ruby runtimes and the competition.

In this talk, we'll dive into the internals of MRI, seeking to understand how it executes Ruby code. We'll look at ways to make our code faster, reduce garbage collection overhead, and find more concurrency in our programs. At the end, you will have a better understanding of how MRI works and how to write better code for it.

Help us caption & translate this video!

http://amara.org/v/FG9D/

LoneStarRuby Conf 2013

00:00:15.839 Today, I want to talk about the mechanics of Ruby, thinking in terms of Ruby as a software machine. What does that machine look like, and how does it work?
00:00:24.400 We will dig deep into the layers below our Ruby programs to understand how Ruby works, how it interacts with the operating system kernel, and how it operates on computers like Intel and ARM.
00:00:30.080 But first, let me share a story.
00:00:35.760 Once upon a time, there was a programmer named Matz who was very kind. Every day, he would write in Smalltalk and Perl, but he used C until one day, he decided to write an interpreter. This interpreter allowed him to write in Smalltalk, Lisp, and Perl, and he called the language he created Ruby. Over time, more people began using Ruby to write applications.
00:00:52.800 Eventually, someone discovered that writing web applications in Ruby was a significant breakthrough. Many years passed, and developers realized that while Ruby is wonderful, it has its trade-offs. For instance, Ruby programs may not perform as fast as those written in C or Java. From that day forward, everyone learned to make engineering trade-offs to balance happiness and performance.
00:01:11.040 Now, I have a bone to pick with people who criticize Ruby. Often, discussions about Ruby come across as condescending, focusing on its perceived shortcomings or design trade-offs.
00:01:22.000 For instance, some people argue that Ruby is not concurrent, that the garbage collection (GC) is slow, or that Rails is overly complex. In isolation, these criticisms have some truth, but what critics often overlook is the subjective nature of design decisions. If someone were to lament not receiving a peanut butter and jelly sandwich, would you really feel obliged to make one for them? It's essential for individuals to recognize that they can create their own solutions instead of relying on others.
00:01:42.159 That said, while many criticisms are valid, they shouldn't deter anyone from using Ruby. Instead, I propose we adopt a scientific mentality, examining these design trade-offs to understand why they exist, how they came to be, and what can be done about them moving forward.
00:02:02.000 As scientists, we should enjoy exploring Ruby's mechanics and appreciate the whimsiness that comes with programming. Instead of complaining about GC overhead or thread management issues, let’s celebrate the fact that, today, we didn't have to use malloc at all. We'll focus on optimizing for enthusiasm!
00:02:22.000 Let’s also acknowledge some fantastic resources, like the Real Talk podcast, which dives deep into kernels, compilers, assembly, and more. It's an excellent way to think critically about programming.
00:02:34.400 Here's a hypothesis: Ruby is an unusual yet effective way to write C programs. If you were to pair program with me, I might mention this concept. Let me explain what I mean.
00:02:46.320 Consider this simple Ruby program that prints the Beatles' names; it consists of five straightforward lines. In contrast, the equivalent C program is much longer and, in fact, executes slightly slower than the Ruby code. This challenges the notion that C is always faster than Ruby.
00:03:08.000 We can write Ruby to resemble C code, and here’s how we can deconstruct that Ruby program. First, we create a Ruby module and define a singleton method. Then, we set up a fixed-size array and populate it with the Beatles' names. Lastly, we implement a for loop to print each name. The constructs used here mirror what a C extension might include.
00:03:30.080 When writing a C extension, the first step is to include the Ruby C API by adding ruby.h, which gives you access to various functions essential for C extensions. You would then declare variables and create a reference for the Beatles module, assigning it nil. You also declare a C function for the print functionality.
00:03:53.040 A crucial aspect of Ruby C extensions is understanding that a "VALUE" in this context is a Ruby object, which corresponds to complex pointers and unions in C. The "init_beatles" function is called when the extension loads, where you set up objects for your Ruby programs using C.
00:04:16.080 You call module_new to create the Beatles module and attach a singleton method, which links it to the print function you declared earlier. This method accepts the module as its self object and returns a Ruby object.
00:04:26.720 In this method, you will pre-declare several Ruby objects such as 'beatles', 'name', 'puts', 'kernel', and 'args'. To create a new fixed-size Ruby array in a C extension, use 'ruby_array_new', and for literal strings, utilize 'ruby_string_new'.
00:04:44.080 We then get a handle on the Kernel module so we can call "puts" on it. The internal method 'rb_intern' behaves similarly to creating a symbol in Ruby. At this point, we have everything set up to print the Beatles' names.
00:05:01.760 Now, we’ll loop through the Beatles array and print each name using the 'rb_apply' function. This function effectively sends the puts command to the Kernel module.
00:05:24.080 It’s interesting to note that there are no calls to malloc or free in this C extension. We haven't made any direct system calls either. While we don't achieve amazing performance, we can observe how Ruby code can seamlessly translate into C constructs, providing a new gaze into Ruby's capabilities.
00:05:44.000 We can see how Ruby handles various operations while allowing developers to avoid the intricacies normally associated with C, particularly memory management. As developers, we can become proficient without diving too deeply into the complexities of lower-level languages.
00:06:09.280 However, the underlying reality is that Ruby executes in a way that creates garbage, which we must manage judiciously. Benchmarking simple tasks, such as arithmetic operations, offers insights into performance overhead, especially when it comes to garbage collection.
00:06:30.560 A tool like DTrace can help us probe into running programs to observe garbage collection activities and when malloc calls occur. This sort of insight can direct our understanding of Ruby's performance.
00:06:52.080 Using DTrace, we can map calls to object creation and free events during execution, providing a clear view of how Ruby handles memory and garbage.
00:07:11.200 The ability to visualize the inner workings of Ruby and its relationships to memory management is key to improving performance. By analyzing these processes, we can gain valuable knowledge that will impact our programming practices.
00:07:32.000 Furthermore, it's important to realize that Ruby is not just any programming language. MRI (Matz's Ruby Interpreter) is uniquely designed and doesn't always adhere to patterns we see in other runtimes, like JVM or V8.
00:07:53.520 Understanding Ruby's architecture, its strengths and limitations, explains why certain design decisions were made. Emphasizing developer satisfaction over raw performance is a trade-off inherent to MRI.
00:08:11.120 One primary takeaway is that MRI is fundamentally a scripting language that provides convenient access to text manipulation and object-oriented programming.
00:08:31.920 Despite its utility, Ruby was not built with high concurrency in mind, contrasting it with platforms like JVM and Erlang that prioritize performance for concurrent applications.
00:08:49.840 It's essential to accept that while Ruby may not reach the same performance heights as these other frameworks, the trade-off is its ease of use and developer satisfaction.
00:09:06.880 Ruby’s development community largely embraces standardization, which varies from perceptions in the Western tech culture. Understanding this cultural difference provides critical insight into Ruby's evolution.
00:09:26.560 Being aware that Ruby does not follow typical semantic versioning practices is crucial as well, impacting how developers approach upgrading and transitioning between versions.
00:09:42.960 As we continue to engage with Ruby, it's vital to remember that MRI is just one implementation of Ruby. Supporting alternatives like JRuby and Rubinius can enable faster performance for Ruby applications.
00:09:59.680 Encouraging these platforms allows us to explore Ruby as a language, separate from the specifics of MRI, opening up opportunities for greater efficiency.
00:10:20.480 Through this lens, we can examine the mechanics of MRI and appreciate how these inner workings affect our development experiences.
00:10:38.480 My suggestion is to apply the knowledge you gain from understanding Ruby internals to everyday programming tasks. Familiarize yourself with the interactions and limitations to become a better coder.
00:10:56.000 Finally, as we wrap up this discussion, remember to take this knowledge and apply an experimental approach to your work. Measure performance, iterate, and engage with the Ruby community to enhance your skills.
00:11:14.000 Applause! Now, let’s reflect on how MRI operates as a virtual machine and the significance of the kernel on computers like x86.
00:11:34.000 Understanding Ruby’s environment and how it parses through code gives you tremendous insight into optimization opportunities.
00:11:52.000 The exploration of performance through garbage collection and memory management in Ruby ultimately helps streamline the development experience.
00:12:06.000 For developers eager to optimize, taking advantage of available tools is crucial. Tuning performance based on empirical data helps mitigate issues.
00:12:22.000 Effective programming relies on identifying bottlenecks within your applications, enabling more focused development efforts and higher efficiency overall.
00:12:39.520 In conclusion, remember that Ruby is a dynamic and adaptable language. It varies significantly from traditional compile-time languages.
00:12:57.200 By engaging with Ruby at a deeper level, whether through C extensions or simply understanding its design, you can unlock numerous possibilities.
00:13:14.000 Your ability to delve into Ruby’s mechanisms to optimize and innovate will serve as powerful assets.
00:13:30.000 So as you go forward, keep an inquisitive mindset and transform your insights into action within your Ruby projects.
00:13:44.000 Thank you for your time, and let's continue exploring the wondrous world of Ruby together.
Explore all talks recorded at LoneStarRuby Conf 2013
+25