如何简化和优化我的代码?

时间:2011-03-19 00:18:43

标签: ruby-on-rails optimization simplify

我有一些我想优化的代码。 首先,一点都不差,但也许它可能会更短或更快,主要是update_result方法:

class Round < ActiveRecord::Base
  belongs_to :match
  has_and_belongs_to_many :banned_champions, :class_name => "Champion", :join_table => "banned_champions_rounds"
  belongs_to :clan_blue, :class_name => "Clan", :foreign_key => "clan_blue_id"
  belongs_to :clan_purple, :class_name => "Clan", :foreign_key => "clan_purple_id"
  belongs_to :winner, :class_name => "Clan", :foreign_key => "winner_id"

  after_save {self.update_result}

  def update_result
    match = self.match
    if match.rounds.count > 0
      clan1 = match.rounds.first.clan_blue
      clan2 = match.rounds.first.clan_purple
      results = {clan1=>0, clan2=>0}
      for round in match.rounds
        round.winner == clan1 ? results[clan1] += 1 : results[clan2] += 1
      end
      if results[clan1] > results[clan2] then
        match.winner = clan1; match.looser = clan2
        match.draw_1 = nil; match.draw_2 = nil
      elsif results[clan1] < results[clan2] then
        match.winner = clan2; match.looser = clan1
        match.draw_1 = nil; match.draw_2 = nil
      else
        match.draw_1 = clan1; match.draw_2 = clan2
        match.winner = nil; match.looser = nil
      end
      match.save
    end
  end
end

第二,种子完全糟糕和缓慢.rb:

require 'faker'

champions = [{:name=>"Akali"},
{:name=>"Alistar"},
{:name=>"Amumu"},
{:name=>"Anivia"},
{:name=>"Annie"},
{:name=>"Galio"},
{:name=>"Tryndamere"},
{:name=>"Twisted Fate"},
{:name=>"Twitch"},
{:name=>"Udyr"},
{:name=>"Urgot"},
{:name=>"Veigar"}
]

Champion.create(champions)


10.times do |n|
  name = Faker::Company.name
  clan = Clan.create(:name=>name)
  6.times do |n|
    name = Faker::Internet.user_name
    clan.players.create(:name=>name)
  end
end

for clan in Clan.all do
  2.times do
    match = Match.create()
    c = [clan,Clan.first(:offset => rand(Clan.count))]
    3.times do
      round = match.rounds.create
      round.clan_blue = c[0]
      round.clan_purple = c[1]
      round.winner = c[0]
      round.save!
    end
    for item in c
      for p in item.players.limit(5)
        rand_champion = Champion.first(:offset => rand(Champion.count))
        match.participations.create!(:player => p, :champion => rand_champion)
      end
    end
    match.save!
  end
  2.times do
    match = Match.create()
    c = [clan,Clan.first(:offset => rand(Clan.count))]
    3.times do
      round = match.rounds.create
      round.clan_blue = c[0]
      round.clan_purple = c[1]
      round.winner = c[1]
      round.save!
    end
    for item in c
      for p in item.players.limit(5)
        rand_champion = Champion.first(:offset => rand(Champion.count))
        match.participations.create!(:player => p, :champion => rand_champion)
      end
    end
    match.save!
    end
  2.times do
    match = Match.create()
    c = [clan,Clan.first(:offset => rand(Clan.count))]
    2.times do |n|
      round = match.rounds.create
      round.clan_blue = c[0]
      round.clan_purple = c[1]
      round.winner = c[n]
      round.save!
    end
    for item in c
      for p in item.players.limit(5)
        rand_champion = Champion.first(:offset => rand(Champion.count))
        match.participations.create!(:player => p, :champion => rand_champion)
      end
    end
    match.save!
  end
end

有机会优化它们吗?

3 个答案:

答案 0 :(得分:2)

不要低估空格在清理代码可读性方面的价值!

class Round < ActiveRecord::Base
  belongs_to :match

  belongs_to :clan_blue,   :class_name => "Clan", :foreign_key => "clan_blue_id"
  belongs_to :clan_purple, :class_name => "Clan", :foreign_key => "clan_purple_id"
  belongs_to :winner,      :class_name => "Clan", :foreign_key => "winner_id"

  has_and_belongs_to_many :banned_champions, :class_name => "Champion", :join_table => "banned_champions_rounds"

  after_save { match.update_result }
end

class Match < ActiveRecord::Base
  def update_result
    return unless rounds.count > 0

    clan1, clan2 = rounds.first.clan_blue, rounds.first.clan_purple

    clan1_wins = rounds.inject(0) {|total, round| total += round.winner == clan1 ? 1 : 0 }
    clan2_wins = rounds.length - clan1_wins

    self.winner = self.loser = self.draw_1 = self.draw_2 = nil

    if clan1_wins == clan2_wins
      self.draw1, self.draw2 = clan1, clan2
    else
      self.winner = clan1_wins > clan2_wins ? clan1 : clan2
      self.loser  = clan1_wins < clan2_wins ? clan1 : clan2
    end

    save
  end  
end

对于你的种子,我会用工厂模式替换你的灯具,如果用于测试的话。但是,如果你要坚持你所拥有的东西,那么将整个块包装在交易中,它应该会变得更快。

答案 1 :(得分:1)

嗯,在你的第一个例子中,你似乎正在强迫Match行为进入Round类,这与抽象OOP不一致。您的update_result方法实际上属于您的Match类。一旦你这样做,我认为代码会清理一下。

在你的第二个例子中,很难看出你想要做什么,但它的速度太慢并不奇怪。每个创建和保存都会生成一个单独的数据库调用。乍一看,您的代码会生成超过一百个单独的数据库保存。你真的需要所有这些记录吗?你能结合一些保存吗?

除此之外,您可以使用build而不是create将数据库调用减半,如下所示:

  round = match.rounds.build
  round.clan_blue = c[0]
  round.clan_purple = c[1]
  round.winner = c[0]
  round.save!

如果您想保存一些代码行,可以用以下语法替换上面的内容:

match.rounds.create(:clan_blue_id => c[0].id, :clan_purple_id => c[1].id, :winner_id => c[0].id)

答案 2 :(得分:1)

在种子文件中:     c = [clan,Clan.first(:offset =&gt; rand(Clan.count))] 这有效,但看起来你在Ruby中选择一个随机数。根据我的理解,如果你可以用SQL而不是Ruby做一些事情,它通常会更快。试试这个:

c = [clan,Clan.find(:all, :limit => 1, :order => 'random()')

你不会获得太多的收益,因为它只为每个战队运行两次(总共20倍),但有类似这两行的

# (runs 60x total)
    rand_champion = Champion.first(:offset => rand(Champion.count))

# (runs up to 200x, I think)
    c = [clan,Clan.first(:offset => rand(Clan.count))]

一般情况下,您几乎可以始终在程序中找到更多要优化的内容。因此,从最重复的区域开始 - 最深层嵌套的循环,可以最有效地利用您的时间。我将把上述2行(以及其他可能类似的行)优化为练习。如果您遇到问题,请在评论中告诉我。

另外,我相信你会在很多回复中得到很多好的建议,所以我强烈建议您设置一个基准测试程序,以便衡量差异。对于您测试的每个版本,请确保多次运行它,这样您就可以获得良好的平均值(在后台运行的程序可能会使您的结果失效)。

就简单性而言,我认为可读性非常重要。它不会让你的代码运行得更快,但它可以让你的调试更快(而且你的时间很重要!)。给我带来麻烦的一些事情是非描述变量,如cp。我有时也这样做,但是当你在同一范围内有多个这些变量时,我很快就会达到一个点,我认为“那个变量又是什么?”。像temp_clan而不是c之类的东西还有很长的路要走。

为了便于阅读,我还希望.each代替for。不过,这完全是个人偏好。

btw我喜欢英雄联盟:)

编辑:(评论不会让我缩进代码)再看看,我意识到这个片段可以进一步优化:

  for p in item.players.limit(5)
    rand_champion = Champion.first(:offset => rand(Champion.count))
    match.participations.create!(:player => p, :champion => rand_champion)
  end

更改Champion.first(:offset => rand(Champion.count))

rand_champs = Champion.find(:all, :limit => 5, :order => 'random()')
for p ...
  i = 0
  match.participations.create!(:player => p, :champion => rand_champs(i))
  i++
end

这会将5个SQL查询减少为1.因为它被称为60x,这会将你的SQL查询从60减少到12.作为额外的加分,你不会在同一个团队中获得重复的冠军,(或者我猜如果这是你的意图可能是一个缺点)