Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This very much depends on the use case. If you are truly CPU-bound than yes, ruby is a bad choice. Similarly, if you are IO-bound or otherwise need a lot of concurrency ruby is not great. I also think it's problematic for large code bases with hundreds of developers. However, there is a huge swath of web apps and APIs that just need to pull a bit of data from a DB or two, munge it together and send it to the client. Rails is perfectly horizontally scalable and plenty fast for this even at scale given Amdahl's law.

That said, there are definitely some self-inflicted wounds people run into here. Of course there are the oft-cited expert beginner problems like n+1 queries and missing indices, but there's a more subtle issue as well: ActiveRecord itself is so convenient that it discourages writing baseline efficient code even when you need it. Basically any time you need to process a medium to large amount of data, ActiveRecord acts like a sort of super boxed type (to use Java terminology) adding a huge amount of overhead. Yes it's true that ruby doesn't even have true primitive types that can achieve the performance of Go or Java, but often times ruby primitives are plenty fast, you just have to be willing to write a bit of extra code, and ActiveRecord as an ORM has plenty of escape hatches at various levels of granularity to facilitate this (eg. pluck, find_by_sql, update_all, execute, etc).



I definitely agree that when writing Rails at scale, it's extremely important to view ActiveRecord as just another tool in the toolbox, and not always effective for every use case. I've worked in places that had a dogmatic aversion to ever writing raw SQL, and to a one they always suffered for it in performance sensitive code.

At the places who did handle this well, the "high-performance code" that needs to be hand-tuned SQL is usually much smaller than you think (a few queries here and there), and ActiveRecord is still great for your simple queries or for smallish tables.


Could you go into more detail about what you see as issues with IO bound tasks?

My understanding is that MRI Ruby provides non-block IO operations if you wrap them in a thread and that it is only CPU bound tasks that are blocked by the GVL.

Is there some other issue related to that?

(JRuby provides fully multi-threading for cpu bound tasks without GVL).


"My understanding is that MRI Ruby provides non-block IO operations if you wrap them in a thread and that it is only CPU bound tasks that are blocked by the GVL."

All IO operations in ruby are subject to the GIL (global interpreter lock).


GVL is an implementation detail rather than a feature of the language (I believe the term Global VM Lock replaced GIL in the standard library, sometime around ruby 2.0ish I think).

JRuby, for example, has no GVL including for CPU based code; everything there can run in parallel.

Even in MRI Ruby though, wrapping IO operations in a thread allows you to release the GVL when the IO operation blocks.

e.g.

  5.times.map do
    Thread.new do
      Net::HTTP.get('example.com', '/index.html')
    end
  end.each(&:join)
Will perform those network requests in parallel rather than sequencially. This is how ActiveRecord can perform asynchronous database calls in parallel on MRI Ruby.

I got that HTTP example from[1], which has a good write up but it's also covered in Working with Ruby Threads by Jesse Storimer[2].

I asked the original question because in the Concurrent-Ruby Readme they discuss Ruby's mutable references and the possibility of thread safety issues because of that.[3]

1. https://pawelurbanek.com/ruby-concurrent-requests

2. https://www.goodreads.com/book/show/17826435-working-with-ru...

3. https://github.com/ruby-concurrency/concurrent-ruby




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: