Обход потока памяти с эффективным использованием памяти с использованием класса типов скалаза Traverse
Я пытаюсь пройти / упорядочить большой поток (например, scala.collection.immutable.Stream
) используя Scalaz' (версия 7.1.2) Traverse
класс, но я постоянно сталкиваюсь с java.lang.OutOfMemoryError: GC overhead limit exceeded
вопрос.
Мой обход выглядит примерно так:
import scalaz._
import Scalaz._
import scala.collection.immutable.Stream
Stream.range(1, 1000000000).traverse[MTrans, Int](f)
где MTrans
это монадный трансформатор, включающий EitherT
а также StateT
а также f: Int => MTrans[Int]
,
Я вообще просто заинтересован в последовательности элементов (передача состояния) и нужен только конечный результат (MTrans[Int]
), а не вся (материализованная) последовательность / поток.
У меня есть версии с traverseKTrampoline
но это не помогает, так как это не StackruError
выпускать как описано в других подобных постах. Я также пробовал сочетания использования EphemeralStream
а также sequence
но не имели успеха.
Как я могу (память-) эффективно пройти / упорядочить такой поток?
Обновление 1
Ниже приведен более полный пример того, что я пытаюсь сделать. Это близко напоминает структуру, которая у меня есть, и демонстрирует ту же проблему (предел нагрузки GC в какой-то момент превышает).
object Main {
import scalaz._
import Scalaz._
import Kleisli._
import scala.collection.immutable.Stream
import scala.language.higherKinds
case class IState(s: Int)
type IStateT[A] = StateT[Id, IState, A]
type MTransT[S[_], A] = EitherT[S, String, A]
type MTrans[A] = MTransT[IStateT, A]
def eval(k: Int): MTrans[Int] = {
for {
state <- get[IState].liftM[MTransT]
_ <- put(state.copy(s = state.s % k)).liftM[MTransT]
} yield (k + 1)
}
def process(i: Int, k: Int): MTrans[Int] = {
for {
state <- get[IState].liftM[MTransT]
_ <- put(state.copy(s = state.s + i)).liftM[MTransT]
res <- eval(k)
} yield res
}
def run() = {
val m = Stream
.range(1, 1000000000)
.traverseKTrampoline[MTrans, Int, Int](i => kleisli(process(i, _))).run(7)
m.run(IState(0))
}
}
Обновление 2
На основании некоторого вклада Эрика и от Applicative против монадических комбинаторов и свободной монады в Scalaz, я придумал следующее простое foldLeft
решение на основе аппликативного *>
:
val m = Stream
.range(1, 1000000000)
.toEphemeralStream
.foldLeft(0.point[MTrans]) { acc => i =>
acc *> process(i, 3)
}
Хотя это (все еще) кажется безопасным для стека, оно требует большого количества кучи и работает очень медленно.