Я пытаюсь создать шахматный движок, используя альфа-бета-минимаксный алгоритм поиска, но код работает слишком медленно. Я сделал все оптимизации, о которых только мог подумать, но в одном потоке он все еще очень медленный. Я просмотрел исходный код некоторых других движков, чтобы увидеть, как они это делают, и вики по шахматному программированию (https://www.chessprogramming.org/Parallel_Search#Parallel_Alpha-Beta), но код выше моего уровня, и я не понимаю их. Я также не смог найти никаких письменных источников или фрагментов кода.
Может ли кто-нибудь объяснить, как эффективно реализовать многопоточность в алгоритме альфа-бета-поиска? Спасибо.
Около 3 секунд для глубины 4. На любую глубину, превышающую эту, требуется несколько минут. Он написан на C# для игрового движка Unity. Это просто минимакс с обрезкой альфа-бета. Пока нет транспозиции или порядка перемещения.
Какое представительство в совете вы используете? 2D-массив, 1D-массив, битборды? Если любой из первых двух, то ваша производительность является разумной. Сначала подумайте о том, чтобы сделать больше обычных оптимизаций, прежде чем делать многопоточность, многопоточность будет трудной для отладки. Порядок перемещения — это золото, и его очень легко реализовать. Также попробуйте отсечение нулевого хода, поиск PVS и, возможно, какое-то другое сокращение. Вы копируете доску или делаете/отменяете ход? Сделайте последнее. Много информации о шахматном программировании, на которую вы ссылались, поищите там больше оптимизаций.
Пожалуйста, уточните вашу конкретную проблему или предоставьте дополнительную информацию, чтобы выделить именно то, что вам нужно. Как сейчас написано, трудно точно сказать, о чем вы спрашиваете.
Альфа-бета по своей сути является последовательным алгоритмом, так как ваши альфа- и бета-значения постоянно обновляются в ходе поиска, а отсечки определяются на основе этих значений. По этой причине получить какое-либо ускорение за счет увеличения количества потоков очень сложно, и чем больше потоков вы бросите, тем меньше будет выигрыш.
Однако есть еще несколько способов сделать это, большинство из них довольно сложны и крайне плохо масштабируются при большем количестве потоков. Алгоритм перехода раньше был концепцией ожидания Young Brothers, это довольно сложный алгоритм, и он использовался, например, Stockfish до нескольких лет назад. Однако с увеличением количества ядер, доступных на современных компьютерах, масштабирование было очень плохим, а код очень сложным. Сегодня большинство современных движков используют то, что называется Lazy SMP. Этот алгоритм настолько прост, насколько это возможно, и масштабируется лучше, чем другие.
В ленивом SMP все, что вам нужно сделать, это запустить точно такой же поиск, как и обычно, только в нескольких потоках. Он зависит от наличия работающей таблицы транспонирования, через которую потоки взаимодействуют друг с другом. Потоки никогда не будут точно синхронизированы, и случайность приведет к тому, что каждый поток будет исследовать несколько разные части дерева поиска, а затем сохранять свои результаты в таблице транспонирования, где они могут быть использованы другим потоком. Конечно, каждый поток выполняет много повторяющейся работы, однако это все же лучше, чем пытаться умничать, разделяя работу и замедляя алгоритм, и это особенно верно, когда вы начинаете увеличивать количество потоков.
Я рекомендую вам заглянуть на вики по шахматному программированию, где вы даже можете найти псевдокод того, как это реализовать. https://www.chessprogramming.org/Lazy_SMP
Хотя я также должен отметить, что если то, что вы ищете, улучшает ваше время до глубины, реализация многопоточности не так много для вас сделает (а в некоторых крайних случаях она может даже замедлить ее!). Вместо этого вам нужна более агрессивная обрезка дерева поиска и более эффективная реализация (например, отсутствие выделения памяти, чтобы никогда не запускался сборщик мусора и т. д.).
большое спасибо! Мне было трудно понять логику этого. Я рассмотрю Lazy SMP.
Насколько медленно это медленно? На каком языке вы это пишете? Какие функции вы реализовали?