RubyGuides
Share this post!

All posts by Jesus Castello

ruby tcmalloc

Profiling Ruby’s Memory Allocation with TCmalloc

Everytime Ruby needs to request memory from the operating system it has to call the malloc function. This function is part of the operating system itself, but there are alternative implementations you can use.

One of those implementations is Google’s tcmalloc.

TCmalloc is part of the Google Performance Tools suite.

You can use these tools to explore exactly how Ruby allocates memory.

And thanks to the LD_PRELOAD environment variable (in Linux) we can replace the system malloc with tcmalloc.

Enabling The Profiler

You can enable tcmalloc’s profiler with an additional environment variable (HEAPPROFILE).

This will produce the following output:

Here you see a confirmation that the profiler has been enabled, then we see the program’s output, after that we find the filename (profile.0001.heap) & the amount of memory used by our program (2 MB).

To read this file you will need another tool included with tcmalloc.

This will produce the following output:

Well, that’s just a bunch of memory addresses! You need a version of Ruby with debugging symbols to be able to see the function names.

Then you will get this output:

What you see here is how much memory was allocated by which MRI’s functions.

It’s interesting to know that aligned_malloc is the function used to allocate new pages for Ruby objects, stack_chunk_alloc is used by the GC itself during the marking phase, and objspace_xmalloc0 / objspace_xcalloc allocate space for strings, arrays & any other data that doesn’t fit in a RVALUE struct.

Now:

TCmalloc knows nothing about Ruby objects, the only thing it does is track calls to malloc, calloc & realloc to find out how much memory is requested.

If you want to get a heap dump at the Ruby level you can use ObjectSpace.dump_all. This gives you a JSON file with all the live objects in your application & their memory size.

But what tcmalloc can show you is a visualization of all the C functions that end up requesting memory.

This will open Chrome or Firefox with an SVG file that looks like this:

ruby heap profile

Not only does TCmalloc give you this nice profiling capability, but using it you can also increase your application’s performance by 4-9%! You could also try jemalloc, which is another malloc implementation that also includes a profiler.

Summary

You have learned how to use gperftools (Google Performance Tools) to visualize & analyze the memory usage of the Ruby interpreter. If you liked this I think you will also enjoy my upcoming book on Ruby internals 🙂

Thanks for reading!

Ruby Time Complexity

The Definitive Guide To Time Complexity For Ruby Developers

Time complexity is one of the most interesting concepts you can learn from computer science, and you don’t need a degree to understand it!

It’s interesting because it helps you see why a particular algorithm or program may be slow & what can you do to make it faster.

You can apply this to your own code, it’s not only for fancy algorithms that you find in computer science books, as I will demonstrate for you later in this article.

But first we need to talk about what is slow & what is fast.

Slow vs Fast

Is sorting 1 million numbers in 150ms (milliseconds) slow or fast?

I think that’s pretty fast, but this is not the question that time complexity is trying to answer.

What we want to know is how an algorithm is going to perform as the “input size” grows.

When we talk about “input size” we are talking about the arguments for the function or method. If a method takes one string as an argument then that’s the input, and the string’s length is the input size.

Big O Notation

With Big O notation we can classify algorithms according to their performance.

It looks like this:

In this case O(1) represents a “constant time” algorithm. That means that the algorithm will always take the same time to finish its work, regardless of how much work it has to do.

Another is “linear time”:

Where “n” represents the size of the input (string size, array size, etc). This means that the algorithm will finish its work in a 1:1 relation to input size, so doubling the input size will also double the time it takes to complete the work.

Here’s a table:

Notation Name Description
O(1) Constant Always takes the same time.
O(log n) Logarithmic Amount of work is divided by 2 after every loop iteration (binary search).
O(n) Linear Time to complete the work grows in a 1 to 1 relation to input size.
O(n log n) Linearithmic This is a nested loop, where the inner loop runs in log n time. Examples include quicksort, mergesort & heapsort.
O(n^2) Quadratic Time to complete the work grows in a input size ^ 2 relation. You can recognize this whenever you have a loop that goes over all the elements & every iteration of the loop also goes over every element. You have a nested loop.
O(n^3) Cubic Same as n^2, but time grows in a n^3 relation to input size. Look for a triple nested loop to recognize this one.
O(2^n) Exponential Time to complete the work grows in a 2 ^ input size relation. This is a lot slower than n^2, so don’t confuse them! One example is the recursive fibonacci algorithm.

Algorithm Analysis

You can start developing your intuition for this if you think about the input as an array of “n” elements.

Imagine that you want to find all the elements that are even numbers, the only way to do this is to read every element once to see if it matches the condition.

You can’t skip numbers because you may miss some of the numbers you are looking for, so that makes this a linear time algorithm O(n).

3 Different Solutions To The Same Problem

Analyzing individual examples is interesting, but what really helps understand this idea is seeing how we can solve the same problem using different solutions.

We are going to explore 3 code examples for a Scrabble solution checker.

Scrabble is a game that given a list of characters (like ollhe) asks you to form words (like hello) using these characters.

Here’s the first solution:

Do you know what is the time complexity of this code? The key is in the count method, so make sure you understand what that method does.

The answer is: O(n^2).

And the reason for that is that with each_char we are going over all the characters in the string word. That alone would be a linear time algorithm O(n), but inside the block we have the count method.

This method is effectively another loop which goes over all the characters in the string again to count them.

Ok.

How can we do better? Is there a way to save us some looping?

Instead of counting the characters we could remove them, that way we have less characters to go through.

Here’s the second solution:

This one is a little more interesting. This will be a lot faster than the count version in the best case, where the characters we are looking for are near the start of the string.

But when the characters are all at the end, in reverse order of how they are in found in word, the performance will be similar to that of the count version, so we can say that this is still an O(n^2) algorithm.

We don’t have to worry about the case where none of the characters in word are available, because the all? method will stop as soon as sub! returns false. But you would only know this if you have studied Ruby extensively, way beyond what regular Ruby courses offer you.

Let’s try with one more solution.

What if we remove any kind of looping inside the block? We can do that with a hash.

Reading & writing hash values is a O(1) operation, which means it’s pretty fast, especially compared to looping.

We still have two loops, one to build the hash table, and one to check it. Because these loops aren’t nested this is an O(n) algorithm.

If we benchmark these 3 solutions we can see that our analysis matches with the results:

Here’s a line chart:

big o chart

Notice a few things:

  1. The Y axis (vertical) represents how many seconds it took to complete the work, while the X axis (horizontal) represents the input size.
  2. This is a logarithmic chart, which compresses the values in a certain range, but in reality the count line is a lot steeper.
  3. The reason I made this a logarithmic chart is so you can appreciate an interesting effect, with a very small input size count is actually faster!

Summary

You learned about algorithmic time complexity, big O notation & time complexity analysis. You also saw a few examples where we compare different solutions to the same problem & analyzed their performance.

Hope you learned something new & interesting! If you did please share this post on social media & subscribe to the newsletter if you haven’t yet so I can send you more content like this.

Thanks for reading.

ruby c extension

How to Write a Ruby C Extension

Writing a C extension allows you to interact with Ruby from C.

You may want to this if there is a particular important method you would like to optimize with the speed of C, or if you would like to create an interface between a C library and Ruby.

Your First C Extension

Create a file named extconf.rb with these contents:

Then run ruby extconf.rb which will create a few files for you (Makefile & extconf.h). Don’t change these files.

Now create foobar.c, this name comes from the create_makefile line in extconf.rb. You can change it if you want but it has to match your .c filename for this to work.

Inside foobar.c you add this:

This is the basic skeleton of your C extension. Notice that you have to use the name you declared on extconf.rb plus the word Init_. In this case Init_foobar.

You can compile this by running make, and what you get is a so file which you can load into your Ruby application using require.

Writing A Ruby Method From C

You can create c functions that you can call from your Ruby code. This is how built-in classes & methods work.

All the Ruby methods have to return a VALUE. A VALUE is a Ruby object.

Example:

Now we need to attach this function to a Ruby class or module. You can create a new class or use an existing one.

Here I’m creating a module:

You can use the rb_define_method method to attach the C function to this module.

The arguments are:

Argument Description
mod Module object
“print_hello” Name of the Ruby method
rb_print_hello Name of the C function
0 Number of arguments this method takes, use -1 for variable arguments

Now run make again to compile the extension, and try it like this:

There you go, you just called a C function from Ruby!

Creating a Ruby Hash From C

Often you will want to create & manipulate Ruby objects from C.

You can do that using the C API.

For example, to create a hash & add some elements to it:

Two important things to notice here, first the rb_str_new2 function, this creates a Ruby string.

Then the INT2FIX macro, this converts a regular integer into a Ruby integer.

Now if you expose this function to Ruby using rb_define_method you will get a regular Ruby hash with one key (test) & a value of 1.

Don’t forget to recompile your extension everytime you make changes 🙂

Working With Strings

Your C functions can also take parameters.

For example:

Here we are taking a VALUE (ruby object) as an argument & then we make sure it’s a string with the RB_TYPE_P macro. After that we just print the string length using the RSTRING_LEN macro.

If you want a pointer to the string data you can use the RSTRING_PTR macro.

Example:

Also notice how there is another parameter here, self, this is the object receiving the method call.

If you want to define this method on the string class itself you would do it like this:

Summary

You learned how to write a C extension for Ruby so you can access all the power of C from your Ruby programs. You also learned how to expose C functions into Ruby & how to work with Ruby objects from C, like strings & hashes.

If you need more functions to interact with Ruby from C you may have to find them in Ruby’s source code & reading C extension code since this is not well documented.

Hope you found this article useful & interesting! If you did share it with your friends so they can enjoy it too.

ruby symbols vs strings

How Are Symbols And Strings Different?

Have you ever wondered about the differences between symbols & strings?

Let’s talk about that!

Strings are used to work with data.

Symbols are identifiers.

That’s the main difference. Symbols are not just “frozen strings“, they have different uses than strings.

When to Use Symbols

One of the most common uses for symbols is to represent method & instance variable names.

Example:

The :title after attr_reader is a symbol that represents the @title instance variable.

You can also use symbols as hash keys.

Example:

The benefits?

Symbols look better, they are immutable & if you benchmark string keys vs symbols keys you will find that string keys are about 1.70x slower.

Symbols can also be used in metaprogramming methods like send:

In Summary:

You should use symbols as names or labels for things (like methods) & use strings when you care more about the data (individual characters).

Converting Between Strings & Symbols

Sometimes you will get a symbol back from some method call & you will need to convert it into a string so you can compare it with other strings or do some other string operation.

You can do this using the to_s method.

For example, when using method_missing you get the name of the missing method as a symbol & you may want to check if this method name matches a certain pattern (like ending in ?).

Example:

You can also convert a string into a symbol.

The method to do this is String#to_sym:

Creating An Array Of Symbols

If you want to create an array of symbols you can use this code:

This saves you from having to type the colons & the commas.

Similar to the string version %w:

Ruby Symbols Video

Symbol GC

Another interesting fact about symbols is that there are different types, and the reason for that is that symbols were not garbage collected before Ruby 2.2, which means that they where not cleaned up from memory when no longer needed like regular Ruby objects (strings, hashes, arrays…).

You can see an example here:

You will notice that the total count of symbols increases by 10, just like you would expect since we are creating 10 new symbols.

But since Ruby 2.2 these symbols are removed from memory because they are just temporary & not being used by something else in this code.

If you try this code on a version of Ruby that has Symbol GC enabled both symbol counts will be the same.

Some symbols will never be removed from memory, these are called “immortal symbols”.

You can count them using the ObjectSpace module:

Notice that symbols created directly, like :a1 will automatically become immortal symbols. Creating a new method will also create an immortal_static_symbol to go with it.

So where do mortal symbols come from?

From strings converted into symbols with the to_sym method.

You can check this yourself using ObjectSpace.count_symbols.

And if you are wondering what’s an immortal_dynamic_symbol, it’s a symbol that has been promoted from mortal to immortal. This can happen when you create a method with the name of a mortal symbol.

Summary

On this article you learned:

  • Symbols are immutable
  • Symbols are not pointer to values, they are values themselves
  • Strings are for data & symbols are for identity
  • How to convert between strings & symbols
  • Symbol GC was introduced in Ruby 2.2 to clean up temporary symbols

Hope you learned something new!

Please share this post so it can reach more people 🙂

functional programming

Functional Programming In Ruby

Maybe you just heard about functional programming & have some questions.

Like…

What is functional programming exactly? How does it compare to object-oriented programming? Should you be using functional programming in Ruby?

Let me answer these questions for you so you can get a better picture.

What Is Functional Programming?

It’s not just a fad or a fancy word, it’s an actual programming paradigm that has been around for a long time but it has regained popularity recently.

And the basic ideas behind this paradigm are easier to understand than you think.

In functional programming we avoid changing state & we try to write “pure” functions.

Avoiding state change means that these functions don’t change anything outside of the function, no instance variables, no changing some object that was passed in…

None of that.

In a functional programming language (like Haskell) all data is IMMUTABLE.

There are things like variables, but they behave more like in the mathematical world. Once a variable is given a value, the compiler won’t allow you to redefine this variable with another value.

functional programming

Benefits Of Functional Programming

Immutability is the main advantage of functional programming because mutable data can lead to subtle errors that are hard to track.

Example:

In this example I want to find out if all the elements in the array are different from the first element.

To make this work we need to remove the first element from the array & at the same time save this element so we can compare it against the rest.

How can we do that?

We are working with an array & if you take a look at the list of available methods you will find that the Array#shift method does exactly what we want.

It works just fine until…

…you look at the value of arr after calling the method once:

Surprise!

The array lost one element (1) & we didn’t notice.

That’s how sneaky this kind of mutability bugs can be.

Fixed version:

Functional vs OOP

Should we all just adopt functional programming?

It may seem like all this immutable state makes functional programming the complete opposite of OOP, and in a sense it is, but there is still a way that the two programming paradigms can work together.

So no, there is no need to rush & go full-on functional programming. Ruby is designed for OOP anyway, so you would be fighting against the grain.

The good news:

You can still use the best ideas from functional programming & apply them to your Ruby code.

Reduce Mutability As Much As Possible

One way to do that is to STOP using attr_accessor, stick only to attr_reader.

After you do that then you need to keep an eye on strings, arrays & hashes.

There are methods that will change these objects:

  • Most methods ending in ! (like gsub!)
  • delete
  • update
  • clear
  • shift / unshift / pop / push

The first step is to be aware of these methods.

If you have to use one of these methods you can work on a duplicate object.

Given a string & a clone of that string:

We get these results when we clear the duplicated string:

This keeps the original string safe.

Partial Application

There is more to functional programming than immutable data & pure functions.

Like the partial application of functions, also known as “currying”.

Example:

Notice how the add method takes two arguments, but by using the curry method we can “preload” one of the arguments.

Then we get a lambda that we can call with just the 2nd argument.

Here’s another example:

One more example:

Summary

You have learned about functional programming, the core of it is pure functions & immutable data, it’s just a way to think about your code & not completely incompatible with OOP.

Thanks for reading, don’t forget to subscribe to the newsletter if you haven’t yet! 🙂