· 2 min read

The colon operator really is the fastest.

A scatter plot with error bars comparing four ways of generating an integer sequence in R. Both axes are logarithmic: number of integers from 10 to 1 billion across, and milliseconds from 10 to 10 billion up. The legend gives seq_len in red, colon in olive, seq.int in cyan and seq in purple. At the smallest sizes the four separate clearly — seq is slowest at about 6,500 milliseconds for ten integers, while colon and seq_len are around 250, more than twenty times faster. From about 100,000 integers onward all four converge and their error bars overlap completely, rising together to about 2 billion milliseconds at a billion integers. Only seq has points at the two largest sizes.

Way back when I was first learning R, I ran across an old listserv post that talked about how the colon (:) operator was the fastest way to generate a sequence. I never really thought about it, but I got in the habit of always using it whenever I needed a sequence.

Anecdotally, I knew from running a few simulations that seq() should be avoided if you’re generating a lot of small sequences repeatedly, but that’s a relatively rare case. Is the colon operator really that much faster than the alternatives — seq(), seq.int(), or seq_len() — in general cases?

Turns out the answer is “yes” — most of the time. Running a simple microbenchmark script, I tested the generation of numbers from 10110^1 to 10910^9 for each of the four functions. Then I plotted the mean with bars representing the 2.52.5th and 97.597.5th percentiles (on a log-log plot).

If you’re generating large sequences, it really doesn’t seem to matter which function you use, but for the common cases (e.g., slicing a vector or enumerating a loop), the colon operator outperforms the others. I’m not really sure there’s a lesson here except to trust R listserv posts and use : as often as possible. Code here.