За последний год разработки моего шахматного движка (www.chessbin.com) большая часть времени была потрачена оптимизация моего кода для лучшего и более быстрого поиска ходов. За это время я научился нескольким приемам, которыми хочу поделиться с вами.
Измерение эффективности
По сути, вы можете улучшить свою производительность двумя способами:
- Оценивайте свои узлы быстрее
- Ищите меньшее количество узлов, чтобы получить тот же ответ
Вашей первой проблемой при оптимизации кода будет измерение. Как понять, что вы действительно изменили ситуацию? Чтобы помочь вам с этой проблемой, вам нужно убедиться, что вы можете записать некоторую статистику во время поиска хода. Вот те, которые я фиксирую в своем шахматном движке:
- Время, которое потребовалось для завершения поиска.
- Количество найденных узлов
Это позволит вам сравнить и протестировать ваши изменения. Лучший способ приблизиться к тестированию — создать несколько сохранений из начальной позиции, мидгейма и эндгейма. Запишите время и количество искомых узлов для черного и белого. После внесения каких-либо изменений я обычно провожу тесты с упомянутыми выше сохраненными играми, чтобы увидеть, внес ли я улучшения в две вышеупомянутые матрицы: количество искомых узлов или скорость.
Чтобы еще больше усложнить ситуацию, после внесения изменения в код вы можете запускать свой движок 3 раза и каждый раз получать 3 разных результата. Допустим, ваш шахматный движок нашел лучший ход за 9, 10 и 11 секунд. То есть разброс около 20%. Итак, вы улучшили свой движок на 10%-20% или просто изменили нагрузку на свой компьютер. Откуда вы знаете? Для борьбы с этим я добавил методы, которые позволят моему движку играть против самого себя, он будет делать ходы и за белых, и за черных. Таким образом, вы можете проверить разницу во времени не только для одного хода, но и для серии из 50 ходов в течение игры. Если в прошлый раз игра длилась 10 минут, а теперь 9, то вы, вероятно, улучшили свой движок на 10%. Повторный запуск теста должен подтвердить это.
Поиск повышения производительности
Теперь, когда мы знаем, как измерять прирост производительности, давайте обсудим, как определить потенциальный прирост производительности.
Если вы работаете в среде .NET, вам поможет профилировщик .NET. Если у вас есть версия Visual Studio для разработчиков, она встроена бесплатно, однако есть и другие сторонние инструменты, которые вы можете использовать. Этот инструмент сэкономил мне часы работы, так как он подскажет, где ваш двигатель тратит большую часть своего времени, и позволит вам сосредоточиться на проблемных местах. Если у вас нет инструмента профилирования, вам, возможно, придется как-то регистрировать временные метки, когда ваш движок проходит различные этапы. Я не предлагаю этого. В этом случае хороший профайлер на вес золота. Red Gate ANTS Profiler дорогой, но лучший из тех, что я когда-либо пробовал. Если вы не можете себе это позволить, по крайней мере, используйте его для их 14-дневной пробной версии.
Ваш профилировщик наверняка определит для вас некоторые вещи, однако вот несколько небольших уроков, которые я усвоил, работая с C#:
- Сделать все приватным
- Все, что вы не можете сделать приватным, сделайте это запечатанным
- Сделайте как можно больше методов статическими.
- Не делайте свои методы болтливыми, один длинный метод лучше, чем 4 маленьких.
- Шахматная доска, хранящаяся в виде массива [8][8], работает медленнее, чем массив из [64].
- Замените int на byte, где это возможно.
- Вернитесь от своих методов как можно раньше.
- Стеки лучше списков
- Массивы лучше, чем стеки и списки.
- Если вы можете определить размер списка, прежде чем заполнить его.
- Кастинг, бокс, распаковка — это зло.
Дальнейшее повышение производительности:
Я считаю, что генерация ходов и их порядок чрезвычайно важны. Однако вот в чем проблема, как я ее вижу. Если вы оцениваете счет каждого хода перед сортировкой и запуском альфа-бета, вы сможете оптимизировать порядок ходов, чтобы получить очень быстрые отсечки альфа-бета. Это потому, что вы сможете сначала попробовать лучший ход. Однако время, которое вы потратили на оценку каждого хода, будет потрачено впустую. Например, вы могли оценить счет на 20 ходах, отсортировать свои ходы, попробовать первые 2 и получить отсечку на ходу номер 2. Теоретически время, которое вы потратили на остальные 18 ходов, было потрачено впустую.
С другой стороны, если вы сделаете более легкую и более быструю оценку, скажем, просто захватывает, ваша сортировка будет не такой хорошей, и вам придется искать больше узлов (до 60% больше). С другой стороны, вы не стали бы тщательно оценивать каждый возможный ход. В целом этот подход обычно быстрее.
Нахождение этого идеального баланса между наличием достаточного количества информации для хорошей сортировки и отсутствием дополнительной работы над ходами, которые вы не будете использовать, позволит вам найти огромные преимущества в вашем алгоритме поиска. Кроме того, если вы выберете более плохой подход к сортировке, вы захотите сначала выполнить более поверхностный поиск, скажем, выполнить 3, отсортировать свой ход, прежде чем перейти к более глубокому поиску (это часто называют итеративным углублением). Это значительно улучшит вашу сортировку и позволит вам искать гораздо меньше ходов.
person
Adam Berent
schedule
13.07.2009