我有一个很长的字符串序列,它们需要由一些处理函数处理,然后作为另一个序列对象收集。这个问题似乎非常适合fork / join类型的攻击。</ p>
该函数是一个实例化成本非常高的类的成员。但是,在期货中实例化和共享单个类对象似乎会导致问题,因此我将实例化处理器数量的4倍实例化,然后将其分解为期货。
// instantiate processing class objects
val processors = 1 to SomeNumber map (x=> new MyProcessor)
val processorstream = Stream.continually(processors).flatten
// the input strings
val input: Seq[String] = some sequence of strings
val splitinput = input.grouped(some large number).toStream
// create futures
val mytask = splitinput.zip(processorstream).collect {
case (subseq of strings, processor) => future {
map elements of subsequence of strings with processor}}
然后我像这样收集输出
val result = mytask.map(x => x.apply()).reduce(_++_) // or some appropriate concatenation operator
我的问题是,即使我有8个核心,这也不能给我全部cpu利用率。它只使用一个核心。
为了调查,我尝试的替代方案是
val input: Seq[String] = some sequence of strings
// no stage where I split the input into subsequences
val mytask = input.zip(processorstream).collect {
case (string, processor) => future {
process string with processor}}
val result = mytask.map(x => x.apply())
这种替代方案既有效又无效。它实现了完全的cpu利用率,但抛出了几个例外,因为(一个假设)处理器运行的每个字符串太快,有时同一个处理器对象会同时应用于不同的字符串。
我更确定我的假设是处理器工作得太快,因为如果我提供更长的输入(比如说全文文档而不是10个字标题),我会获得完整的cpu利用率,而不会抛出任何异常。
我也试过尝试akka期货和scalaz承诺,当我将输入序列分成子序列时,他们似乎只使用一个cpu。
那么在使用字符串的子序列作为输入时,如何在此实例中使用期货获得完整的cpu利用率?
答案 0 :(得分:2)
Per @ om-nom-nom:
input.par.map { s => task(s) }
答案 1 :(得分:0)
您可以尝试将ThreadLocal
用于可变处理器。相当无用的例子:
val words = io.Source.fromFile("/usr/share/dict/words").getLines.toIndexedSeq
class Processor {
val sb = new StringBuffer() // mutable!
println("---created processor---")
def map(s: String): Int = {
sb.setLength(0)
for (i <- 1 to s.length()) {
sb.append(s.substring(0, i))
}
sb.toString().sum.toInt // don't make any sense out of this
}
}
val tl = new ThreadLocal[Processor] {
override protected def initialValue() = new Processor
}
val parRes = words.par.map(w => tl.get.map(w)).sum
val serRes = words.map( w => tl.get.map(w)).sum
assert(parRes == serRes)
这将默认创建与CPU核心一样多的线程,因为---created processor---
消息证明了这一点。