
Ruby is too slow for programming competitions - ClifReeder
http://blog.clifreeder.com/blog/2013/04/21/ruby-is-too-slow-for-programming-competitions/
======
RyanZAG
A lot of people are talking about the problem itself - which is interesting -
but very often you run into real problems which cannot be simplified so easily
(NP hard problems, many different other cases). In addition, when you need to
do important calculations on a server for your product and you're using Ruby,
you may very well end up requiring 10x as many servers. This is pretty massive
when you consider an AWS server can be $350/month with Go (1 large instance),
and then $3,500/month with Ruby - if you can even parallelize your problem
that well - generally you can't. That's a lot of startup runway being eaten
for not that much benefit.

Of course, most products don't really need to do calculations outside of a
database and this is why Ruby has taken off so well - but it's still important
to realize that choosing Ruby over PyPy/Go/Whatever really can be a very
expensive choice in the long run when your product suddenly relies on some
unique math solutions, and having half your product in Ruby and half in C is
awful to have to deal with.

The solution to this is a project like PyPy for Ruby, but it doesn't seem to
be coming...

~~~
Derbasti
> having half your product in Ruby and half in C is awful to have to deal
> with.

Why? In many cases, only a tiny fraction of your code needs to be fast. In my
experience, it is very reasonable to code that part in some fast language
while delegating the bigger (and often more complex) part in a higher level
language such as Ruby.

~~~
lbrandy
Everyone always agrees with this and, in my experience, has never really tried
it. It works better in theory than in practice (or, at a minimum, it works
only on a small subset of the types of problems that a naive view might
otherwise lead you to believe). The problem is the data isn't organized in a
way to be computation efficient, and all the C code in the world isn't going
to make chasing pointers to pointers to pointers to ints fast.

A ton of work has to go into computation-friendly data organization (see all
of numpy, scipy, etc) before you can ever actually optimize effectively.

EDIT: since all of my responses are of the form: "oh no I did it once with
great results", I'll retract the part where I said "have never really tried
it". I'm not arguing it's never worked. I'm arguing that the set of likely
performance problems and the set of easy-to-optimize-with-C problems don't
overlap as much as people would like to believe.

~~~
vidarh
I have tried it. My MSc dissertation required a lot of computation (weeks of
runtime _after_ I'd rewritten critical portions in C++), and I'd do it that
way again.

For many types of problems, putting the data into an efficient format before
passing it to your C/C++ code is trivial in terms of both difficulty and time
spent.

I prototyped everything in Ruby, profiled, and then rewrote a handful of the
critical parts in C and C++.

~~~
timr
I've done it too, and I think most of the responses to the GP are missing the
point. Can it be done? Sure. But it's a pain in the ass. It's not nearly as
trivial as people suggest. It's basically only worthwhile when you know you
have the resources to build an infrastructure of re-usuable fast modules that
get coupled with a scripting language interface.

Even when you give up on writing true "hybrid" code (i.e. "just write modules
for the slow parts in C++", which is a _gigantic_ pain), and instead try to
write driver code that calls standalone compiled applications, you find
yourself having to write a lot of duplicate code to parse/validate I/O on both
sides of the language boundary. It sucks, and there's no way to make it not
suck.

I'd go so far as to say that for problems where processing speed is a known
requirement from the start, you should just give up on interpreted languages.
Fast interpreted languages have been right around the corner for long as I've
been writing code, but they sure seem to be taking their time.

~~~
vidarh
I didn't find it hard at all, nor a pain in the ass. I wrote my code. Profiled
it. Found the few, small bits that needed to be fast and treated the Ruby code
as an executable spec for the C, and rewrote it very quickly.

There is SWIG support for Ruby, as well as the Ruby FFI implementation and
RubyInline, offering various degrees of simplicity, but none of them are hard.
The Ruby extension interface is more work, but even that is fairly simple to
work with. I've taken C libraries and wrapped them in Ruby in minutes using
SWIG for example.

Overall I often these days choose to write in Ruby first and replace _even for
applications that I intend to write the finished version of entirely in C_.
Prototyping in Ruby and rewriting in C once I have a working version is faster
to me than iterating over a C version.

~~~
timr
_"I wrote my code. Profiled it. Found the few, small bits that needed to be
fast and treated the Ruby code as an executable spec for the C, and rewrote it
very quickly."_

The fact that the speed-critical parts of your problem could be reduced to a
_"few, small bits"_ indicates that we're talking about entirely different
things.

I was in a problem domain where writing the time-critical pieces in a
different language meant ensuring that huge amounts of working-set data were
available to that optimized code. Efficient access to that data meant storage
in native format, which meant that supporting two languages would require a
nasty I/O layer for the interpreted language. Not worth it.

And that's just for the problems where it was possible to consider using
Perl/Python at all. For some problems, the memory overhead of Perl/Python data
structures ruled out use on all but the smallest problems. Again, not worth
it.

It's nice that the speed-critical parts of your problem were reducible to a
few lines of code, but that's not often the case. For most real problems, I/O
is a huge concern, and can't be dismissed just by pre-processing the input.

~~~
emn13
Also, it's a weird wonderland in which you know exactly which parts need to be
fast, reimplement those, and then never need to touch those bits again. In my
experience, that's not the case - you're going to want to tune and alter your
algorithm, and if it's API needs to go through several languages, it's going
to be a pain. Every time you decide you need a slightly different input format
or data store or set of hyperparameters, you'll likely be changing many more
files than you would in a one-language solution, and in a way that's hard to
unit-test and hard to statically type.

It's certainly doable, and definitely the right way to go if you want number
crunching in a high-level language, but it's not a free lunch. It's certainly
not a quick one-time conversion and then you can forget about it.

------
xyzzyz
_Sure, I knew Ruby wasn’t going to be zomg fast, but I always assumed that if
I chose the right solution and wrote in an efficient manner (memoizing,
storing values/lookups that would be used later, limiting search spaces, etc),
my ability to write code quickly and concisely mattered more than sheer
processing speed._

 _I was wrong._

Sure, the author is wrong, but not because Ruby is slow (it is, though). He's
wrong, because he did not find the right solution for the problem. Instead, he
wrote a brute-force solution with a simple optimization, and did not realize
that this is the real cause of his code under-performing, blaming the Ruby's
slowness for it, when he really should have blamed the Dunning-Kruger effect.

~~~
ConceitedCode
Even if he did brute force it, he did the same thing with Go and it was much
faster. So by comparison Ruby is slow and his conclusion is sound. Yes he
could have optimized it, but that just means he could of optimized the Go
solution too.

~~~
klochner
Let's consider his conclusions:

    
    
        ruby is slow:                         sound
        ruby's slowness is his main problem:  not-sound
    

He's not going to win programming contests with brute-force solutions. Even
his brute-force method makes unnecessary computations - why keep running after
n^2 is out of range?

> memoizing, storing values/lookups that would be used later, limiting search
> spaces, etc

He did none of that. Kudos to OP for benchmarking & profiling though -- the
speed of Go is impressive.

------
nikic
I also come to the conclusion that Ruby-like languages are just not the right
tool for this kind of problem, but it should be pointed out that you would
have been able to solve the 10^14 data set easily, even with Ruby.

I was solving that problem with PHP and got it running reasonably fast (1s per
range) for ranges up to around 10^60. PHP is probably faster than Ruby, but
it's still in the same area.

The first trick you can use is that you don't have to go through all numbers
between start and end, rather you can directly only traverse the palindromes
(i.e. you will only have to increment one half of the integer).

The second trick is that only numbers consisting of 0s and 1s (and a 2 at the
start/end or the middle) can satisfy the condition (exception: the number
"3"). This further massively reduces the search space.

There are more criteria that you can use to narrow down the numbers to
consider (e.g. the number of non-zero digits can't be greater than 9). But in
the end, cracking the 10^100 range is probably not possible in a language like
Ruby or PHP. Using something like C++ on the other hand it becomes a
triviality.

~~~
bvdbijl
Yep, in a competition you want a language that had as little magic as
possible. Languages like Ruby (not necessarily dynamic languages!) are just
not really suitable for it

~~~
chc
This seems like such a vague, insubstantial piece of advice. I've seen it
before, but never in a more concrete form. I don't see any "magic" in Ruby.
How do I determine if a language is magic? Usually, the best I can figure is
that they're either confusing syntax with semantics or they're actually
thinking of some library like ActiveRecord rather than Ruby.

Really, for pretty much anything I could conceivably think of to call "magic"
in Ruby (e.g. method_missing), there's equivalent "magic" in Python (e.g.
__get_attr__). And yet PyPy is pretty darned fast.

~~~
robryan
I think people are referring to language features that are very powerful and
concise but have horrible performance when you get large data sets like this.

Something like the inject method, at least in my limited use of ruby with
these kind of Google Code questions, seemed problematic.

------
bajsejohannes
For comparison, I translated it pretty directly to Python:

    
    
        import sys
        from math import sqrt
        
        def string_palindrome(num):
            s = str(num)
            return s == s[::-1]
        
        sys.stdin.readline() # throw away first line (number of cases)
        
        for count, line in enumerate(sys.stdin):
            found = 0
            start, finish = [int(num) for num in line.split(" ")]
            sqrt_start  = int(sqrt(start ))
            sqrt_finish = int(sqrt(finish))
        
            for x in xrange(sqrt_start, sqrt_finish+1):
                if string_palindrome(x):
                    square = x * x
                    if string_palindrome(square) and start <= square <= finish:
                        found += 1
        
            print("Case #%d: %d" % (count, found))
    

For the first 15 test cases I get the following run times:

    
    
        pypy 2.0-beta2: 1.6 seconds
        pypy 1.9:       2.1 seconds
        ruby 1.9.3p194: 3.7 seconds
        python 3.3.0:   5.9 seconds
        python 2.7.1:   6.2 seconds
    

The integer version for checking palindromes was also much slower in python.

Edit: Even though the interger version of _palindrome is slower in pypy,
there's another optimization that works there, but is slow in cpython. It
brings the run time down to 0.6 seconds!

    
    
        def half_string_palindrome(num):
            num_str = str(num)
            num_len = len(num_str)
        
            for i in xrange(0, num_len // 2):
                if num_str[i] != num_str[num_len - i - 1]:
                    return False
        
            return True

~~~
raverbashing
Check with PyPy if you can

~~~
TillE
I just tested, and it should be about 6x faster than Python 2.7. About 1.0
second for the first fifteen cases.

------
minwcnt5
The following site allows you to search for people's solutions by language:

<http://www.go-hero.net/jam/13/solutions>

There are actually several people who solved this problem (including both
large inputs) using Ruby.

And for those asking about Python, most Code Jam problems are apparently
calibrated to be solvable using Python. Keep in mind that it's one of the
languages Google uses internally and Guido used to work there, so they
probably want to support it. From <https://code.google.com/codejam/problem-
preparation.html>:

"Where possible, if you allow multiple programming languages, make it so that
all languages are fast enough to solve the problem. Occasionally that's
impossible; in Code Jam we generally calibrate by making sure a somewhat
optimized Python program can solve a problem. Sometimes, though, there's just
no way to have Python succeed with the best algorithm and C++ fail with a
worse one. In those cases we'll typically decide that sometimes in life, you
just need to use a faster programming language. We try to avoid making that
decision if we can."

------
wting
First of all, there are plenty of people who solved both large inputs with
Python/Ruby (with and without pre-computing all the fair and square
numbers).[0][1]

As others have stated, the contests are designed such that language shouldn't
matter and problem sets are deemed solvable with Python as a baseline. For
this particular question, the official contest analysis goes into detail on
how to reduce the solution space:

[https://code.google.com/codejam/contest/2270488/dashboard#s=...](https://code.google.com/codejam/contest/2270488/dashboard#s=a&a=2)

Now the issue becomes the trade off between development speed vs runtime
performance.

Is it possible that you might solve the problem with a worse solution in a
better performing language? Yes.

Is it possible you might finish a solution within the time constraints in a
more expressive language? Yes.

If something is CPU bound in production, then going with a better performing
language is a viable consideration.[2] However, _my_ goal in programming
contests is often to become a better programmer in the process. As a result, I
choose to focus more on improving algorithmic complexity which is language
agnostic.

[0]: <http://www.go-hero.net/jam/13/solutions/0/3/Ruby>

[1]: <http://www.go-hero.net/jam/13/solutions/0/3/Python>

[2]: For the pedantic, I understand that language performance is different
from implementation performance.

Edit: Corrected links, thanks victorhn.

~~~
victorhn
Correct Link for [0] is

<http://www.go-hero.net/jam/13/solutions/0/3/Ruby>

And the most difficult version (L2) was solved by 19 contestants.

------
electrograv
Wow, 5 minutes in Ruby, and less than a second in Go? I know scripting
languages are slow, but it's easy to forget just how much slower. (EDIT:
Corrected misread number thanks to dljsjr).

I don't want to detract from the article's main point with a cliche
"algorithmic optimization beats fine tuning", but I think it's worth
mentioning. Unless I'm mistaken, this particular problem can be solved about
10,000,000x more efficiently.

    
    
        > Given two numbers X and Y, return how many numbers in that range
        > (inclusive) are palindromes, and also the square of a palindrome.
    

I would just count the digits of X and Y, then ranging between those counts,
apply the following algorithm: For N digits, enumerate all numbers of N/2
digits satisfying the bounds X and Y. If N is even then simply mirror the
first N/2 digits. If N is odd, mirror the first N/2-1. Finally, check the
bounds of the resulting number against X and Y to filter out edge cases.

If the original algorithm runs in 10^N time, this would be 10^(N/2). For N=14
as mentioned, that's roughly a speedup of 10,000,000.

Edit: It seems I misinterpreted the question (which sounds like you're
supposed to enumerate all palindrome numbers X>=N>=Y, and also print out the
squares of those numbers). This is actually asking for a particular type of
palindromes -- those which are palindromic squares of palindromes. As macobo
helpfully points out, you can drastically optimize mathematically from this
additional filter.

~~~
ClifReeder
To clarify, the Go program (which took under a second to run) was doing the
same processing as the full Ruby program that took 50 minutes to run.

~~~
KVFinn
Others have asserted that they aren't doing the same processing and the GO
program is not correct.

<https://news.ycombinator.com/item?id=5586152>

~~~
jff
Go is not an acronym

Go is not an acronym

Go is not an acronym

See also: "ham" (amateur radio) is not an acronym

------
bambax
I'm not a rubyist but it appears the OP was doing something like this:

    
    
      for each range (start to sqr(end))
    
        for each number in the range
    
          check if the number is fair and square
    

If you do this you're checking the same numbers many times (total count of
numbers checked is over 3 hundred million calls...)

The right approach is to first compute _all_ fair and square numbers between
0-10^14, store the result, and then simply check how many are found in each
range.

It turns out there are only 39 fair and square numbers between 0 and 10^14.

I participated in Code Jam 2013 and passed the qualification round using
Python; after precomputing the fair and square numbers, checking ranges was
pretty much instantaneous (for large input 1).

~~~
eric970
Sounds right to me as well. But hey, isn't it much easier to blame the
language and its VM? Or interpreted languages in general?

------
Aaronneyer
The Go solution is incorrect. It's not using int64, and most of the finishes
are bigger than a 32bit int. If you run it on the following input: """ 2 8
10000200002 1 500 """ You will see that the loop that runs from start to
finish is not running on the first case, because it sees the finish as 0.
Haven't tested it using int64, but I would imagine it would be much
slower(Still a decent bit faster than Ruby presumably), because any heavy
computation for this problem was removed.

~~~
gnuvince
I noticed that as well [1], and rewriting the code to use int64 (and thus
cover the entire range) makes the Go code run in 2m40s. Still faster than
Ruby, but not as incredible as the author thought it was.

[1] <https://news.ycombinator.com/item?id=5586152>

------
moron4hire
These sort of contests are not tests of speed of computation, they are tests
of algorithmic knowledge. The final data sets will (usually) all be designed
to make even the most optimized implementations of the wrong algorithm fail.

------
gnuvince
I'm sorry, but is the Go program correct? I ran it on my machine, and for Case
#1, it reports a solution of 1, while there are actually 19.

EDIT: I modified the Go program to use int64 instead of int (the upper bound
of the first case is not a valid 32-bit integer), and the execution time is
now much higher: 2 minutes 41 seconds on my laptop.

~~~
darkgray
Go 1.1 made int default to int64, so it may depend on how up-to-date your
particular version is. God knows what the blog poster uses, though.

~~~
gnuvince
I'm still on Go 1.0.2. However, I strongly doubt that having 1.1 yields the
correct result in the amount of time that the article boasts; I rewrote the
program in C (direct translation), and with both clang and gcc, I get timings
of around 2m05s. It's hard to believe that for a CPU-bound task, a Go program
would be 200x faster than a C program.

~~~
darkgray
I'd agree it's dubious, but I was mostly commenting on the "correctness" of
the code. It'd work on Go 1.1, so it's technically "correct". Sometimes. :)

~~~
gnuvince
Fair enough.

------
dllu
It is true that Ruby tends to be slow for programming competitions, especially
if the computation is done serverside such as Codeforces.com.

Notice that your solution runs in O(sqrt(N)) time. If N were up to 10^14, with
10^3 test cases, then your solution could have to deal with up to 10^10
iterations... which is dangerous even for a fast language (since your CPU can
only do about 10^9 things per second). As a rule of thumb, one should only
ever deal with at most 10^8 things. Even just incrementing an integer 10^10
times can take some time (fortunately, not every one of the test cases were
10^14 so your Go solution took under a second).

However, Problem C of Google Code Jam qualification 2013 can be easily solved,
even with a slow language, by making some observations:

1) Instead of iterating through numbers and checking if they are palindromic,
you can just iterate through the numbers with half the number of digits and
generate the palindrome by mirroring the half. A naive solution that uses this
would run in O(N^(1/4) log(N)^1.6) instead of your O(N^(1/2) log(N)^1.6)
solution. (The log(N)^1.6 is due to the time taken for multiplication,
assuming the Karatsuba method).

2) With a small amount of math you can figure out that for any fair and square
number N = M _M, then for any digit s in M, we must have s_ (sum of all digits
in M) < 10\. Thus solutions are of the form:

case 1) Contains up to nine digits 1. (e.g. 101, 111111111)

case 2) Contains up one digit 2 and up to four digits 1. (e.g. 1002001, 2)

case 3) Contains up to two digits 2 and up to one digit 1. (e.g. 200010002,
202)

case 4) Contains one three. (i.e., just 3)

A naive solution using observation 2 may run in O(log(N)^5.6) time.

3) Use combinatorics to figure out count the ways to arrange the digits in the
4 cases shown above, rather than iterating through them. As it turns out, it
is unnecessary to do this since even using an O(log(N)^6) solution I passed
the 10^100 input. Since there are only about 40,000 fair and square numbers
from 1 to 10^100, you can easily precompute them and find them by binary
search.

------
thinkpad20
This is a problem which is CPU-bound rather than IO-bound. Ruby, Python and
the like won't typically show a great difference in speed when the majority of
time is spent in IO operations (like web crawling, or text processing, running
web apps, querying databases, etc), because the bottleneck on performance
comes mostly from the slow reading of files and network sockets. In this case,
though, the operations are mostly dependent on just doing computation, so a
high-level interpreted language like Ruby really shows its limitations.

This is one of the (many) reasons why although languages like Ruby and Python
are great for rapid development, on-the-fly updates and other reasons, it can
pay dividends to be fluent in a high-performance language. It wouldn't even
need to be C (although this wouldn't be hard to do in C) -- a language like D
would give you excellent performance and nearly as easy development as Ruby.

------
ClifReeder
Hey everyone, thanks for all the input on this. As I mentioned in the post,
programming competitions aren't my forte, and I have a lot to learn in that
arena. All the feedback on how this should have been done more efficiently is
great.

In the future, I obviously need to work on coming up with better algorithms
for solving these kinds of problems, but I'll probably continue to try and use
Go because it's nice to have that fallback to sheer speed when my algorithm
isn't great.

~~~
Lonny_Eachus
Just a suggestion, but I would also give some thought to efficient coding
style, not just algorithms. For example, you could have speeded up your I/O
dramatically.

~~~
Lonny_Eachus
Correction:

I benchmarked the I/O improvement I was thinking about. It actually only
resulted in an improvement of about 30%. Worthwhile, probably, but not as
"dramatic" as I thought it would be. Mea culpa.

~~~
Lonny_Eachus
hsitz:

It was only an EXAMPLE. It might be irrelevant to this particular issue, but
look at the context of my comment: it isn't irrelevant to the subject of
learning to code more efficiently.

~~~
hsitz
No problem. When I read your post I was confused whether you were suggesting
that he could have gotten a 30% speed up in overall time by making i/o more
efficient. Now I understand you weren't.

But I would still suggest that, within the context of programming
competitions, a certain kind of "programming efficiency" is close to
irrelevant. A sloppily coded solution that nevertheless implements the proper
algorithm can be many orders of magnitude faster than an efficiently coded
implementation of a slow algorithm. Programming contests are more about having
the insight to implement an algorithm having low big O, not so much about
efficient coding practices. This isn't to say that efficient coding practices
aren't important generally, just that there are much more important things to
focus on in programming competitions.

------
hayksaakian
* competitions based on speed of computation.

I'm sure if the competition was based on speed of implementation there would
be different conclusions.

~~~
schmrz
Given two developers, one proficient in Ruby and one in Go there would be
probably no difference in implementation time for such a trivial problem.

------
pieguy
I participated in this contest, and solved this problem. The problem with the
post is that it's basically saying "This correct ruby program is way slower
than this _incorrect_ go program, therefore ruby is bad for programming
competitions". Obviously not a valid argument.

------
ksec
I am risking myself getting insanely downvoted for writing this. But I hope it
at least gets some of the attention.

I really hope the Ruby Communities do actually admit Ruby is Slow. Because
until you actually admit there is a problem, there will never be any notion of
wanting to fix it.

Most of the time, they will come up with Rails Examples or serving dynamic web
pages, and the bottleneck being in Database. And the higher amount of request
you get, you can solve it with caching.

This is, in my view a very limited scope of Ruby usage.

They deny any wrong doing of Ruby, suggesting it is a dynamic languages, and
it's wrong comparing to a compiled languages, or the algorithm are done wrong.

They keep suggesting under _xxx_ usage, Ruby is fast enough to get the job
done.

Then there is the argument of throwing more money and hardware to grow and
scale. But the truth is most of time Ruby will require more then a few
dedicated servers to keep going. While it is not as drastic as the iron.io 28
to 1, but for startup lowering running cost is important too.

Then there is this arguments comes up which i hated most. You are not going to
worry about the need of that many servers, or scaling that needs to change to
another languages unless you are AirBnb, Twitter etc..... I mean what? I am
sorry i may not have worded it correctly but most of the time those read like
you are never going to be successfully that you are very unlikely to have to
worry about.

Performance is important!. And for most this isn't about pushing it to
Javascript V8 or insane 1000x programmer Mike did with LuaJIT. It is just Ruby
should have a more respectable performance VM. While Ruby is designed to make
programmers happy, I am pretty sure there are many aren't happy with its basic
performance.

~~~
camus
PHP and PYTHON are slow too. Like them Ruby can take advantage ) of C
extensions. You cant write everything in ruby like PHP developpers use native
drivers for database communication , compression , image manipulation ,etc ...
that's normal.

Scripted languages should be used for what they are for , a thin layer on the
top of other more performant languages.

Like Twitter , Ruby/Rails allowed them to bootstrap their idea ,"probe" a
market, make it work , then they thought about performance and scaling. That's
what scripted languages are for.

~~~
dmpk2k
Yes, but I think what ksec was driving at is there isn't an intrinsic reason
what you said should be true.

 _Why_ should Ruby (and scripting languages) be used for RAD and not
performance? That's because implementations range from really slow to somewhat
slow. Self, LuaJIT, V8 and IonMonkey, and increasingly PyPy are ample evidence
that this need not be the case, and that's just listing the dynamically-typed
languages.

The bar has been raised the past few years. Increasingly we can pick three of:
productivity, libraries, performance. Ruby lacks the last, which makes it a
less interesting prospect for future greenfield projects compared to its
competition.

------
rsiqueira
Even using the same algorithm implementation, there is a huge speed processing
difference between languages.

For example the implementation of the Mandelbrot algorithm in RUBY takes 47
MINUTES to complete, while it takes LESS THAN 30 SECONDS when doing the same
computation using much faster languages such as JAVA, SCALA, C or FORTRAN.
According to this performance test, those languages are more than 100 times
faster than Ruby, Python or Perl. Source: Computer Language Benchmarks
[http://benchmarksgame.alioth.debian.org/u32q/performance.php...](http://benchmarksgame.alioth.debian.org/u32q/performance.php?test=mandelbrot)
Other benchmark speed comparisons between programming languages, showing
similar results: <http://benchmarksgame.alioth.debian.org/>

~~~
igouy
> between languages

Between specific programs, using specific _programming language
implementations_.

------
tonycoco
This whole, "Ruby is slow," diatribe is just ridic.

Everyone here that has done any C/C++ or Ruby development would probably agree
that if you're looking for number smashing, Ruby is just not cutting it unless
you're into rockin' C extensions and getting what you want... for now.

Ruby, within a few years, will probably have some VM action, whether the JVM
with JRuby or Rubinius wins out, who cares.

In any case, in like 5 or 6 years when we all have 50/100 core machines,
Ruby's gonna be just fine for almost everything. Maybe by then we will be done
arguing and just agree that Ruby is the nicest language to look at and develop
in, even if it has subpar performance. Because if you're arguing something
different, you've probably never gone from Ruby to C or Java or even Python...
just so you all know, it blows.

~~~
mitchi
Tim Sweeney predicted that we'd all have 20 core machines with 80 hardware
threads by 2009. Not really what happened. The whole point is that while Ruby
is nice, it's just too slow. Someone is better off investing time learning a
faster compiled/JIT language.

~~~
cheald
Too slow for what? Certainly not all classes of problems. Heavy mathematical
work (every number in Ruby is an object! That's going to be much, much slower
than a C primitive!), sure, but there are lots of folks using it quite happily
to solve a wide range of problems.

~~~
tomrod
How does Ruby scale?

\- Not-a-Rubyista

------
voidlogic
Many people seem to be asserting just because the OP's algorithm was sub-
optimal, that it means his Ruby code running slow vs Go is a non-issue. The
problem is even well written Ruby vs Go has the same kind of performance
divide:

[http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...](http://benchmarksgame.alioth.debian.org/u64/benchmark.php?test=all&lang=go&lang2=yarv&data=u64)
(All the usual benchmark disclaimers apply, your mileage may very, your app is
always the best bench, etc etc.)

~~~
estavaro
Ruby is said to be useful for text processing and such. In Japan they had a
need for the language to deal with text files specified in a Japanese codeset.
Some of the flexibility to support different codesets cost Ruby a little. For
Ruby 1.9 and 2.0, Matz took a while in the transition to "Unicode" support in
order to keep some flexibility, even though many people preferred the old way
of UTF8 and so on.

Ruby is best when it's used for the things it was meant for, but that hasn't
kept people from trying to use it for more stuff. That's how Ruby found a good
use in servicing web apps, first with CGI and then with dedicated instances in
Rails and the like.

Now with Dart I understand that people demand languages be architected in
different ways to extract more performance from them. Dart doesn't have
"eval", for example. Whereas Ruby and Javascript do.

The question though is one of a divide, between the people who would never use
Ruby or JavaScript and those who do use and love those languages that come
with a lot of flexibility from the core. Python is like Ruby and JavaScript,
even if the Python syntax could be borrowed for different languages that are
more restricted in what they offer.

~~~
fosap
I don't see the point with eval. Every lisp has a eval, and most AOT Lisps are
faster than ruby.

Not including eval seems to be a "discipline and bondage" fetish of language
designers.

Yes, you can write pretty awful code with eval. But it allows metaprogramming
aswell. I'd like to judge if something is awful or awesome.

~~~
estavaro
With eval language designers might have a delayed execution that could make
compile-time optimizations less straightforward. Part of the toolset could
have to be present during deployment time to be able to handle eval which
happens at runtime. And yes, security concerns could also play a hand.

In Dart the code declaration is taken very seriously in many ways, and
avoiding eval buys them more compile-time, loading-time and perhaps runtime
optimization opportunities. I find that more than the language designers
themselves, it's the end-users or developers that ask for more "bondage and
discipline" from their tools. People who come from Java and C++ backgrounds,
for example. They just can't have enough compile-time stuff aided by an IDE.

------
justplay
> TL;DR. Ruby is optimized for developer happiness, not machine happiness.

sounds good . I am ruby happy developer .

------
g3rald
There is a huge difference between Ruby and Go, in terms of performance. I
wonder if anyone has any experience with Python, considering that both are
interpreted programming languages.

~~~
jdotjdot
I've has decent experiences with Python in Google Code Jam, with two
exceptions. (1) Python can't handle large numbers, so the mere existence of
huge integer inputs for the large prime number problem blew up my code since
Python could not convert numbers that large. (2) Python is fine with math, but
at a certain point of data accumulation, performance just __plummets __, no
matter how good your algorithm is. Overall, though, I haven't had the
experience the parent comment did. Either Python works beautifully or it
literally just doesn't work, rather than take an hour.

I treat it as a challenge, though, as it forces me to be smarter about my
implementation.

~~~
Luyt
_Python can't handle large numbers_

Are you sure? Can you post a number of which you think Python can't handle?

    
    
        >>> int("983728338427643876432784367834678326438763278463276543675324675327867498274982787463273647235347652673547623548628347892374982374862384676235478632894790238409382984782768347683274983274964826487264826482264862384628648723643874268426482634826438742648276487264823764873627463252642654")
    
        983728338427643876432784367834678326438763278463276543675324675327867498274982787463273647235347652673547623548628347892374982374862384676235478632894790238409382984782768347683274983274964826487264826482264862384628648723643874268426482634826438742648276487264823764873627463252642654L
        >>>

------
codeonfire
Ruby is too slow for brute force solutions for programming competitions. If
you look at the third set of cases, the inputs could have a range 0..10^100.
Even with the square root, any solution that is going to check 10^50
numbers/strings is going to take a very long time in any language.

------
mseepgood
Idiomatic Go tip: you should use more type inference via := instead of
declaring all variables at the beginning of a function. And use gofmt :)

------
trailfox
From the post...

Running time:

Ruby: 3180 seconds

Ruby (profiled and optimized): 342 seconds

Go (first attempt): 0.7 seconds

------
bbwharris
He did everything right. Tried it in ruby, if it would have been fast enough
this article wouldn't exist. Instead he applied good software engineering and
found the right tool for the job.

This doesnt say "don't use ruby it's too slow". It's says prototype in your
comfort zone and optimize. Isn't this what we all agree on?

Edit: auto correct fail.

------
andrewcooke
most numbers are not palindromes. so generate palindromes, and then test.

~~~
mrgordon
yes exactly, this is how a friend did it in jruby (he got a perfect score)

------
sspiff
I have noticed a similar thing recently. A programming challenge with a 10s
max time restriction, and just reading the input and storing it in an array
takes over 7 seconds.

I allocate the array ahead of time, as soon as I know the size.

The equivalent C/C++ version took under a second.

~~~
yxhuvud
How did you read the input? All at once or one line at a time?

~~~
sspiff
One line at a time. Is that significantly slower for stdin? I would assume
that this wouldn't make a difference, since we're talking about simple memory
operations, and 20k method invocations shouldn't be that hard on a modern
system.

~~~
yxhuvud
stdin? No, that shouldn't matter then, but it would if you were reading from
file.

------
macobo
In this context, no it's not.

One thing that Code Jam does differently (and imho better) is that they give
you an input set and you have 8 minutes (or so) to upload the solution. This
is expected to give more than enough space for language speed variance.

Instead of execution time, coding time is much more precious in such
competitions, and

Micro-optimizing has it's place, but it's usually not in a programming
competition. that's what using ruby instead of C gives you leverage for that,
as you can express more complex ideas and algorithms more easily (which should
be the focus point).

This doesn't mean you shouldn't know the cost of operations.

~~~
yen223
"Instead of execution time, coding time is much more precious in such
competitions"

Unfortunately, in such competitions you'll be competing against people who
write C++ algos faster than normal people writing Python.

~~~
macobo
That's because most contests (but not all) are limited to C, C++ and Java and
they've practiced hundreds if not thousands of hours on solving those kinds of
problems. Of course they will be more efficient in c++ than a person who is
spending most of their time wondering how costly string operations are.

That doesn't mean that there aren't also people who have done the same and
still prefer python (or some other language) not for execution, but
development speed (and bignum, larger standard library...)

------
kyllo
So you wrote a brute-force algorithm in a slow, interpreted implementation of
a dynamic language, and you were surprised that your solution took a long time
to run?

Using a faster language implementation could have allowed you to use the same
brute-force algorithm and come in under the time limit, but your real problem
is your algorithm, not your language implementation.

You can write fast code in a fast language and get very fast running time.

You can also write slow code in a fast language, or fast code in a slow
language, and still have acceptably fast running time.

But you wrote slow code in a slow language, the worst of both worlds.

------
VeejayRampay
Maybe the result would vary with JRuby or Rubinius (I'd be interested in
benchmarks) because not only is Ruby itself not the fastest language, but its
main implementation (MRI) is also not the best for speed.

~~~
ClifReeder
This is a good point that I didn't consider. Running the 'just iterate over
the range version' of this code with jruby:

    
    
        [master] clifff@fair_and_square: ruby -v
        jruby 1.7.3 (1.9.3p385) 2013-02-21 dac429b on Java HotSpot(TM) 64-Bit Server VM 1.6.0_43-b01-447-11M4203 [darwin-x86_64]
        [master] clifff@fair_and_square: time ruby fair_and_square.rb C-large-1.in.bak
    
        real	6m39.105s
        user	6m37.762s
        sys	0m19.009s

~~~
aghoreyshi
What is sys time? The amount of time it's actually run on the processor?

~~~
eatitraw
No, it is the amount of time your program spent in system calls:
[http://stackoverflow.com/questions/556405/what-do-real-
user-...](http://stackoverflow.com/questions/556405/what-do-real-user-and-sys-
mean-in-the-output-of-time1)

------
ngoel36
Did you need to check `&& (start..finish).cover?(square)` on Line 17?

~~~
bvdbijl
nope, iterating up to the square root of finish covers that

------
jt2190
The article doesn't actually say that performance was one of the judging
criteria, and I'm unable to find anything in the code jam rules about this.
Can anyone here clarify?

~~~
mrgordon
Performance is not technically a criteria but you have eight minutes from the
time you download the input data until you have to be done uploading the
output data. So if you spend 50 minutes incrementing integers, then you'll get
0 points no matter what :)

~~~
jt2190
Thanks!

------
anonymoushn
If you precompute the palindromes in the range, store a table of prefix sums,
and calculate each of the 10,000 cases in constant time it will probably pass
(in ruby).

Edit: as electrograv pointed out, another optimization is to generate
palindromes from sqrt(start) to sqrt(end) rather than testing each number in
the range for palindrome-ness. This saves you a factor of 10^3.5.

------
calinet6
From the edits: "If you are like me and don’t always come up with the best
solutions, you may want to consider other languages too."

\--> "You may always want to consider other languages too."

No one should ever agree with an ultimatum on language choice: if you need
hardcore speed, use a lower-level language, not an interpreted garbage-
collected scripting language. Duh.

------
moron4hire
Also, if you ever think you need built-in support for large numbers in your
programming language during one of these contests, then you're using the wrong
algorithm. These contests are typically designed to be doable with very, very
simple code using primitive data types.

------
joydeepdg
Remember PAPP[1]

[1] Richard Buckland's awesomeness - <http://www.youtube.com/watch?v=JTAkUs-
NjxU#t=44m21s>

------
frogball
Isn't it obvious that there must be a better solution to the problem then your
solution? You shouldn't try to find the palindromes, but rather generate them.

------
spyder
<https://news.ycombinator.com/item?id=5304873>

------
lectrick
Couldn't someone implement a Ruby CLANG dynamic runtime recompiler?

------
cbolat
Ruby is uber fast for hipsters..

------
MaxSize
EventMachine helps with performance critical Ruby code

~~~
clarle
You're right that EventMachine helps when you're dealing with IO-limitations
(like those commonly found in web applications), but for CPU-limited functions
like the ones that you see in programming competitions, it's not going to
provide much of a benefit.

------
davidw
Great, so stop dicking around with some zero-sum game (
<http://en.wikipedia.org/wiki/Zero%E2%80%93sum_game> \- a competition may only
have one winner ) and go build a business, where no one cares what language
you use as long as the solution works for your customers - and there can be
more than one winner in many cases.

Or even if you don't have a side project or startup or something, go build
something cool and open source it and do that, rather than working on some
artificial problem.

You can click the little down arrow all you want, but it won't keep me from
thinking you're mostly wasting your time with these competitions, and it won't
put any money in your pocket.

~~~
geedy
Curious, would you call University education dicking around as well?

Seems to me challenging the brain is never a waste of time. We would not have
some of the medical technologies we have today if it were not for massively
parallel GPUs, which of course were developed for the sole purpose of playing
games.

~~~
davidw
> would you call University education dicking around as well?

That's a big debate! For a lot of people, I would, though, yes. There are
serious economists who posit that universities are mostly about signalling,
rather than actually learning much. I don't think I agree with that entirely,
and believe it depends a lot on what's studied. It also depends on the
opportunity costs, which may not be that high for many people that age.

> GPUs, which of course were developed for the sole purpose of playing games.

I don't believe the creation of games is a zero-sum game at all; there can be
many winners, and, as you say, there are other side effects that are
beneficial. The creation of games also falls into one of business, side-
project or open source project as mentioned in my other post, no?

Artificial coding competitions aren't really about creating much of anything
though, from what I can tell. A bunch of people solve the same thing and then
throw away what they made.

I agree that challenging your brain isn't a waste, but it seems that in a
world that is desperate for coders, there are more productive ways of
challenging your brain in a way that's perhaps even more satisfying, because
of the positive feeling of having helped to create something useful.

~~~
geedy
> That's a big debate! For a lot of people, I would, though, yes. There are
> serious economists who posit that universities are mostly about signalling,
> rather than actually learning much. I don't think I agree with that
> entirely, and believe it depends a lot on what's studied. It also depends on
> the opportunity costs, which may not be that high for many people that age.

Fair enough. For myself, it was what lead me to my career. I found a large
number exams followed a similar format to this very coding competition. Many
times on the job I have thought back on exam questions for inspiration for the
problems I was solving. In that sense, I most definitely do not classify my
education as "dicking around".

It brings be to my original point; when video games were being developed,
these admitted benefits were certainly not obvious at the time. We simply
wanted to render things faster, and at higher resolutions. Now other
applications of the same technology is ubiquitous in many regards.

As a final tangential point, I don't think the code competition is wasteful
because it does not have any immediate intrinsic value. See exams example
above, and really, the discussion here. Also, perhaps this was his so called
"downtime". Many people would argue that not all facets of our life should be
strictly geared towards productivity. He may already run a business or hold a
day job (maybe his blog gives clues otherwise, I don't know).

~~~
davidw
There are plenty of things that aren't "productive", but strike me as still
being more creative/interesting/useful than churning out the same answer as
everyone else to a made-up problem. Making your own programming language or OS
is not 'productive', but a great learning experience, creative, and lots of
fun. And who knows, maybe someone will get some use out of it. I once made a
fairly simple programming language, and it did end up seeing some niche
adoption and use in industry, which was cool.

