All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.signalcollect.triplerush.CombiningMessageBus.scala Maven / Gradle / Ivy

The newest version!
/*
 *  @author Philip Stutz
 *  @author Mihaela Verman
 *
 *  Copyright 2013 University of Zurich
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package com.signalcollect.triplerush

import com.signalcollect.interfaces.MessageBus
import com.signalcollect.messaging.SignalBulker
import com.signalcollect.interfaces.WorkerApiFactory
import com.signalcollect.messaging.AbstractMessageBus
import scala.reflect.ClassTag
import com.signalcollect.interfaces.VertexToWorkerMapper
import com.signalcollect.messaging.BulkMessageBus
import com.signalcollect.interfaces.MessageBusFactory
import QueryParticle._
import scala.collection.mutable.ArrayBuffer
import com.signalcollect.util.IntLongHashMap
import com.signalcollect.util.IntHashMap
import com.signalcollect.triplerush.EfficientIndexPattern._
import com.signalcollect.util.IntIntHashMap
import akka.actor.ActorSystem
import com.signalcollect.interfaces.BulkSignal
import com.signalcollect.interfaces.BulkSignalNoSourceIds
import com.signalcollect.interfaces.SignalMessageWithoutSourceId
import com.signalcollect.triplerush.util.CompositeLongIntHashMap
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap

class SignalBulkerWithoutIds[@specialized(Long) Id: ClassTag, Signal: ClassTag](size: Int) {
  private[this] var itemCount = 0

  def numberOfItems = itemCount

  def isFull: Boolean = itemCount == size

  final val targetIds = new Array[Id](size)
  final val signals = new Array[Signal](size)

  def addSignal(signal: Signal, targetId: Id): Unit = {
    signals(itemCount) = signal
    targetIds(itemCount) = targetId
    itemCount += 1
  }

  def clear(): Unit = {
    itemCount = 0
  }
}

class ResultBulker(val size: Int) {
  private[this] var itemCount = 0

  def numberOfItems = itemCount

  def isFull: Boolean = itemCount == size

  private[this] final val results = new Array[Array[Int]](size)

  def addResult(result: Array[Int]): Unit = {
    results(itemCount) = result
    itemCount += 1
  }

  def clear(): Unit = {
    itemCount = 0
  }

  def getResultArray: Array[Array[Int]] = {
    val resultsCopy = new Array[Array[Int]](itemCount)
    System.arraycopy(results, 0, resultsCopy, 0, itemCount)
    resultsCopy
  }
}

class CombiningMessageBusFactory[Signal: ClassTag](flushThreshold: Int, resultBulkerSize: Int)
    extends MessageBusFactory[Long, Signal] {
  def createInstance(
    system: ActorSystem,
    numberOfWorkers: Int,
    numberOfNodes: Int,
    mapper: VertexToWorkerMapper[Long],
    sendCountIncrementorForRequests: MessageBus[_, _] => Unit,
    workerApiFactory: WorkerApiFactory[Long, Signal]): MessageBus[Long, Signal] = {
    new CombiningMessageBus[Signal](
      system,
      numberOfWorkers,
      numberOfNodes,
      mapper.asInstanceOf[VertexToWorkerMapper[Long]],
      flushThreshold,
      resultBulkerSize,
      sendCountIncrementorForRequests: MessageBus[_, _] => Unit,
      workerApiFactory)
  }

  override def toString = "CombiningMessageBusFactory"
}

/**
 * Version of bulk message bus that combines tickets of failed queries.
 */
final class CombiningMessageBus[Signal: ClassTag](
  val system: ActorSystem,
  val numberOfWorkers: Int,
  val numberOfNodes: Int,
  val mapper: VertexToWorkerMapper[Long],
  val flushThreshold: Int,
  val resultBulkerSize: Int,
  val sendCountIncrementorForRequests: MessageBus[_, _] => Unit,
  val workerApiFactory: WorkerApiFactory[Long, Signal])
    extends AbstractMessageBus[Long, Signal] {

  lazy val workerApi = workerApiFactory.createInstance(workerProxies, mapper)

  val aggregatedTickets = new IntLongHashMap(initialSize = 8)
  val aggregatedResults = new Int2ObjectOpenHashMap[ResultBulker](8)
  val aggregatedCardinalities = new CompositeLongIntHashMap(initialSize = 8)
  val aggregatedResultCounts = new IntIntHashMap(initialSize = 8)

  override def sendSignal(signal: Signal, targetId: Long): Unit = {
    // If message is sent to a Query Vertex
    if (targetId.isOperationId) {
      val extractedOperationId = OperationIds.extractFromLong(targetId)
      signal match {
        case resultCount: Int =>
          val oldResultCount = aggregatedResultCounts(extractedOperationId)
          aggregatedResultCounts(extractedOperationId) = oldResultCount + resultCount
        case tickets: Long =>
          handleTickets(tickets, extractedOperationId)
        case result: Array[Int] =>
          handleTickets(result.tickets, extractedOperationId)
          val bindings = result.bindings
          val oldResults = aggregatedResults.get(extractedOperationId)
          if (oldResults != null) {
            oldResults.addResult(bindings)
            if (oldResults.isFull) {
              val targetId = OperationIds.embedInLong(extractedOperationId)
              sendToWorkerForVertexId(SignalMessageWithoutSourceId(targetId, oldResults.getResultArray), targetId)
              aggregatedResults.remove(extractedOperationId)
            }
          } else {
            val newBulker = new ResultBulker(resultBulkerSize)
            newBulker.addResult(bindings)
            aggregatedResults.put(extractedOperationId, newBulker)
          }
        case other @ _ =>
          bulkSend(signal, targetId)
      }
    } // If message is sent to an Index Vertex
    else if (signal.isInstanceOf[Int]) {
      val t = targetId.asInstanceOf[Long]
      val oldCardinalities = aggregatedCardinalities(t)
      aggregatedCardinalities(t) = oldCardinalities + signal.asInstanceOf[Int]
    } else {
      bulkSend(signal, targetId)
    }
  }

  override def sendSignal(signal: Signal, targetId: Long, sourceId: Option[Long], blocking: Boolean = false): Unit = {
    throw new Exception(s"Non-optimized messaging for TripleRush, this should never be called: signal=$signal targetId=$targetId")
  }

  def bulkSend(
    signal: Signal,
    targetId: Long): Unit = {
    val workerId = mapper.getWorkerIdForVertexId(targetId)
    val bulker = outgoingMessages(workerId)
    bulker.addSignal(signal, targetId)
    pendingSignals += 1
    if (bulker.isFull) {
      pendingSignals -= bulker.numberOfItems
      // It seems that for small arrays arraycopy is faster than clone:
      // http://www.javaspecialists.co.za/archive/Issue124.html
      val length = bulker.signals.length
      val signalsClone = new Array[Signal](length)
      System.arraycopy(bulker.signals, 0, signalsClone, 0, length)
      val targetIdsClone = new Array[Long](length)
      System.arraycopy(bulker.targetIds, 0, targetIdsClone, 0, length)
      sendToWorker(workerId, BulkSignalNoSourceIds[Long, Signal](signalsClone, targetIdsClone))
      bulker.clear
    }
  }

  private[this] def handleTickets(tickets: Long, operationId: Int): Unit = {
    val oldTickets = aggregatedTickets(operationId)
    if (oldTickets < 0) {
      if (tickets < 0) {
        aggregatedTickets(operationId) = oldTickets + tickets
      } else {
        aggregatedTickets(operationId) = oldTickets - tickets
      }
    } else {
      if (tickets < 0) {
        aggregatedTickets(operationId) = tickets - oldTickets
      } else {
        aggregatedTickets(operationId) = tickets + oldTickets
      }
    }
  }

  override def flush(): Unit = {
    // It is important that the results arrive before the tickets, because
    // result tickets were separated from their respective results.
    if (!aggregatedResults.isEmpty) {
      val i = aggregatedResults.keySet().iterator()
      while (i.hasNext()) {
        val queryVertexId = i.nextInt()
        val results = aggregatedResults.get(queryVertexId)
        val targetId = OperationIds.embedInLong(queryVertexId)
        sendToWorkerForVertexId(SignalMessageWithoutSourceId(targetId, results.getResultArray), targetId)
      }
      aggregatedResults.clear()
    }
    if (!aggregatedResultCounts.isEmpty) {
      aggregatedResultCounts.process { (queryVertexId, resultCount) =>
        val targetId = OperationIds.embedInLong(queryVertexId)
        sendToWorkerForVertexId(SignalMessageWithoutSourceId(targetId, resultCount), targetId)
      }
    }
    if (!aggregatedTickets.isEmpty) {
      aggregatedTickets.process { (queryVertexId, tickets) =>
        val targetId = OperationIds.embedInLong(queryVertexId)
        sendToWorkerForVertexId(SignalMessageWithoutSourceId(targetId, tickets), targetId)
      }
    }
    if (!aggregatedCardinalities.isEmpty) {
      aggregatedCardinalities.process { (targetId, cardinalityIncrement) =>
        sendToWorkerForVertexId(SignalMessageWithoutSourceId(targetId, cardinalityIncrement), targetId)
      }
    }
    if (pendingSignals > 0) {
      var workerId = 0
      while (workerId < numberOfWorkers) {
        val bulker = outgoingMessages(workerId)
        val signalCount = bulker.numberOfItems
        if (signalCount > 0) {
          val signalsCopy = new Array[Signal](signalCount)
          System.arraycopy(bulker.signals, 0, signalsCopy, 0, signalCount)
          val targetIdsCopy = new Array[Long](signalCount)
          System.arraycopy(bulker.targetIds, 0, targetIdsCopy, 0, signalCount)
          sendToWorker(workerId, BulkSignalNoSourceIds[Long, Signal](
            signalsCopy,
            targetIdsCopy))
          outgoingMessages(workerId).clear
        }
        workerId += 1
      }
      pendingSignals = 0
    }
  }

  override def reset(): Unit = {
    super.reset
    pendingSignals = 0
    var i = 0
    val bulkers = outgoingMessages.length
    while (i < bulkers) {
      outgoingMessages(i).clear
      i += 1
    }
  }

  protected var pendingSignals = 0

  val outgoingMessages: Array[SignalBulkerWithoutIds[Long, Signal]] = new Array[SignalBulkerWithoutIds[Long, Signal]](numberOfWorkers)
  for { i <- 0 until numberOfWorkers } {
    outgoingMessages(i) = new SignalBulkerWithoutIds[Long, Signal](flushThreshold)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy