Parallel processing 并行运行Akka流阶段会显著增加内存压力

Parallel processing 并行运行Akka流阶段会显著增加内存压力,parallel-processing,akka,akka-stream,memory-pressure,akka-actor,Parallel Processing,Akka,Akka Stream,Memory Pressure,Akka Actor,我正在尝试实现一个Akka流,它从视频文件中读取帧,并应用SVM分类器来检测每一帧上的对象。检测可以并行运行,因为视频帧的顺序无关紧要。我的想法是创建一个遵循Akka Streams Cookbook()的图形,其中有两个检测阶段标记为.async 它的工作在一定程度上符合预期,但我注意到我的系统的内存压力(只有8GB可用)急剧增加,并明显降低了系统的速度。与使用.mapsync()将三个参与者集成到执行对象检测的流中的不同方法相比,内存压力显著降低 我错过了什么?为什么并行运行两个阶段会增加记

我正在尝试实现一个Akka流,它从视频文件中读取帧,并应用SVM分类器来检测每一帧上的对象。检测可以并行运行,因为视频帧的顺序无关紧要。我的想法是创建一个遵循Akka Streams Cookbook()的图形,其中有两个检测阶段标记为
.async

它的工作在一定程度上符合预期,但我注意到我的系统的内存压力(只有8GB可用)急剧增加,并明显降低了系统的速度。与使用
.mapsync
()将三个参与者集成到执行对象检测的流中的不同方法相比,内存压力显著降低

我错过了什么?为什么并行运行两个阶段会增加记忆压力,而三个并行运行的参与者似乎工作正常

附加说明:我正在使用OpenCV读取视频文件。由于分辨率为4K,因此
Mat
类型的每个视频帧约为26.5 MB

.async并行运行两个阶段
显著增加内存压力

implicit val materializer = ActorMaterializer(
  ActorMaterializerSettings(actorSystem)
    .withInputBuffer(initialSize = 1, maxSize = 1)
    .withOutputBurstLimit(1)
    .withSyncProcessingLimit(2)
  )

val greyscaleConversion: Flow[Frame, Frame, NotUsed] =
  Flow[Frame].map { el => Frame(el.videoPos, FrameTransformation.transformToGreyscale(el.frame)) }

val objectDetection: Flow[Frame, DetectedObjectPos, NotUsed] =
  Flow.fromGraph(GraphDSL.create() { implicit builder =>
    import GraphDSL.Implicits._

    val numberOfDetectors = 2
    val frameBalance: UniformFanOutShape[Frame, Frame] = builder.add(Balance[Frame](numberOfDetectors, waitForAllDownstreams = true))
    val detectionMerge: UniformFanInShape[DetectedObjectPos, DetectedObjectPos] = builder.add(Merge[DetectedObjectPos](numberOfDetectors))

    for (i <- 0 until numberOfDetectors) {
      val detectionFlow: Flow[Frame, DetectedObjectPos, NotUsed] = Flow[Frame].map { greyFrame =>
        val classifier = new CascadeClassifier()
        classifier.load("classifier.xml")
        val detectedObjects: MatOfRect = new MatOfRect()
        classifier.detectMultiScale(greyFrame.frame, detectedObjects, 1.08, 5, 0 | Objdetect.CASCADE_SCALE_IMAGE, new Size(40, 20), new Size(100, 80))
        DetectedObjectPos(greyFrame.videoPos, detectedObjects)
      }

      frameBalance.out(i) ~> detectionFlow.async ~> detectionMerge.in(i)
    }

    FlowShape(frameBalance.in, detectionMerge.out)
  })

def createGraph(videoFile: Video): RunnableGraph[NotUsed] = {
  Source.fromGraph(new VideoSource(videoFile))
    .via(greyscaleConversion).async
    .via(objectDetection)
    .to(Sink.foreach(detectionDisplayActor !))
}
val greyscaleConversion: Flow[Frame, Frame, NotUsed] =
  Flow[Frame].map { el => Frame(el.videoPos, FrameTransformation.transformToGreyscale(el.frame)) }

val detectionRouter: ActorRef =
  actorSystem.actorOf(RandomPool(numberOfDetectors).props(Props[DetectionActor]), "detectionRouter")

val detectionFlow: Flow[Frame, DetectedObjectPos, NotUsed] =
  Flow[Frame].mapAsyncUnordered(parallelism = 3)(el => (detectionRouter ? el).mapTo[DetectedObjectPos])

def createGraph(videoFile: Video): RunnableGraph[NotUsed] = {
  Source.fromGraph(new VideoSource(videoFile))
    .via(greyscaleConversion)
    .via(detectionFlow)
    .to(Sink.foreach(detectionDisplayActor !))
}