Обновление и поиск в реальном времени Haskell

Я пишу игровой ай (aichallenge.org - Ants), который требует много обновлений и ссылается на структуры данных. Я пробовал как массивы, так и карты, но основная проблема заключается в том, что каждое обновление создает новое значение, что делает его медленным. Игра загружает вас, если вы потратите более одной секунды, чтобы сделать свой ход, поэтому приложение считается "жестким в реальном времени". Возможно ли иметь производительность изменчивых структур данных в Haskell, или я должен изучить Python или переписать мой код в OCaml?

Я полностью переписал "стартовый пакет" муравьев. Изменено с массивов на Карты, потому что мои тесты показали, что Карты обновляются намного быстрее.

Я запустил версию Maps с профилированием, которая показала, что около 20% времени выполняется только обновлениями карт.

Вот простая демонстрация того, насколько медленны обновления Array.

slow_array =
    let arr = listArray (0,9999) (repeat 0)
        upd i ar = ar // [(i,i)]
    in  foldr upd arr [0..9999]

Теперь, оценивая slow_array! 9999 занимает почти 10 секунд! Хотя было бы быстрее применять все обновления сразу, пример моделирует реальную проблему, когда массив должен обновляться каждый ход, и предпочтительно каждый раз, когда вы выбираете ход при планировании следующего хода.


Благодаря nponeccop и Tener для ссылки на векторные модули. Следующий код эквивалентен моему первоначальному примеру, но работает за 0,06 секунды вместо 10.

import qualified Data.Vector.Unboxed.Mutable as V

fast_vector :: IO (V.IOVector Int)
fast_vector = do
  vec <- V.new 10000
  V.set vec 0
  mapM_ (\i -> V.write vec i i) [0..9999]
  return vec

fv_read :: IO Int
fv_read  = do
  v <- fast_vector
  V.read v 9999

Теперь, чтобы включить это в мой код Ants...

Ответ 1

Прежде всего, подумайте, можете ли вы улучшить свой алгоритм. Также обратите внимание, что значение по умолчанию Ants.hs не является оптимальным, и вам нужно перевернуть его.

Во-вторых, вы должны использовать профайлер, чтобы найти, где проблема с производительностью, вместо того, чтобы полагаться на размахивание руками. Код Haskell, как правило, намного быстрее, чем Python (в 10-30 раз быстрее, вы можете посмотреть Language Shootout, например сравнение) даже с функциональными структурами данных, так что, возможно, вы что-то не так.

Haskell поддерживает изменчивые данные довольно хорошо. См. ST (поток состояний) и библиотеки для изменяемых массивов для ST. Также просмотрите пакет векторов. Наконец, вы можете использовать data-parallel haskell, haskell-mpi или другие способы распараллеливания для загрузки всех доступных ядер процессора или даже распространения работы над несколькими компьютеры.

Используете ли вы скомпилированный код (например, cabal build или ghc --make) или используете runhaskell или ghci? Последние являются интерпретаторами байт-кода и создают гораздо более медленный код, чем компилятор нативного кода. См. Ссылка на кабалу - это предпочтительный способ создания приложений.

Также убедитесь, что включена оптимизация (-O2и другие флаги). Обратите внимание, что -O vs -O2 может иметь значение и попробовать разные бэкенды, включая новый LLVM-сервер (-fllvm).

Ответ 2

Обновление массивов по одному элементу одновременно невероятно неэффективно, потому что каждое обновление включает в себя создание копии всего массива. Другие структуры данных, такие как Map, реализуются как деревья и, таким образом, позволяют логарифмически обновлять время. Тем не менее, при общем обновлении функциональных структур данных один элемент за раз часто оказывается неоптимальным, поэтому вы должны попытаться сделать шаг назад и подумать о том, как вы можете реализовать что-то как преобразование всей структуры сразу, а не только одного элемента в то время.

Например, пример slow_array можно записать гораздо более эффективно, выполнив все обновления за один шаг, для чего требуется только одно копирование массива.

faster_array =
    let arr = listArray (0,9999) (repeat 0)
    in  arr // [(i,i) | i <- [0..9999]]

Если вы не можете придумать альтернативу императивному алгоритму с одним элементом по времени, измененные структуры данных упоминаются как еще один вариант.

Ответ 3

В основном вы запрашиваете изменяемую структуру данных. Помимо стандартных библиотек я бы рекомендовал вам найти это:

Тем не менее, я не уверен, что они вам нужны. Существуют и аккуратные алгоритмы для постоянных структур данных. Быстрая замена Data.Map - это хеш-таблица из этого пакета: