Ruby Multi线程,我做错了什么?

时间:2013-11-22 03:44:49

标签: ruby-on-rails ruby multithreading elasticsearch

所以,为了提高我们的应用程序的速度,我正在使用我们的rails应用程序试验多线程。 这是代码:

require 'thwait'
require 'benchmark'

city = Location.find_by_slug("orange-county", :select => "city, state, lat, lng", :limit => 1)
filters = ContractorSearchConditions.new()
image_filter = ImageSearchConditions.new()
filters.lat = city.lat
filters.lon = city.lng
filters.mile_radius = 20
filters.page_size = 15
filters.page = 1
image_filter.page_size = 5
sponsored_filter = filters.dup
sponsored_filter.has_advertised = true
sponsored_filter.page_size = 50
Benchmark.bm do |b|
  b.report('with') do
    1.times do
      cities = Thread.new{ 
        Location.where("lat between ? and ? and lng between ? and ?", city.lat-0.5, city.lat+0.5, city.lng-0.5, city.lng+0.5)
      }
      images  = Thread.new{ 
        Image.search(image_filter)[:hits]
      }
      sponsored_results_extended = Thread.new{ 
        sponsored_filter.mile_radius = 50
        @sponsored_results = Contractor.search( sponsored_filter )
      }
      results = Thread.new{
        Contractor.search( filters )
      }
      ThreadsWait.all_waits(cities, images, sponsored_results_extended, results)
      @cities = cities.value
      @images = images.value
      @sponsored_results = sponsored_results_extended.value
      @results = results.value
    end
  end
  b.report('without') do
    1.times do
      @cities = Location.where("lat between ? and ? and lng between ? and ?", city.lat-0.5, city.lat+0.5, city.lng-0.5, city.lng+0.5)
      @image = Image.search(image_filter)[:hits]
      @sponsored_results = Contractor.search( sponsored_filter )
      @results = Contractor.search( filters )
    end
  end
end

Class.search正在我们的ElasticSearch服务器上运行搜索。(负载均衡器后面有3台服务器),其中正在我们的RDS实例中运行活动记录查询。

(一切都在同一个数据中心。)

以下是我们的开发服务器上的输出:

Bob@dev-web01:/usr/local/dev/buildzoom/rails$ script/rails runner script/thread_bm.rb -e development
       user     system      total        real
with  0.100000   0.010000   0.110000 (  0.342238)
without  0.020000   0.000000   0.020000 (  0.164624)

Nota:如果对线程,互斥,GIL没有任何了解,我的知识非常有限。

2 个答案:

答案 0 :(得分:1)

由于线程的创建和管理,“with”块中的开销比“without”块的开销要大得多。当代码是IO绑定时,使用线程将最有帮助,并且看起来并非如此。四次搜索在20ms内完成(没有阻止),这意味着这些搜索应该花费更少的时间。 “with”块需要100ms才能执行,因此我们可以推断出至少80ms的时间不用于搜索。尝试使用较长的查询进行基准测试,以了解结果的不同之处。

请注意,我假设所有搜索都具有相同的延迟,这可能是也可能不是,并且始终执行相同的操作。 “无”块可能会受益于某种查询缓存,因为它在“with”块之后运行。交换基准的顺序时结果会有所不同吗?另外,我忽略了迭代(1.times)的开销。除非将迭代次数更改为大于1的值,否则应删除它。

答案 1 :(得分:1)

即使您正在使用线程,因此并行执行查询IO,您仍然需要反序列化从查询中返回的任何结果。这使用CPU。 MRI Ruby 2.0.0具有全局解释器锁。这意味着Ruby代码一次只能运行一行,而不是并行运行,而且只能运行在一个CPU核心上。为了反序列化所有结果,CPU必须在不同线程之间多次上下文切换。这比按顺序反序列化每个结果集要多得多。

如果通过等待查询的响应占主导地位,并且它们不会同时返回,那么并行化线程可能会有一个优势。但很难预测到这一点。

您可以尝试使用JRuby或Rubinius。这些都将使用多个内核,因此实际上可以按预期加速代码。