Как я могу ускорить этот код Rails? - PullRequest
0 голосов
/ 26 мая 2011

Это неопределенный вопрос, который я знаю .... но производительность этого блока кода ужасна.От рендеринга страницы требуется около 15 сек. От действия к действию ...

Цель этого действия - извлечь все профессии из резюме, все навыки из этого резюме и профессии.Они должны быть организованы в 2 массива:

  • первый массив содержит все профессии (без дубликатов) и располагает их в порядке их оценки.Для каждой двойной найденной записи показатель увеличивается на 1

  • , второй массив содержит ВСЕ навыки как из массива занятий, так и из резюме.Опять же, не допускается дублирование, но для каждого встреченного дублирования счет существующего увеличивается на единицу.

Ниже приведен блок кода, который выполняет эту операцию.Он сравнительно большой по сравнению с другими моими фрагментами кода, но я надеюсь, что он понятен.Я знаю, что работа с массивами, как и у меня, сбивает с толку, но вот что означает каждая позиция массива:

  • позиция 0: объект навыка / объекта занятия
  • позиция 1: оценказаписи
  • позиция 2: местоположение, найденное в db
  • положение 3: местоположение, найденное в cv

    определение категории

    @cv = Cv.find(params[:cv_id], :include => [:desired_occupations, :past_occupations, :educational_skills])
    @menu = :second
    @language = Language.resolve(:code => :en, :name => :en)
    @occupation_hashes = []
    @skill_hashes = []
    
    (@cv.desired_occupations + @cv.past_occupations).each do |occupation|
      section = []
      section << 'Desired occupation' if @cv.desired_occupations.include? occupation
      section << 'Work experience' if @cv.past_occupations.include? occupation
    
      unless (array = @occupation_hashes.assoc(occupation)).blank?
        array[1] += 1
        array[2] = (array[2] & section).uniq
      else
        @occupation_hashes << [occupation, 1, section]
      end
    
      occupation.skills.each do |skill|
        unless (array = @skill_hashes.assoc skill).blank?
          label = occupation.concept.label(@language).value
          array[1]+= 1
          array[3] << label unless array[3].include? label
        else
          @skill_hashes << [skill, 1, [], [occupation.concept.label(@language).value]]
        end
      end
    end
    
    @cv.educational_skills.each do |skill|
      unless (array = @skill_hashes.assoc skill).blank?
        array[1]+= 1
        array[3] << 'Education skills' unless array[3].include? 'Education skills'
      else
        @skill_hashes << [skill, 1, ['Education skills'], []]
      end
    end
    
    # Sort the hashes
    @occupation_hashes.sort! { |x,y| y[1] <=> x[1]}
    @skill_hashes.sort! { |x,y| y[1] <=> x[1]}
    
    @max = @skill_hashes.first[1]
    @min = @skill_hashes.last[1] end
    

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

Ответы [ 2 ]

1 голос
/ 26 мая 2011

Вы должны выполнить профилирование в своем коде, чтобы увидеть, что занимает много времени.Вы можете выяснить, как работать с профилировщиками, или просто добавить несколько простых операторов puts или logger.info в ваш код с отметкой времени.Вероятно, проще всего сделать это с помощью Benchmark.Примечание: вам может понадобиться require 'benchmark' ... не уверен, если это требуется автоматически в Rails или нет.

Для отдельной строки вы можете сделать что-то вроде этого:

    logger.info Benchmark.measure { @cv = Cv.find(params[:cv_id], :include => [:desired_occupations, :past_occupations, :educational_skills]) }

И для синхронизации больших блоков кода:

logger.info Benchmark.measure do
  (@cv.desired_occupations + @cv.past_occupations).each do |occupation|
    section = []
    section << 'Desired occupation' if @cv.desired_occupations.include? occupation
    section << 'Work experience' if @cv.past_occupations.include? occupation

    unless (array = @occupation_hashes.assoc(occupation)).blank?
      array[1] += 1
      array[2] = (array[2] & section).uniq
    else
      @occupation_hashes << [occupation, 1, section]
    end
  end
end

Я бы просто начал с больших блоков, а затем сузил их.Не зная, с каким размером набора данных вы имеете дело, трудно сказать, что представляет собой проблемная зона.

Я также согласен с другими, что вам будет гораздо лучше разбить эту вещь на более мелкие методы.,Это также облегчит тестирование производительности, так как вы можете делать такие вещи, как:

Benchmark.measure { 10000.times { foo.do_that_thing_that_might_be_slow }} 
1 голос
/ 26 мая 2011

Это вполне блок кода там. Как правило, если вы пишете такие серьезные методы, у вас будут проблемы с его поддержкой в ​​будущем. Техника, которая может помочь, состоит в том, чтобы разбить этот монолитный кусок кода и превратить его в вспомогательный класс, который выполняет обработку на более логических этапах, упрощая его точную настройку.

Например, интерфейс может быть:

@categorizer = CvCategorizer.new(params[:cv_id])

Это инкапсулирует все вышеперечисленное и сохранит его в переменных экземпляра, сделанных доступными благодаря объявлению с attr_reader.

Использование служебного класса означает, что вы можете разбить инициализацию на более понятные шаги:

def initialize(cv_id)
  # Call a wrapper method that loads the CV
  @cv = self.load_cv(cv_id)

  # Perform discrete steps to re-order the imported data
  self.organize_occupations
  self.organize_skills
end

Действительно трудно сказать, почему это медленно, просто глядя на него, хотя я бы очень внимательно обратил внимание на log/development.log, чтобы увидеть, что там происходит. Может быть, начальная загрузка мучительно медленная, но остальная часть метода в порядке.

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