私のチェスエンジン(www.chessbin.com)の開発の最後の年の間、より良くそしてより速い動きの検索を可能にするために私のコードを最適化することに多くの時間が費やされました。その間、私はあなたと共有したいいくつかのトリックを学びました。
性能測定
基本的に、次の2つの方法でパフォーマンスを向上させることができます。
- ノードをより速く評価する
- 同じ答えを出すために、より少ないノードを検索します
コード最適化の最初の問題は測定です。あなたが本当に違いを生んだことをどうやって知っていますか?この問題を解決するには、移動検索中にいくつかの統計を記録できることを確認する必要があります。チェスエンジンでキャプチャするものは次のとおりです。
- 検索が完了するまでにかかった時間。
- 検索されたノードの数
これにより、変更のベンチマークとテストが可能になります。テストに取り組む最良の方法は、オープニングポジション、ミドルゲーム、エンドゲームからいくつかのセーブゲームを作成することです。黒と白を検索したノードの時間と数を記録します。変更を加えた後、私は通常、上記のセーブゲームに対してテストを実行して、上記の2つのマトリックス(検索されたノードの数または速度)が改善されたかどうかを確認します。
さらに複雑なことに、コードを変更した後、エンジンを3回実行し、毎回3つの異なる結果を得ることができます。あなたのチェスエンジンが9、10、11秒で最高の動きを見つけたとしましょう。それは約20%の広がりです。それで、エンジンを10%〜20%改善しましたか、それともPCの負荷を変化させただけでしたか。どうして知っていますか?これと戦うために、エンジンがそれ自体と対戦できるようにするメソッドを追加しました。これにより、白と黒の両方が動きます。このようにして、1回の移動での時間の変動だけでなく、ゲームの過程で50回もの一連の移動をテストできます。前回のゲームに10分かかり、現在は9分かかる場合は、エンジンが10%向上した可能性があります。テストを再度実行すると、これを確認できます。
パフォーマンスの向上を見つける
パフォーマンスの向上を測定する方法がわかったので、潜在的なパフォーマンスの向上を特定する方法について説明します。
.NET環境を使用している場合は、.NETプロファイラーが友だちになります。Visual Studio for Developersエディションをお持ちの場合は、無料で組み込まれていますが、他にも使用できるサードパーティツールがあります。このツールを使用すると、エンジンがほとんどの時間を費やしている場所がわかり、問題のある場所に集中できるため、作業時間を節約できます。プロファイラーツールがない場合は、エンジンがさまざまな手順を実行するときに、何らかの方法でタイムスタンプをログに記録する必要があります。私はこれを提案しません。この場合、優れたプロファイラーは金でその重量に見合う価値があります。Red Gate ANTS Profilerは高価ですが、私が今まで試した中で最高のものです。余裕がない場合は、少なくとも14日間のトライアルに使用してください。
あなたのプロファイラーはあなたのために物事を確実に識別します、しかしここに私がC#で働くことを学んだいくつかの小さな教訓があります:
- すべてをプライベートにする
- プライベートにできないものは何でも、封印してください
- できるだけ多くのメソッドを静的にします。
- メソッドをおしゃべりにしないでください。1つの長いメソッドの方が4つの小さなメソッドよりも優れています。
- 配列[8][8]として格納されているチェス盤は、[64]の配列よりも低速です。
- 可能な場合は、intをbyteに置き換えます。
- できるだけ早くメソッドから戻ってください。
- スタックはリストよりも優れています
- 配列はスタックやリストよりも優れています。
- リストにデータを入力する前に、リストのサイズを定義できる場合。
- キャスティング、ボクシング、アンボクシングは悪です。
さらなるパフォーマンスの向上:
ムーブの生成と順序付けは非常に重要だと思います。しかし、私が見ているように、ここに問題があります。アルファベータを並べ替えて実行する前に各移動のスコアを評価すると、移動の順序を最適化して、非常に迅速なアルファベータカットオフを取得できます。これは、ほとんどの場合、最初に最善の動きを試すことができるためです。ただし、各移動の評価に費やした時間は無駄になります。たとえば、20の手でスコアを評価し、最初の2つの手で並べ替えて、手番号2のカットオフを受け取ったとします。理論的には、他の18の手で費やした時間は無駄になります。
一方、キャプチャだけと言って、より軽くてはるかに高速な評価を行う場合、ソートはそれほど良くなく、より多くのノードを検索する必要があります(最大60%多く)。一方、あなたはすべての可能な動きについて重い評価をすることはありません。全体として、このアプローチは通常より高速です。
適切な並べ替えに十分な情報を用意することと、使用しない移動で余分な作業を行わないことのこの完璧なバランスを見つけることで、検索アルゴリズムに大きなメリットを見つけることができます。さらに、より貧弱なソートアプローチを選択した場合は、最初に浅い検索、たとえば3を重ねて、より深い検索に入る前に移動をソートする必要があります(これは反復深化と呼ばれることがよくあります)。これにより、並べ替えが大幅に改善され、検索する動きがはるかに少なくなります。