在我的国际象棋引擎 ( www.chessbin.com ) 开发的最后一年中,大部分时间都花在优化我的代码上,以便更好更快地进行移动搜索。在那段时间里,我学到了一些技巧,我想与你分享。
衡量绩效
本质上,您可以通过两种方式提高性能:
- 更快地评估您的节点
- 搜索更少的节点以得出相同的答案
您在代码优化中的第一个问题将是测量。你怎么知道你真的有所作为?为了帮助您解决这个问题,您需要确保可以在移动搜索过程中记录一些统计数据。我在国际象棋引擎中捕获的是:
这将允许您对更改进行基准测试和测试。进行测试的最佳方法是从开局、中局和终局创建多个保存游戏。记录黑白搜索的时间和节点数。在进行任何更改后,我通常会针对上述保存游戏进行测试,看看我是否在上述两个矩阵中做出了改进:搜索的节点数或速度。
更复杂的是,在进行代码更改后,您可能会运行引擎 3 次,每次都会得到 3 个不同的结果。假设您的国际象棋引擎在 9、10 和 11 秒内找到了最佳走法。这是大约 20% 的价差。那么你是把你的引擎提高了 10%-20% 还是只是改变了你电脑上的负载。你怎么知道的?为了解决这个问题,我添加了一些方法,可以让我的引擎与自己对战,它会为白色和黑色做出动作。通过这种方式,您不仅可以测试一个动作的时间变化,还可以测试游戏过程中多达 50 个动作的一系列动作。如果上次游戏需要 10 分钟,而现在需要 9 分钟,那么您的引擎可能提高了 10%。再次运行测试应该可以确认这一点。
寻找性能增益
既然我们知道如何衡量性能提升,让我们讨论如何识别潜在的性能提升。
如果您在 .NET 环境中,那么 .NET 分析器将是您的朋友。如果您有 Visual Studio for Developers 版本,它是免费内置的,但是您可以使用其他第三方工具。这个工具为我节省了几个小时的工作,因为它会告诉你你的引擎大部分时间都花在了哪里,并让你专注于你的问题点。如果您没有分析器工具,您可能必须以某种方式记录时间戳,因为您的引擎会经历不同的步骤。我不建议这样做。在这种情况下,一个好的分析器是物超所值的。Red Gate ANTS Profiler 很贵,但是我试过的最好的。如果你买不起,至少在他们的 14 天试用期使用它。
您的分析器会为您明确地识别事物,但是这里有一些我在使用 C# 时学到的小教训:
- 将所有内容设为私有
- 无论你不能将其设为私有,将其密封
- 使尽可能多的方法成为静态的。
- 不要让你的方法变得冗长,一个长的方法比 4 个小的方法好。
- 存储为数组 [8][8] 的棋盘比 [64] 的数组慢
- 尽可能将 int 替换为 byte。
- 尽早从您的方法中返回。
- 堆栈比列表好
- 数组比堆栈和列表更好。
- 如果您可以在填充列表之前定义列表的大小。
- 铸造,拳击,拆箱是邪恶的。
进一步的性能提升:
我发现移动生成和排序非常重要。然而,这是我所看到的问题。如果您在排序和运行 Alpha Beta 之前评估每个动作的得分,您将能够优化您的动作顺序,以便您将获得极快的 Alpha Beta 截止。这是因为您将能够首先尝试最好的动作。但是,您花费在评估每个动作上的时间将被浪费掉。例如,您可能已经评估了 20 个动作的得分,对您的动作进行排序尝试前 2 个动作并收到第 2 个动作的截止值。理论上,您在其他 18 个动作上花费的时间被浪费了。
另一方面,如果您进行更轻松、更快速的评估,例如仅捕获,您的排序将不会那么好,您将不得不搜索更多节点(最多 60% 以上)。另一方面,您不会对每一个可能的举动都进行严格的评估。总的来说,这种方法通常更快。
在拥有足够信息以进行良好排序和不对您不会使用的移动进行额外工作之间找到这种完美平衡,将使您在搜索算法中获得巨大收益。此外,如果您选择较差的排序方法,您将希望首先进行较浅的搜索,例如第 3 层,在进行更深入的搜索之前对您的移动进行排序(这通常称为迭代深化)。这将显着改善您的排序并允许您搜索更少的移动。