views:

572

answers:

4

I am trying to run 500 clients that send some request to the server simultaneously for load testing purpose. The client is a ruby program again. Sounds trivial. But I am facing weird problem with ruby threads. My code looks like this -

n = 10

n.times do
  Thread.new do
    `calc`
  end
end

The code is a sample. I'm just trying to run calc command from command line (If you are trying this code on platform other than windows please replace it with some command that works on your command line or shell). This will later be replaced by 'ruby my_client.rb' and also the value of n will be set to 500 (or whatever).

The problem that I am facing here is that regardless the number of threads I want to create, only 3 threads are created at a time. That is only 3 calc windows are opened simultaneously. The remaining threads just wait in a queue waiting for termination of these 3 threads. May be it has something to do with blocking and non-blocking calls. But I tried Java-equivalent of the same program and it worked perfectly. Its an old saying that threads in ruby are not recommended. Is it true that this is a problem with Ruby's threads or am I doing something wrong?

+3  A: 

The "Matz" C implementation of Ruby (MRI) does not use native threads up through 1.8.6. I believe this is changed in Ruby 1.9, but I understand we're probably still not going to see great multi-threaded performance due to the Global Interpreter Lock.

If you really need good multi-threaded support, and for your software to be written in Ruby, you might try running it on JRuby. A quick sanity test showed that using your example I would get 2 OS threads on MRI and 12 when running the same thing under JRuby. This was on OS X using "MRI" 1.8.6 and JRuby 1.1.6.

Another option, since it looks like you're spawning a thread in order to fork a new process, might be to utilize DRb instead.

Thanks for the DRb pointer. Jruby should solve the problem. Don't have any experience in DRb, will learn and give that a shot too. Thanks :)
Chirantan
Just because Ruby is only green threads, doesn't mean it should only run three threads at once.
womble
That does look like a reason anyway
vava
ruby has a big fat io lock which, for me, makes threads almost useless. :/
reto
A: 

This worked perfectly for me on os x with textmate.

n = 10

threads = []
n.times do |i|
  threads << Thread.new do
    `mate test#{i}.txt`
  end
end

threads.each { |t| t.join }
jshen
Tried your code. Same issue. May be what Alex says is right.
Chirantan
+2  A: 

The problem you're observing is specific for GUI applications. It gets much better when you run command-line stuff inside workers.

With example below I can run 200 wget instances just fine, which is probably enough for your load testing goals.

n = 200

threads = []
(1..n).each do |i|
  threads << Thread.new do
    puts `wget google.com` # forgive me google
    sleep 10
    puts "#{i} done"
  end
end

threads.each do |t| # wait until all workers are done
  t.join
end

You could probably get many more workers if you switch from wget to Ruby code for fetching web pages. And still, you should remember that Ruby threads scales only that far. Don't expect many thousands or parallel threads to work fine -- try subprocesses or continuation-based approach instead.

Alex Lebedev
I rand the code under Windows XP SP3, ruby 1.8.7 and it worked just fine.
Lolindrath
A: 

You might want to spawn separate processes instead. Kernel::fork doesn't work under Windows so you'd have to use good old Kernel::system or Kernel::popen and create a separate scripts for that or use special command line arguments.

Although Ruby 1.9 is almost there and if you can switch on it, it does have native OS threads and stuff like that wouldn't be happening.

vava