aPar and aParFor

Note: note that the control constructs described in this section will be likely redesigned in the future.

With vats shown in the previous section you have probably already guessed that by combining aLater(vat) and aAll it is possible to launch activities in the other parallel vats. Yes, it is possible, and some constructs to support it are already in the AsyncControl class. These constructs use spawner as the first argument that has type ASpawner (it specifies single spawn method, like we have discussed in the previous section, actually the objects extends this trait).

  @Test
  def testSimple() {
    doAsync {
      aSeq {
        aPar(ExecutorVat) {
          aLater(Vat.current)
        }.andLast {
          Vat.current
        }
      }.thenLast {
        case (v1, v2) =>
          assertNotSame(Vat.current, v1)
          assertNotSame(Vat.current, v1)
          assertNotSame(v1, v2)
      }
    }
  }

Note that creation of new vat with spawner does not necessary implies creation of new thread. The ExecutorVat schedules its work on the thread pool only when it has messages to dispatch.

Like for aAll operator, there is a loop from for aPar as well.

  @Test
  def testLoop() {
    doAsync {
      expectEquals((11, 55)) {
        aSeq {
          val vats = new HashSet[Vat]
          val vc = Vat.current
          vats.add(vc)
          aParFor(ExecutorVat, 1 to 10) {
            (_, Vat.current)
          }.foldLeft((vats, 0)) {
            case ((vs, s), (i, v)) =>
              assertSame(vc, Vat.current)
              vs += v
              (vs, s + i)
          }
        } thenLast {
          case (vs, s) => (vs.size, s)
        }
      }
    }
  }

Note that folding is done in the original vat that started aParFor only body of the loop is executed usingspawner.

Parallel Merge Sort

You could see how to use aPar and aParFor over fixed thread pool in the merge sort sample in the distribution. However, if you will run it, you will notice, that performance gain from adding new treads is pretty minor. And it takes almost the same time to as non parallel version. This happens because all activities are launched in parallel, and CPU constantly switches from one task to another. This trashes CPU caches. Other reason is that all arrays are allocated at the same stressing the memory.

To make task distribution and load more controlled, there is a special class for it TaskTree described in the next section of the document.