the blog
Back to -Blog

Vista and Debugger Developments

Tweaks and benchmarks
by Dermot Hogan
Sunday 11 February 2007.

I’ve been fixing various problems that have come to light since we launched Ruby In Steel two weeks ago. Some I expected and some I didn’t. The expected ones were various problems with IntelliSense, debugger expression parsing and various small configuration glitches. The two I didn’t expect were Vista and the requirement for a thread-safe debugger.

Vista took us by surprise because we had assumed that few, if any, developers would be using Vista this early on (Vista was ‘released’ the day after Ruby In Steel: I really must talk to Bill about scheduling our releases better in future). It turns out we were wrong. Vista is being using - and quite widely. I can see why having installed it. Apart from the usual few moments of irritation about things being in the ‘wrong’ place, Vista really is very nice to use.

The other thing we got wrong was thinking that you don’t need a multithreaded debugger with Rails. It turns out that you do. So, some months ahead of schedule, I’ve reworked the debugger to make it thread safe. I’ve been testing it over the last couple of days with WEBrick, LightTPD and (at last!) Mongrel. And they all work fine as far as I can see.

Note that I used the phrase ‘thread-safe’ not ‘multi-threaded’. This is because, in order to make the debugger ‘multi-threaded’, I need to build a whole raft of features into the Visual Studio side to control threads – start them, stop them and examine them. That will come later as scheduled.

The downside of the debugger being thread safe is that there’s a performance penalty (the results are below). I’ll be making performance improvements later on when I do the full multi-threaded thing for 1.2. Right now, the priority has been to get Rails debugging fully operational.

In passing, I’ll note that Ruby’s implementation of threads is the worst I’ve ever come across. In the past, I’ve written several multithreaded servers and device drivers so I know well the problems that come as part of dealing with asynchronous execution contexts. I nearly fell off my chair laughing when I found out how Ruby does thread ‘pre-emption’ – at least on Windows.

The figures

This is on a 2.8GHz Pentium 4 with 1.5GB of memory (timings in seconds).

Pentium count none Cylon Ruby ratio
factorial 10000 2.171 15.609 428.0 1 : 7.2 : 197
linear 1000000 0.672 0.922 160.0 1 : 1.4 : 238

And on a Core Duo 6600 with 2GB of memory.

Core Duo count none Cylon Ruby ratio
factorial 10000 1.108 5.132 208.0 1 : 4.6 : 187
linear 1000000 0.281 0.452 94.0 1 : 1.6 : 334

I’ve quoted the results as ratios (none : Cylcon : Ruby) rather than ’overhead’ as it’s probably more meaningful. Even though the thread-safety features do slow down the debugger, Cylon is still much faster than the Ruby debugger. On the factorial benchmark, it’s about 40 times faster. On the linear benchmark it’s around 200 times faster. With a bit more work, however, I can (and will) make it faster still.

When I do the debugger for Ruby In Steel 1.2, I’ll leave in the option for the original ultra-fast single-threaded Cylon since this will be quite useful for single threaded applications like number crunching, machine control or CAD programs. And, as I said, I’ll also work on improving and optimising the performance of the Cylons.

Finally, I also ran the debugger on a more realistic suite of Rails unit tests kindly donated by a user. These involve a fair bit of IO of one sort or another as well as compute operations (the benchmarks above are pure ‘compute’). The ratio I got was approximately 1:2:50. In other words the multithreaded debugger was twice as slow as the code without the debugger and the ‘slow’ debugger was fifty times slower than without. However, because of IO intervals, etc, it’s virtually impossible to make any deductions about the debuggers themselves.

The Code

The code I used is here:

def fac(n)
        n == 1 ? 1 : n * fac(n-1)
end

count = 0

tstart = Time.new
#0.upto(100000) {fac(50)}
0.upto(100000000) {count += 1}
tend = Time.new
puts "%10.3f" % tstart
puts "%10.3f" % tend.to_f
diff = tend - tstart
puts "%10.3f" % diff.to_f
Bookmark and Share   Keywords:  development  news
© SapphireSteel Software 2014