Машаллинг в Ruby 2.2.0 медленнее, чем 2.1.5

У меня есть сложный объект my_object, который я маршалирую с помощью

Marshal.dump(мой_объект)

Я проверил производительность вызова этой линии 100 раз как в 2.1.5p273, так и в 2.2.0, и ниже приведены результаты:

2.1.5  
                  user     system      total        real
Marshal Dump  7.310000   0.120000   7.430000 (  8.988470)
Marshal Dump  7.520000   0.050000   7.570000 (  8.210356)
Marshal Dump  7.610000   0.050000   7.660000 (  8.432685)

2.2.0
                  user     system      total        real
Marshal Dump 26.480000   0.150000  26.630000 ( 29.591742)
Marshal Dump 24.100000   0.300000  24.400000 ( 28.520397)
Marshal Dump 26.210000   0.210000  26.420000 ( 29.993412)

(Для тщательности я запускал тест 3 раза для каждой версии.)

Как видите, в 2.2.0 это занимает в 3 раза больше времени, чем в 2.1.5. Я сосредоточился на Marshal.dump, потому что использование драгоценного камня ruby-prof показало мне, что эта строка работает плохо; но я не могу найти способ получить методы, которые сам Marshal.dump вызывает в профилировщике.

Изменить: см. мой ответ с минимальным воспроизведением, найденным после долгих экспериментов.


person davej    schedule 21.01.2015    source источник
comment
Для тех, кто хочет следовать этому, я урезал его до минимальной копии и записал здесь: bugs.ruby-lang.org/issues/10761. Проблема, похоже, не в размере, а в сложности, так как мне пришлось создать объект со многими уровнями вложенных объектов внутри него, чтобы воспроизвести проблему.   -  person davej    schedule 21.01.2015
comment
То, как вы отредактировали, из-за которого исходный вопрос исчез, а этот вопрос… ну, это не вопрос, это в основном ответ. Я думаю, что все это интересно, но для стандартов SO я думаю, что это заслуживает отрицательного голоса.   -  person iain    schedule 21.01.2015
comment
@ian: Правда? Мой выбор: оставить вопрос, который на самом деле никому не помог, или опубликовать что-то, что, я бы сказал, имеет довольно большое значение, или удалить весь вопрос. Я делал то, что считал лучшим для общества. Честно говоря, такая мелочность, как ваша, портит репутацию гикам.   -  person davej    schedule 21.01.2015
comment
Да, действительно. Это не мелочь, это то, как работает сайт. Я пришел к этому вопросу, и исходный вопрос ушел, а ответ на его месте! Если у вас есть ответ, используйте форму ответа. Если у вас есть изменения, которые нужно внести в вопрос, отредактируйте его так, чтобы он был понятен. Часто это просто строка и слово Edit: с дополнительной информацией.   -  person iain    schedule 21.01.2015
comment
Я вернул его, как было, теперь в основном пустая трата времени и места, учитывая, что теперь известная основная причина довольно специфична, а исходный пост теперь в основном посторонний. Но правила есть правила....   -  person davej    schedule 21.01.2015
comment
Пространство в Интернете бесконечно, не беспокойтесь об этом ;-) Проголосуйте за правильно сформулированный и интересный вопрос ^_^   -  person iain    schedule 21.01.2015
comment
Давайте продолжим обсуждение в чате.   -  person davej    schedule 21.01.2015


Ответы (2)


Исходное местоположение: nil.

Marshal.method(:dump).source_location
#=> nil

Это означает, что это метод, реализованный на C, и больше нет кода Ruby, который вы можете отследить. Другими словами, это атомарный/элементарный метод.

Если вы считаете, что ваш результат верен, я предлагаю вам опубликовать его как ошибку в Рубиновый сундук. В новейших версиях Ruby действительно было обнаружено несколько проблем с производительностью, поэтому ваш случай не кажется необычным.

person sawa    schedule 21.01.2015
comment
спасибо за технику и предложение. Теперь начинается поиск крошечной воспроизводимой версии проблемы! - person davej; 21.01.2015

Замедление вызывает сортировка поплавков.

require 'benchmark'

class ToBeMarshaled

  def initialize n
    @a = []
    n.times do |i|
      @a << i.to_f
    end
  end

end

tbm = ToBeMarshaled.new(10000)

n = 100

Benchmark.bm do |x|
  x.report("Marshal Dump") {for i in 1..n; Marshal.dump(tbm); end}
end

результаты (тестирование выполнялось 3 раза для каждой версии Ruby):

2.1.5
                  user     system      total        real
Marshal Dump  0.640000   0.010000   0.650000 (  0.744080)
Marshal Dump  0.670000   0.000000   0.670000 (  0.758597)
Marshal Dump  0.650000   0.020000   0.670000 (  0.747583)

2.2.0
                  user     system      total        real
Marshal Dump 25.070000   0.220000  25.290000 ( 27.980023)
Marshal Dump 24.100000   0.160000  24.260000 ( 26.633049)
Marshal Dump 24.440000   0.230000  24.670000 ( 27.540826)

~ в 35 раз медленнее.

Если вы удалите «.to_f» из этого кода, вы получите:

2.1.5
                  user     system      total        real
Marshal Dump  0.160000   0.000000   0.160000 (  0.180247)
Marshal Dump  0.180000   0.000000   0.180000 (  0.189485)
Marshal Dump  0.160000   0.010000   0.170000 (  0.191304)

2.2.0
                  user     system      total        real
Marshal Dump  0.120000   0.010000   0.130000 (  0.146710)
Marshal Dump  0.130000   0.010000   0.140000 (  0.159851)
Marshal Dump  0.130000   0.000000   0.130000 (  0.143917)

2.2.0 немного вытесняет 2.1.5.

person davej    schedule 21.01.2015