Performance

Posts tagged with "Performance."
  • 22

    AUG
    2014

    Sleepy Programs

    When we think of real multiprocessing, our thoughts probably drift more towards languages like Erlang, Go, Clojure, or Rust. Such languages really focus on getting separate "processes" to communicate via messages. This makes it a lot easier to know when one process is waiting on another, because calls to receive messages typically block until one is available.

    But what about Ruby? Can we do intelligent process coordination in Ruby?

    Yes, we can. The tools for it are more awkward though. It's easy to run into tricky edge cases and hard to code your way out of them correctly.

    Let's play with an example to see how good we can make things. Here's what we will do:

    1. We will start one parent process that will fork() a single child process
    2. The child will push three messages onto a RabbitMQ queue and exit()
    3. The parent will listen for three messages to arrive, then exit()

    Here's a somewhat sloppy first attempt at solving this:

    #!/usr/bin/env ruby
    
    require "benchmark"
    
    require "bunny"
    
    QUEUE_NAME = "example"
    MESSAGES   = %w[first second third]
    
    def send_messages(*messages)
      connection = Bunny.new.tap(&:start)
      exchange   = connection.create_channel.default_exchange
    
      messages.each do |message|
        exchange.publish(message, routing_key: QUEUE_NAME)
      end
    
      connection.close
    end
    
    def listen_for_messages(received_messages)
      connection = Bunny.new.tap(&:start)
      queue      = connection.create_channel.queue(QUEUE_NAME, auto_delete: true)
    
      queue.subscribe do |delivery_info, metadata, payload|
        received_messages << payload
      end
    
      time_it("Received #{MESSAGES.size} messages") do
        yield
      end
    
      connection.close
    end
    
    def time_it(name)
      elapsed = Benchmark.realtime do
        yield
      end
      puts "%s: %.2fs" % [name, elapsed]
    end
    
    def wait_for_messages(received_messages)
      until received_messages == MESSAGES
        sleep 0.1  # don't peg the CPU while we wait
      end
    end
    
    def send_and_receive
      pid = fork do
        sleep 3  # make sure we're receiving before they are sent
        send_messages(*MESSAGES)
      end
      Process.detach(pid)
    
      received_messages = [ ]
      listen_for_messages(received_messages) do
        wait_for_messages(received_messages)
      end
    end
    
    send_and_receive
    

    Read more…

  • 3

    AUG
    2007

    The Ruby VM: Episode V

    You have told us before that one of the big reasons to move to a new Ruby VM was to provide new options for optimization. Can you talk a little about the optimizations you have added to the new Ruby VM thus far and what operations will likely be faster because of them?

    ko1:

    OK. At first, I write about basic of YARV instruction. YARV has two type instructions. First is primitive instruction. It's as written, primitive. Ruby code can be represented in these primitive instruction. Second is instructions for optimization. It's not needed to represent Ruby scripts, but they are added for optimization. Primitive instructions doesn't include _ in their name (like putobject), and optimize instructions do (like opt_plus). This policy helps you if you want to see VM instructions. Initially, you need to read primitive instructions.

    The most easy and effective optimization is Specialized Instructions. This optimization replace method call with another VM instruction, such as Fixnum#+ to opt_plus. Current Ruby's numeric calculation is slow because all operations are method call. For example, 1 + 2 means 1.+(2). But numeric operations are more lightweight than Ruby's method invocation. So method call is only overhead for numeric operation. Specialized Instructions allow the VM to skip method call overhead.

    But we can't know which expression is numeric operation or not at compile time. See this expression: a = c ? 1 : [:elem], a will be Fixnum or Array at runtime.

    So, we can't replace + expression with numeric operation instruction. Specialized Instruction, for example opt_plus which is replaced with + method invocation will do following code:

    def opt_plus(recv, val) # simple version
       if recv.class == Fixnum && val.class == Fixnum
         if Fixnum#+ is not redefined
           return calculate "recv + val" without method call
         end
       end
       # normal method invocation
       recv.+(val)
    end
    

    Check receiver and value are Fixnum or not, and check Fixnum#+ are not redefined. After these check, calculate them without method invocation. In fact, Float#+ are also checked. There are other specialized instructions.

    YARV eases to implement such instructions with VM generator. You shouldn't write bothersome code such as stack manipulation. If you write VM instruction such as opt_plus in simple VM DSL, VM generator will translate it to C code.

    Specialized Instruction is very simple, but effective for simple benchmark such as fib() or tak() and some calculate bound program.

    Read more…

  • 27

    APR
    2007

    The Ruby VM: Episode III

    Let's talk a little about threading, since that's a significant change in the new VM. First, can you please explain the old threading model used in Ruby 1.8 and also the new threading model now used in Ruby 1.9?

    Matz:

    Old threading model is the green thread, to provide universal threading on every platform that Ruby runs. I think it was reasonable decision 14 years ago, when I started developing Ruby. Time goes by situation has changed. pthread or similar threading libraries are now available on almost every platform. Even on old platforms, pth library (a thread library which implements pthread API using setjmp etc.) can provide green thread implementation.

    Koichi decided to use native thread for YARV. I honor his decision. Only regret I have is we couldn't have continuation support that used our green thread internal structure. Koichi once told me it's not impossible to implement continuation on YARV (with some restriction), so I expect to have it again in the future. Although it certainly has lower priority in 1.9 implementation.

    ko1:

    Matz explained old one, so I show you YARV's thread model.

    As you know, YARV support native thread. It means that you can run each Ruby thread on each native thread concurrently.

    It doesn't mean that every Ruby thread runs in parallel. YARV has global VM lock (global interpreter lock) which only one running Ruby thread has. This decision maybe makes us happy because we can run most of the extensions written in C without any modifications.

    Read more…

  • 16

    APR
    2007

    No Longer the Fastest Game in Town

    If your number one concern when working with CSV data in Ruby is raw speed, you might want to know that FasterCSV is no longer the fastest option.

    There are a couple of new contenders for Ruby CSV processing including a C extension called SimpleCSV and a pure Ruby library called LightCsv. I haven't been able to test SimpleCSV locally, because I can't get it to build on my box, but users do tell me it's faster. I have run some trivial benchmarks for LightCsv though and it too is pretty quick:

    $ rake benchmark
    (in /Users/james/Documents/faster_csv)
    time ruby -r csv -e '6.times { CSV.foreach("test/test_data.csv") { |row| } }'
    
    real    0m5.481s
    user    0m5.468s
    sys     0m0.010s
    time ruby -r lightcsv -e \
    '6.times { LightCsv.foreach("test/test_data.csv") { |row| } }'
    
    real    0m0.358s
    user    0m0.349s
    sys     0m0.008s
    time ruby -r lib/faster_csv -e \
    '6.times { FasterCSV.foreach("test/test_data.csv") { |row| } }'
    
    real    0m0.742s
    user    0m0.732s
    sys     0m0.009s
    

    It's important to note that LightCsv is indeed very "light." FasterCSV has grown up into a feature rich library that provides many different ways to look at your data. In contrast, LightCsv doesn't yet allow you to set column or row separators. Given that, it's only an option for vanilla CSV you just need to iterate over. If that's what you have though, and speed counts, it might just be the right choice.

    Read more…

  • 16

    MAR
    2007

    The Ruby VM: Episode II

    We started these talks because of the excitement around the alternate implementations, like JRuby and Rubinius. How do you feel about all of these new interpreters and how do you see them affecting the official development of Ruby?

    Matz:

    Alternate implementations mean maturity of Ruby language. I'm glad for the fact. But we have never had enough number of developers for core, so I think we need more cooperation between implementations. I had a good talk about future Ruby spec. with Charles Nutter recently. I expect occasion like this more often.

    ko1:

    I think having alternatives is very important. I want to know how to implement Ruby and apply these techniques to YARV.

    In fact, implementing from scratch is very fun. YARV (official Ruby implementation) has many problems resulted from historical reasons (a biggest problem is compatibility to extension libraries).

    Have you downloaded and installed any of the other interpreters?

    Read more…

  • 16

    FEB
    2007

    The Ruby VM: Episode I

    Hello and thank you both for agreeing to answer my questions. To begin, would you please introduce yourselves and tell us about your role in Ruby's development?

    Matz:

    I am the designer and the first implementer of the Ruby language. My real name is Yukihiro Matsumoto, that sounds something like You-Key-Hero Matz-Motor in English. But it's too long to remember and pronounce, so just call me Matz.

    I have been developing Ruby since 1993. It is now quite complicated and has performance problem. I have had vague plan of rewriting the interpreter for long time, but I have never been motivated enough to throw out the current interpreter and start developing new one.

    Then Koichi came in with YARV that seemed to have much brighter future than my vaporware - it runs - so I asked him to take a role of the official implementer of the core. Although I enjoy both designing and implementation of the language, I don't think I am gifted for language implementation. So I thought that it might be the time to focus on designing when I saw YARV.

    ko1:

    Thank you for your interest in YARV and me. BTW, I'm thinking what "YARV" stand for. Because it is not Yet Another. Someone proposed that "YARV ain't RubyVM". If YARV means "YARV ain't RubyVM", what is YARV?

    I'm Koichi Sasada. Koichi is given name, and "ichi" means "one" in Japanese. So I use "ko1" as my nick. I'm an assistant at Department (...snip...) of Tokyo. My research interest is systems software, especially operating system, programming language, parallel systems, and so on. And I'm a member of Nihon Ruby no Kai (Ruby Association in Japan). I plan(ed) some Ruby events like RubyKaigi and am an editor of Rubyist Magazine. I also develop(ed) Nadoka, Rava, Rucheme, and some projects. Say, I'm a developer of YARV: Yet Another RubyVM.

    My role in Ruby's development? To steal VM hacking pleasure from Matz?

    Read more…

  • 29

    JUL
    2006

    YARV Looking Promising, James's C is Not

    I participated in the ICFP programming contest last weekend with a group of friends. We had a great time with the event and learned a ton. I thought I should share two interesting insights with others that might appreciate them.

    First, YARV looks very promising for some general speed increases in Ruby. If you are not familiar with YARV, that's the virtual machine that will run Ruby 1.9. During the contest, we ran into some performance issues with our Ruby solution and after we had optimized all we could think of, we decided to try running our entry on the experimental YARV VM to see if it was faster there. Good news: it was a lot faster.

    Please do not take these numbers as anything more than very non-scientific observations, but we did notice a huge speed increase on YARV. We were reliably waiting around 15 minutes for one section of our program to run on Ruby 1.8.4, but when we introduced YARV the same section generally ran in just under seven minutes. You heard me right there, it was over twice as fast. I think that's very promising news for the future of Ruby.

    Read more…