1

最適化したいコードがいくつかあります。まず、まったく悪くありませんが、主に 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

そして第二に、seeds.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

それらを最適化するチャンスはありますか?

4

3 に答える 3

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

シードについては、テスト用の場合は、フィクスチャをファクトリパターンに置き換えます。ただし、そこにあるものに固執する場合は、ブロック全体をトランザクションでラップすると、桁違いに速くなるはずです。

于 2011-03-22T17:53:36.080 に答える
1

シード ファイル: c = [clan,Clan.first(:offset => rand(Clan.count))] これは機能しますが、Ruby で乱数を選択しているようです。私が理解していることから、Ruby の代わりに SQL で何かを行うことができれば、一般的に高速です。これを試して:

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

クランごとに 2 回 (合計 20 倍) しか実行されないため、あまり多くの利益を得ることはありませんが、これら 2 つのような同様の行があります。

# (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 行 (およびその他の同様の行) の最適化は、演習として残します。問題がある場合は、コメントでお知らせください。

また、多くの回答で多くの優れた提案が得られると確信しているため、違いを測定できるようにベンチマーク ツールを設定することを強くお勧めします。テストするバージョンごとに数回実行して、適切な平均を取得できるようにしてください (バックグラウンドで実行されているプログラムが結果を台無しにする可能性があります)。

シンプルさに関しては、読みやすさはかなり重要だと思います。コードの実行が速くなるわけではありませんが、デバッグが速くなります (時間は重要です!)。私に問題を与えていたいくつかのことは、cとのような説明のない変数pでした。私も時々これを行いますが、同じスコープ内にこれらの変数がいくつかある場合、すぐに「その変数は何のためにあったのか?」と考えるようになります。長い道のりのtemp_clan代わりのようなもの。c

読みやすさのために、.each代わりにfor. ただし、それは完全に個人的な好みです。

ところで私はLeague of Legendsが大好きです:)

編集: (コメントではコードをインデントできません) もう一度見てみると、このスニペットをさらに最適化できることがわかりました。

  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 に減ります。さらにプラスとして、同じチームで繰り返しチャンピオンを取得することはありません (または、それはそれがあなたの意図である場合の欠点)

于 2011-03-24T02:58:45.177 に答える
1

最初の例では、Match 動作を Round クラスに強制しているように見えますが、これは抽象 OOP と一致していません。update_result メソッドは、実際には Match クラスに属しています。そうすれば、コード自体が少しきれいになると思います。

2 番目の例では、何をしようとしているのかわかりにくいですが、非常に遅いことは驚くべきことではありません。作成と保存のたびに、個別のデータベース呼び出しが生成されます。一見すると、コードは 100 を超える個別のデータベース セーブを生成します。それらすべての記録が本当に必要ですか。いくつかのセーブを組み合わせることはできますか?

さらに、次のように create の代わりに build を使用することで、データベース呼び出しを半分に減らすことができます。

  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)
于 2011-03-19T03:09:34.263 に答える