views:

269

answers:

4

I'm writing a script that will ping my ip range. Here's what I have so far:

lines = `ipconfig`.split("\n")
thr = []
ip_line = lines.detect { |l| l=~/Ip Address/i }
matcher = /\d+\.\d+\.\d+\.\d+/.match(ip_line)
if matcher.length > 0
    address = matcher[0]
    address.sub!(/\.\d+$/,"")
    (1 .. 254).each do |i|
     xaddr = address + "." + i.to_s
     puts "pinging #{xaddr}"
     thr << Thread.new {
      `ping #{xaddr}` 
     }
    end

    thr.each do |t|
     t.join
     output = t.value
     puts output
    end
end

The thing is, this executes extremely slow. Like the app isn't threaded. Why is that? I noticed that if I subclass Thread, the whole thing runs much, much faster. What's wrong? Isn't Thread intended for direct usage?

+1  A: 

What Ruby implementation are you running on? In standard Ruby, threads are "green threads", i.e. not real operating system threads but provided by the Ruby runtime.

In JRuby, threads are real OS threads because that's how the JVM implements them.

So, you may see a difference in threading performance between different implementations of Ruby. Note that JRuby is regarded as faster that Ruby 1.8.6, though Ruby 1.9 is faster than JRuby (at least on some benchmarks).

Vinay Sajip
ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32]. The thing is, this runs as if ran sequentially. If I make a subclass of Thread, it runs much better.
Geo
+5  A: 

Ruby threads are controlled by Ruby Interpreter. to operating system, Ruby Interpreter is still just one process(just like any other processes). Ruby Interpreter split that one process into multiple ruby threads.

`ping #{xaddr}`

this line forces the Ruby Interpreter to temporarily give up its control, since you are asking the operating system to execute another process. The ruby interpreter will not regain its control until after the 'ping' finishes. That's probably why the code is slow.

ez
how would I fix that?
Geo
ruby 1.8 does not support OS-level thread. If upgrade to ruby 1.9 is an option, try fibers. http://www.ruby-doc.org/core-1.9/classes/Fiber.html. Another option is using JRuby
ez
This sucks. What could be the reason behind this?
Geo
umm...found a better article, it explains the reason why the ruby thread was implemented this way. http://blog.grayproductions.net/articles/the_ruby_vm_episode_iii
ez
another idea, rather than use system call, why not try ruby's own ping library, http://www.ruby-doc.org/stdlib/libdoc/ping/rdoc/index.html. I have not tried it, but it might work
ez
I just tried my script under JRuby and it works. Thanks.
Geo
+1  A: 

You can use IO.popen like this

thr << Thread.new xaddr do |adr|
  IO.popen "ping #{adr}" do |io|
    io.each {|l| puts l}
  end
end

That way you get more concurrency even with green threads. The reason is that the interpreter does not have to wait until the full output of ping has been sent.

Robert Klemme
A: 

Hmm it seems like I'm getting the same performance for

`...`

and

IO.popen ...

is the same: http://gist.github.com/511599

Am I going crazy?

Aaron Gibralter