Почему наличие большого массива заставляет NoMethodError занимать больше времени - PullRequest
2 голосов
/ 20 февраля 2020

У меня есть следующий код Ruby, где каждый экземпляр BigClass создает массив с экземплярами BigClass (до максимальной глубины).

class BigClass
  # Increase this depending on your computer performance
  MAX_DEPTH = 8

  def initialize(depth = 0)
    @my_arr = []

    5.times do |i|
      unless depth > MAX_DEPTH
        @my_arr << BigClass.new(depth+1)
      end
    end
  end
end

big_class = BigClass.new

puts "Reaches here"

# This line should throw an error but instead freezes
big_class.blah

puts "Doesn't reach here"

При вызове метода, который не не существует, я ожидаю получить NoMethodError в значительной степени мгновенно, однако, кажется, что чем больше содержимое массива в big_class, тем больше времени требуется для возникновения ошибки.

Почему это так?

Несколько вариантов, которые я рассмотрел:

  • Может быть, Ruby выполняет какую-то очистку или вызывает некоторые хуки для всех созданных экземпляров, прежде чем выдать ошибку. Но если я произвожу ошибку из другого объекта или внутри существующего метода, это немедленно вызовет ошибку.
  • Возможно, поиск метода Ruby по некоторым причинам включает поиск переменных экземпляра, но после прочтения этого GitHub Gist Я не могу найти ничего, что указывало бы на то, что это было бы причиной

Что-то, что я заметил, это то, что когда он печатает «Достигнуто здесь», если я ухожу (CTRL- C) он немедленно распечатает NoMethodError. Так что, похоже, нет проблемы с поиском того, существует ли метод, иначе он не знал бы, что это ошибка, когда я ухожу.

1 Ответ

2 голосов
/ 20 февраля 2020

Я думаю, что сработал.

Если я переопределил #inspect метод BigClass, это больше не вызывает проблемы. Это заставляет меня думать, что что-то внутренне вызывает #inspect при обработке ошибки. Поскольку проверка по умолчанию будет включать в себя переменные экземпляра, а также вызывать inspect для них, если существует много переменных экземпляра, и у них всех есть много переменных экземпляра, то обработка может занять очень много времени.

код ниже показывает версию, которая работает так, как я ожидал:

class BigClass
  # Increase this depending on your computer performance
  MAX_DEPTH = 8

  def initialize(depth = 0)
    @my_arr = []

    5.times do |i|
      unless depth > MAX_DEPTH
        @my_arr << BigClass.new(depth+1)
      end
    end
  end

  def inspect
    "now it works"
  end
end

big_class = BigClass.new

puts "Reaches here"

# Correctly raises an error now
big_class.blah_blah_blah

puts "Doesn't reach here"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...