πŸ’Ύ Archived View for ser1.net β€Ί post β€Ί ruby-vs-go-fight.gmi captured on 2024-03-21 at 15:08:44. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-07-16)

🚧 View Differences

-=-=-=-=-=-=-

--------------------------------------------------------------------------------

title: "Ruby vs. Go... FIGHT!" date: 2012-02-14T09:14:00Z

--------------------------------------------------------------------------------

Only sorta-kinda. Β I've been trying to use Go for some tasks for which I'd normally reach to Ruby; most recently was grabbing some date elements from a large-ish XML file. Β I know, I know... Ruby has the best XML library ever[1]Β built into it, but I'm more aware than anybody of the performance issues it has, so I tend to use it for only very small files. Β So when I needed to extract some information out of this fat XML, I thought I'd try Go.

1: http://www.germane-software.com/software/rexml/

The file wasn't so big that I was worried about memory use, but since I only needed a leaf from each branch of the tree, I chose the SAX-ish API anyway. Β That's going to make any code more bloated, but it wasn't too bad; the results are in Version 1[2].

2: http://pastebin.com/ugGzmMvN

After I got everything working, I got to wondering about the performance, so I wrote it again in Ruby[3]. Β I did **not**Β try to use the same logic; instead, I did it in what, for me, is more idiomatic Ruby code. The timings (I chose the average-looking times; I did not properly benchmark these, but I did run each program several times and then grabbed the middlin' looking one), which may or may not be surprising, look like this:

3: http://pastebin.com/qCtuVgMj

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Version    β”‚ Language β”‚ Total time (s) β”‚ CPU usage β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ═══════════║
β”‚ Version 1[4] β”‚ Go       β”‚ 0.113          β”‚ 96%       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version R[5] β”‚ Ruby     β”‚ 0.099          β”‚ 66%       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

"Hmmm", I hear you say. Β Well, the Go version *is*Β actually parsing the XML, and we all know XML for the bloated, expensive-to-parse format that it is. Β OTOH, Ruby is doing regexp on every line, and is additionally reading the entire file into memory first and splitting it into an array on line endings. Β Hmmm. Well, let's try a Go version that is a little more like the Ruby version. Β That's Version 2[6]:

4: http://pastebin.com/ugGzmMvN

5: http://pastebin.com/qCtuVgMj

6: http://pastebin.com/Hun5kKYc

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Version    β”‚ Language β”‚ Total time (s) β”‚ CPU usage β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ═══════════║
β”‚ Version 2[7] β”‚ Go       β”‚ 0.284          β”‚ 103%      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Yowsa! Β That's going in the wrong direction. Β Interestingly, it's now using more than one core of my CPU, so it's doing something thready underneath. Β Maybe it's because I'm reading the file line-by-line off the disk? Β Let's make it even more like the Ruby version; Version 3[8]:

7: http://pastebin.com/Hun5kKYc

8: http://pastebin.com/2sZ91GH9

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Version    β”‚ Language β”‚ Total time (s) β”‚ CPU usage β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ═══════════║
β”‚ Version 3[9] β”‚ Go       β”‚ 0.292          β”‚ 105%      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Definitely going in the wrong direction. Maybe it's the sre2 library? Let's try Version 4[10]:

9: http://pastebin.com/2sZ91GH9

10: http://pastebin.com/qjcSyR5M

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Version    β”‚ Language β”‚ Total time (s) β”‚ CPU usage β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ═══════════║
β”‚ Version 4[11] β”‚ Go       β”‚ 0.037          β”‚ 89%       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Ok, that's better. Armed with this, I went back to not reading the file entirely into memory in Version 5[12]:

11: http://pastebin.com/qjcSyR5M

12: http://pastebin.com/QTAyuddg

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Version    β”‚ Language β”‚ Total time (s) β”‚ CPU usage β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ═══════════║
β”‚ Version 3[13] β”‚ Go       β”‚ 0.292          β”‚ 105%      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version 2[14] β”‚ Go       β”‚ 0.284          β”‚ 103%      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version 1[15] β”‚ Go       β”‚ 0.113          β”‚ 96%       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version R[16] β”‚ Ruby     β”‚ 0.099          β”‚ 96%       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version 4[17] β”‚ Go       β”‚ 0.037          β”‚ 89%       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Version 5[18] β”‚ Go       β”‚ 0.034          β”‚ 91%       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Not a lot of difference; I did see a couple of runs where the CPU use dropped to 89% without affecting the total time, but these are pretty small numbers and we could be seeing actual run time being overwhelmed by the program initialization and what-not.

13: http://pastebin.com/2sZ91GH9

14: http://pastebin.com/Hun5kKYc

15: http://pastebin.com/ugGzmMvN

16: http://pastebin.com/qCtuVgMj

17: http://pastebin.com/qjcSyR5M

18: http://pastebin.com/QTAyuddg

Anyway, I thought it was interesting. Β Ruby is slow as all get-out, but for micro-tasks where most of the heavy lifting is running in native C (regexp in Ruby is native, as is IO), it's more than capable enough. It's also worth noticing that this was with ca. 30 lines of Go code, vs. 8 lines of Ruby code.