Haskell-простой способ кэширования вызова функции

у меня есть такие функции, как:

millionsOfCombinations = [[a, b, c, d] | 
  a <- filter (...some filter...) someListOfAs, 
  b <- (...some other filter...) someListOfBs, 
  c <- someListOfCs, d <- someListOfDs]

aLotOfCombinationsOfCombinations = [[comb1, comb2, comb3] | 
  comb1 <- millionsOfCombinations, 
  comb2 <- millionsOfCombinations,
  comb3 <- someList,
  ...around 10 function calls to find if
    [comb1, comb2, comb3] is actually useful]

оценка millionsOfCombinations занимает 40 секунд. на очень быстрой рабочей станции. Оценка aLotOfCombinationsOfCombinations!!0 заняло 2 дня : - (

как я могу ускорить этот код? До сих пор у меня было 2 идеи - использовать профайлер. Пробовал использовать myapp +RTS -sstderr после компиляции с GHC, но получите пустой экран и не хотите ждать дней, пока он закончится.

2-я мысль была как-то кэшировать millionsOfCombinations. Правильно ли я понимаю, что для каждого значения в aLotOfCombinationsOfCombinations, millionsOfCombinations получает оценку несколько раз? Если это так, как я могу кэшировать результат? Очевидно, я только начал изучать Хаскелла. Я знаю, что есть способ сделать кэширование вызовов с монадой, но я все еще не понимаю этих вещей.

2 ответов


использовать -fforce-recomp, -O2 и -fllvm флаги

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

Профиль Ваш Код

на -sstderr флаг не совсем профилирования. Когда люди говорят профилирование, они обычно говорят о профилировании кучи или профилировании времени через -prof и -auto-all флаги.

Избегайте Дорогостоящих Примитивов

Если вам нужен весь список в памяти (т. е. он не будет оптимизирован), рассмотрите распакованные векторы. Если Int сделаю вместо Integer, рассмотрим это (но Integer является разумным значением по умолчанию, когда вы не знаете!). Используйте преобразования worker/wrapping в нужное время. Если вы сильно опираетесь на Data.Map, попробуйте использовать Data.HashMap из неупорядоченных контейнеров библиотека. Этот список можно продолжать и продолжать, но поскольку у вас еще нет интуиции о том, куда идет ваше время вычислений, профилирование должно быть первым!


Я думаю, что нет никакой возможности. Обратите внимание, что время для создания списка растет с каждым задействованным списком. Таким образом, вы получите около 10000003 комбинации для проверки, что действительно занимает много времени. Кэширование списка возможно, но вряд ли что-то изменит, так как новые элементы могут быть созданы почти мгновенно. Единственный способ, вероятно, изменить алгоритм.

Если millionsOfCombinations является константой (а не функцией с аргументами), она кэшируется автоматически. В противном случае сделайте его константой, используя предложение where:

aLotOfCombinationsOfCombinations = [[comb1, comb2, comb3] | 
  comb1 <- millionsOfCombinations, 
  comb2 <- millionsOfCombinations,
  comb3 <- someList,
  ...around 10 function calls to find if
    [comb1, comb2, comb3] is actually useful] where

  millionsOfCombinations = makeCombination xyz