Archive for the 'Python' Category

Ruby on Rails 2.0 has been released and other Zenbits

Antonio Cangiano December 7th, 2007

Zenbits are posts which include a variety of interesting subjects that I’d like to talk about briefly, without writing a post for each of them.

A few hours ago Rails 2.0 was finally (quietly) released. Unfortunately if you try ‘gem update’ or ‘gem install rails’ you will get the following error:

ERROR: Error installing rails:
              rails requires activeresource (= 2.0.0)

To solve this problem, assuming you are installing, simply run:

$ gem install rails --source http://gems.rubyonrails.org

For details about this new release wait for the official announcement by DHH and his team.


On the subject of announcements, the IBM_DB Python driver for DB2 version 0.2.0 was released. This includes a Python egg for Linux and (finally) for Windows. You can download both of them from Cheeseshop.


A few days ago NetBeans 6.0 was released. Its support for Ruby and for Rails is stellar. Its editor seems to be refined to provide developers with a comfortable environment for programming Ruby and Rails applications in. The code auto-completion (with documentation on the fly) alone makes it extremely valuable. From what I’ve seen so far it’s a solid, well thought out IDE that sets the bar high when it comes to the world of Ruby/Rails editors. Now we need Aptana IDE to implement similar features, for those of us who use and prefer (at least on Windows and Linux) an Eclipse based IDE. Between NetBeans’ support for Ruby and the active development of JRuby, one can only conclude that Sun is very serious about Ruby and that they really “get it”. We can wish for the same kind of commitment from Microsoft, but so far I get the impression that projects like IronRuby are seen by Microsoft as little more than pet projects just like IronPython is. But I’d be happy if my first impression was to be proved wrong. That said, Microsoft is receiving a huge wake up call from their research division, as shown by excellent videos which cover non-mainstream and research topics as well. They’ve also proved this by incorporating advanced features from research languages in C# 3.0. We’ll see how it goes, but it looks like there might be some hope after all.


Speaking of videos, I recommend a fantastic interview with E.W.Dijkstra, recorded a few years ago. It’s called Discipline in Thought and deals with the subject of the nature of programming. I highly suggest that you watch part 1, part 2 and part 3. After that, you can dig further by reading some of his manuscripts in this archive.


On a different topic, I’d like to thank everyone who commented and posted about my Ruby shootout. We made the front page of, among others, Del.icio.us. Its popularity is important to me, because it gives the proper exposure to these projects and their authors and debunks the myth that we are all happy with Ruby’s status quo in terms of speed. The next run will add extra benchmarks (in order to provide less of an advantage to Ruby 1.9). Performance is not everything, but it can be an important aspect. I like Charles Oliver Nutter’s (of JRuby) approach:

If you run across benchmarks of any kind that show JRuby running slower than Ruby 1.8.x, we’d appreciate you filing them as bugs.

That’s the right attitude, it shows serious commitment in terms of resolving this issue. Kudos to him and his team. As far as commitment goes, I can’t praise Engine Yard enough, as they’ve just hired two excellent hackers (Ryan “zenspider” Davis and Eric “drbrain” Hodel) to work full time on Rubinius along with Evan Phoenix (who started the project in the first place). From January onward, Engine Yard will also pay Wilson “Defiler” Bilkovich and Brian “brixen” Ford to do work on Rubinius. That’s a ridiculously high IQ potential to have working on Rubinius. We can only expect great results and undoubtedly say that Engine Yard really gets it.


Finally, for those of you who requested it, please find here the results of my benchmarks in Excel and PDF format.

Update

The released (through rubygems) but not announced Rails 2.0 has now been upgraded to 2.0.1, and that’s what you’d get if you ran ‘gem install rails’ or ‘gem update’. The error reported above still exists, so you can update by specifying the source as mentioned in this post.

Update 2

For those of you who didn’t believe in my “scoop”, here is the official announcement with all the glorious details. Awesome! :)

Update 3

The gems should be properly propagated now, so that error shouldn’t be there anymore.

More on Fibonacci. Oops, Sorry Lisp… Haskell runs it 5 times faster

Antonio Cangiano November 30th, 2007

My post about Ruby 1.9’s impressive improvement over Ruby 1.8.6 created quite an echo within the developer community. Sure, the headline was an attention grabber, just like this one is :-P, but in a matter of a few hours, there were all sorts of blog entries with variants in many languages, more than 200 comments on Reddit, and fifty comments on my own blog. There were however, also a few misconceptions. It was great though because such a simple post generated a lot of discussion amongst developers, with some insightful arguments taking place - and besides it almost created a new meme with the whole “holy shmoly” thing. Fun as that certainly was, let’s try to summarize and clarify a few points.

First and foremost, for those who stopped at the title of the article and didn’t read on, I made it very clear in several places that I didn’t even begin to predict that Ruby 1.9 will be faster than Python 2.5.1 when it comes to real world applications. I ran a simple micro-benchmark where this just happened to be the case. Chances are that Python will have the edge in many instances, especially if we consider that it has several optimized libraries which may still be missing or suboptimal in Ruby. Within the scope of the recursive Fibonacci’s test, which essentially stress-tests method/function calling, Ruby 1.9 seems to be more than 13 times faster than Ruby 1.8.6, but within the Ruby community it’s well known that this improvement factor is not very often replicated in other micro-benchmarks or actual applications.

Ruby is improving, its progress is impressive, so let’s drive this point home and try not to speculate too much. Someone also pointed out Ruby’s lack of Unicode support and in other cases, its disadvantages over Python. That’s missing the point, the article isn’t aimed at comparing or claiming that one language is better than the other. The essence of the message was and remains, that Ruby’s main interpreter — which was typically fairly slow — will soon be replaced by Yarv, which will drastically improve Ruby’s speed, to the point where perhaps it will be comparable with the not-too-fast but acceptable CPython. And yes, even for Python there are psyco and pypy both of which would change the outcome of my test. With this out of the way, let’s move on.

Dima Dogadaylo and my friend Valentino Volonghi both blogged about a much faster version (they employed the use of generators) of my Python snippet. Many other people in this blog and on Reddit proposed faster algorithms too. That’s all well and fine, but it really compares apples and oranges. If we switch from a naive recursive algorithm to an iterative one, even Ruby 1.8.6 will be faster and able to compute the task in a few moments. The computationally expensive and inefficient recursive algorithm should be used by those who want to compare other languages with my results, otherwise the comparison will be meaningless. Using a fast language to implement a slow algorithm is always going to be slower than using a slow language with an efficient algorithm, for N sufficiently large.

The Fibonacci function is mathematically defined as follows:

Fibonacci function

In the intentionally naive and recursive algorithm I adopted in my original post, I essentially wrote the mathematical formula in Ruby and Python syntax, and then executed it for n in a range that goes from 0 up to 35. This is very inefficient, because the tree-recursive process generated in computing F(n) grows exponentially. We have:

F(0) = 0
F(1) = 1
F(2) = F(1) + F(1)
F(3) = F(2) + F(1) = F(1) + F(1) + F(1)
F(4) = F(3) + F(2) = F(1) + F(1) + F(1) + F(1) + F(1)
F(5) = F(4) + F(3) = F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1)
F(6) = F(5) + F(4) = F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1) + F(1)

You can see where this is going. The recurrence relation generated by the algorithm, implies that we execute 3*F(n) - 2 lines of code (for n nontrivial). When n=35, this is a huge number, since F(35) = 9227465. Now you can see why the algorithm really stress-tests function calling and why Ruby 1.8.6 chokes. If we want to evaluate the computational complexity of the algorithm in terms of big-O notation, we have O(phi^n), where phi is the Golden Ratio and equal to (sqrt(5) + 1)/2. That’s exponential. Valentino, Dima and many others came up with variants of iterative or tail-recursive algorithms, whose complexity is linear (O(n)). Using memoization (the technique of keeping a cache of the previous calculations) or a simple iterative loop changes the algorithm’s efficiency. The difference between my original algorithm and these others is humongous and there is no point in comparing them as a means of evaluating the runtime speed of a given language. At that point we could very well use Binet’s formula, or Edsger Dijkstra’s algorithm or even 2×2 matrix exponentiation, but we’d not be proving any point there. If you want to learn more about algorithms (a necessity for any serious programmer), I strongly suggest the introductory textbook “Introduction to Algorithms”.

Don Stewart, a real celebrity in the Haskell community, has replied to my post with two articles that essentially illustrate a couple of points that are not new to many people: 1) Haskell is fast, much faster than Python and Ruby, 2) Haskell’s ability to take advantage of multiple cores by following parallelism hints placed in the code for the compiler, is just plain awesome and easy on programmers. Don did use old versions of Ruby and Python, but I appreciate his response a lot, because he kept the same algorithm in place. He didn’t bait and switch, using one of the many fast implementations available on the Haskell wiki. His fair comparison showed, despite the very limited scope of the test, what kind of performance we can expect from this functional language’s main compiler (GHC).

As I said in the past, Haskell really is a language worth getting excited about. But it’s not all about performance and the trend of increasingly multicored CPUs. So I’m glad that we have both Ruby and Haskell with their strengths and weaknesses. While Ruby 1.9 will hopefully give us a runtime that’s seriously fast enough™ in most circumstances, it’d be nice if in Ruby’s future there were features that allowed us to take advantage of multiple cores, just like Haskell does without cumbersome code modifications.

Amongst the other replies there was a mix of everything, including Assembly and LOLcode, but I’d like to point out the post by a lisper, who took the Haskell vs Lisp approach in “Dude, your quad-cores have been smoking way too much Haskell!”. He runs the following code, first for n=45 and then for n=4500:

(defun printfib-trec (start end)
  (format t "n=~D => ~D~%" start (fib-trec start))
  (if (< start end)
      (printfib-trec (+ start 1) end)))

(defun fib-trec (n)
  "Tail-recursive Fibonacci number function"
  (labels ((calc-fib (n a b)
         (if (= n 0)
         a
         (calc-fib (- n 1) b (+ a b)))))
    (calc-fib n 0 1)))

(time (printfib-trec 0 4500))



On my machine this runs in 3.291 seconds. Algorithm 101, guys. Quick question for my readers: how can an algorithm that is supposed to be O(phi^n) execute in 3 seconds, per n=4500? Simple, it’s that blogger who is being naive and not the algorithm that he adopted. If you pay attention you can see that he’s trying to compare the linear O(n) tail-recursive implementation of Fibonacci in Lisp, with the naive recursive one in Haskell, and from this he concludes “Oops. Sorry Haskell…”. Slow down, cowboy! You want to compare Lisp with Haskell? Let’s do a fair comparison then. Let’s keep the same algorithm for both and use n=45, shall we?

Here is my naive/recursive Lisp version:

(defun printfib (start end)
  (format t "n=~D => ~D~%" start (fib start))
  (if (< start end)
      (printfib (+ start 1) end)))

(defun fib (n)
  "Naive-recursive Fibonacci number function"
  (if (or (= n 0) (= n 1))
	  n
	(+ (fib (- n 1)) (fib (- n 2)))))

(time (printfib 0 45))



And here is the Haskell one:

import Control.Monad
import Text.Printf

fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

main = forM_ [0..45] $ \i ->
            printf "n=%d => %d\n" i (fib i)



On my MacBook Pro, Intel Core 2 Duo 2.2 GHz and 2 GB of RAM, Lisp (well sbcl, which supposedly uses both cores, though there is no documented proof of this) took 259.743 seconds. See the difference between O(n) and O(phi^n)? Try n=4500 with this algorithm and the sun will have burned out before the computation is finished. Haskell, used only 1 core, and took 77.779 seconds. Hmmm, Haskell was 3.3 times faster than Lisp without even parallelizing it.

Just out of curiosity, let’s try again with Don’s code which still implements the same algorithm (whose complexity is O(phi^n)), but which introduces parallelism hints for the compiler. Remember, this is being done out of curiosity, we already established that for this particular micro-benchmark Haskell smokes Lisp away.

import Control.Parallel
import Control.Monad
import Text.Printf

cutoff = 35

fib' :: Int -> Integer
fib' 0 = 0
fib' 1 = 1
fib' n = fib' (n-1) + fib' (n-2)

fib :: Int -> Integer
fib n | n < cutoff = fib' n
      | otherwise  = r `par` (l `pseq` l + r)
 where
    l = fib (n-1)
    r = fib (n-2)

main = forM_ [0..45] $ \i ->
       printf "n=%d => %d\n" i (fib i)



Now that Haskell’s program uses two cores (assuming that Lisp does too, which is unlikely), Haskell runs it in 52.248 seconds versus Lisp’s 259.743 seconds. That’s about 5 times faster than Lisp. Does this prove that Haskell is faster than Lisp in general (or to be more exact, that GHC is faster than sbcl on Mac OS X 10.5)? Nope, guys it’s just a silly micro-benchmark after all. But damn the temptation to say “Oops. Sorry Lisp…” was too strong. ;-)

Holy Shmoly, Ruby 1.9 smokes Python away!

Antonio Cangiano November 28th, 2007

Alright the title of this post is a tad sensational sounding, I know, and it’s in part aimed at messing with my many Pythonista friends. We Rubyists have been teased for a long time, due to the slowness of the main Ruby interpreter. Well, it looks like with Ruby 1.9, it’ll be payback time. Just out of curiosity I decided to run a single benchmark (you can hardly call it that) to see how Ruby 1.9 had improved over the current stable version (1.8.6). I wasn’t planning to make a post about it. It was one of those tests that you do at 3 AM in an irb session when you feel you’ve made your daily peace with your actual workload for the night. When I saw the results though, my jaw dropped. I had to blog about this one.

I ran a recursive Fibonacci function, just to stress test a bit of recursion and method calling, and while I was at it, I decided to compare it with Python too. The test was run on Mac OS X 10.5 with my MacBook Pro (Core 2 Duo 2.2 GHz and 2 GB of memory). It’s a single test (which is obviously not a real world example, as you would use an iterative version of the function if it were), and unlike with real programs, it doesn’t stress many features of the language. At least for now, there is no reasonable evidence to conclude that Ruby 1.9 - which will be released for this coming Christmas - will actually be faster than Python 2.5.1 in the majority of situations, but hear me out and check out these very surprising results.

The Ruby code:

def fib(n)
  if n == 0 || n == 1
    n
  else
    fib(n-1) + fib(n-2)
  end
end

36.times do |i|
  puts "n=#{i} => #{fib(i)}"
end

And the Python equivalent:

def fib(n):
   if n == 0 or n == 1:
      return n
   else:
      return fib(n-1) + fib(n-2)

for i in range(36):
    print "n=%d => %d" % (i, fib(i))

Running the snippets above, I got the following results:

Ruby 1.8.6:       158.869s
Python 2.5.1:      31.507s
Ruby 1.9.0:        11.934s

Ehm, hold on a second! Did Ruby just go from 159 seconds down to 12? Koichi Sasada, do you have an Amazon Wishlist? I was expecting a decent improvement, as I’ve been playing with 1.9 every now and then for a long time - so I knew it was faster - but I was blown away when I timed the latest version from trunk (even if it’s a really silly example that’s being tested). Granted Python is not the fastest language out there, but Ruby 1.9 was still able to execute the script almost 3 times as fast. It’s unbelievable.

Now it’ll be very interesting to run a series of algorithmically equivalent tests for Ruby and Python, and to see just when exactly Ruby 1.9 manages to knock Python out of the water - and where Python has still the edge. If I manage to find some time, I will report the results in this blog. But for now, I’ll say just… wow!

« Prev - Next »