2016-01-03 3 views
4

Мой первый вопрос о SO, но я давно скрывался, поэтому вам придется простить меня, если я нарушил какие-либо правила или разместил вопрос с мусором.MRI ruby ​​threading and performance

Я пытаюсь лучше понять потоки, и я решил протестировать МРТ и посмотреть, как она работает в целом.

Учитывая следующий код (и вывод), почему операции с резьбой намного медленнее, чем вариант без резьбы?

код

class Benchmarker 
    def self.go 
    puts '----------Benchmark Start----------' 
    start_t = Time.now 
    yield 
    end_t = Time.now 
    puts "Operation Took: #{end_t - start_t} seconds" 
    puts '----------Benchmark End------------' 
    end 
end 

# using mutex 
puts 'Benchmark 1 (threaded, mutex):' 
Benchmarker.go do 
    array = [] 
    mutex = Mutex.new 
    5000.times.map do 
    Thread.new do 
     mutex.synchronize do 
     1000.times do 
      array << nil 
     end 
     end 
    end 
    end.each(&:join) 
    puts array.size 
end 

# using threads 
puts 'Benchmark 2 (threaded, no mutex):' 
Benchmarker.go do 
    array = [] 
    5000.times.map do 
    Thread.new do 
     1000.times do 
     array << nil 
     end 
    end 
    end.each(&:join) 
    puts array.size 
end 

# no threads 
puts 'Benchmark 3 (no threads):' 
Benchmarker.go do 
    array = [] 
    5000.times.map do 
    1000.times do 
     array << nil 
    end 
    end 
    puts array.size 
end 

выход

Benchmark 1 (threaded, mutex): 
----------Benchmark Start---------- 
5000000 
Operation Took: 3.373886 seconds 
----------Benchmark End------------ 
Benchmark 2 (threaded, no mutex): 
----------Benchmark Start---------- 
5000000 
Operation Took: 5.040501 seconds 
----------Benchmark End------------ 
Benchmark 3 (no threads): 
----------Benchmark Start---------- 
5000000 
Operation Took: 0.454665 seconds 
----------Benchmark End------------ 

Спасибо заранее.

ответ

5

Как только вы нажмете большое количество потоков (5000), количество накладных расходов для переключения между потоками планировщиком намного превышает объем работы, выполняемой каждой нитью. Как правило, вы хотите 30-50 потоков макс.

Попробуйте уменьшить количество нитей и пропорционально увеличению объема работы, что каждый делает:

20.times.map do 
    Thread.new do 
     250000.times do 
     array << nil 
     end 
    end 
    end.each(&:join) 

и вы увидите гораздо более сопоставимые результаты.

Обратите внимание, что вы, вероятно, увидите нижнюю границу Time(threaded) >= Time(non-threaded) - это время в потоковой версии не может быть ниже однопоточной версии. Это из-за GIL MRI, который позволяет выполнять только один поток за раз (они никогда не могут работать параллельно). Некоторые реализации Ruby, такие как JRuby, позволяют выполнять параллельное выполнение потоков.

+1

Спасибо Мартину, просто перепробовали и обнаружили, что результаты действительно намного более сопоставимы. Отмечено, как ответили. –