Scala параллельная сортировка с использованием java.util.Arrays и scala.concurrent.ops.par

2

Я думаю, что я неправильно выполнил часть своего кода. Я не могу понять, почему мой сорт (используя array.sort) занимает больше времени в "параллельной" версии, чем в непараллельной версии (очевидно, что объединение двух массивов обратно вместе, но я не думал, что это добавит, что гораздо больше времени). Если кто-то может указать на любые ошибки, которые я делаю, или на какие-либо советы по улучшению параллельной версии по непараллельной версии, я был бы признателен. Можно ли объединить массив более эффективно или, возможно, даже параллельно? Если да, то какова наилучшая практика для реализации. Любая помощь будет принята с благодарностью.

import java.util.Arrays
import scala.concurrent._
import scala.collection._

trait Sorts {
  def doSort(a: Array[Double]): Array[Double]
}

object Simple extends Sorts {
  def doSort(a: Array[Double]) = {
    Arrays.sort(a)
    a
  }
}

object Parallel extends Sorts {
  def doSort(a: Array[Double]) = {
    val newArray = new Array[Double](a.length)
    val aLength = (a.length)
    val aSplit = ((a.length / 2).floor).toInt
    ops.par(Arrays.sort(a, 0, aSplit), Arrays.sort(a, (aSplit + 1), aLength))
    def merge(w: Int, x: Int, y: Int) {
      var i = w
      var j = x
      var k = y
      while (i <= aSplit && j <= aLength) {
        if (a(i) <= a(j)) {
          newArray(k) = a(i)
          i = i + 1
        } else {
          newArray(k) = a(j)
          j = j + 1
        }
        k = k + 1
      }
      if (i < aSplit) {
        for (i <- i until aSplit) {
          newArray(k) = a(i)
          k = k + 1
        }
      } else {
        for (j <- j until aLength) {
          newArray(k) = a(j)
          k = k + 1
        }
      }
    }
    merge(0, (aSplit + 1), 0)
    newArray
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val simpleNumbers = Array.fill(10000)(math.random)
    println(simpleNumbers.toList + "\n")
    val simpleStart = System.nanoTime()
    Simple.doSort(simpleNumbers)
    val simpleEnd = System.nanoTime()
    println(simpleNumbers.toList + "\n")
    val simpleDifference = ((simpleEnd - simpleStart) / 1e9).toDouble

    val parallelNumbers = Array.fill(10000)(math.random)
    println(parallelNumbers.toList + "\n")
    val parallelStart = System.nanoTime()
    Parallel.doSort(parallelNumbers)
    val parellelEnd = System.nanoTime()
    println(parallelNumbers.toList + "\n")
    val parallelDifference = ((parellelEnd - parallelStart) / 1e9).toDouble

    println("\n Simple Time Taken: " + simpleDifference + "\n")
    println("\n Parallel Time Taken: " + parallelDifference + "\n")

  }
}

Выход на Intel Core i7:

Simple Time Taken: 0.01314
Parallel Time Taken: 0.05882
Теги:
sorting
parallel-processing

1 ответ

7
Лучший ответ

Думаю, у вас здесь есть несколько разных вещей. Во-первых, в моей системе строка ops.par(Arrays.sort(...)) сама по себе занимает больше времени, чем все Simple.doSort(). Таким образом, должны быть некоторые накладные расходы (создание потоков?), Которые доминируют в производительности для небольшого массива. Попробуйте его на 100 000 или миллион элементов. Во-вторых, Arrays.sort - это сортировка по месту, поэтому она не требует затрат на создание нового массива элементов 10k для результатов.

Чтобы избежать создания второго массива, вы можете сначала сделать раздел, а затем отсортировать две половины параллельно, поскольку рекомендуется здесь

def doSort(a: Array[Double]) = {
  val pivot = a(a.length-1)
  var i = 0
  var j = a.length-2
  def swap(i: Int, j: Int) {
    val temp = a(i)
    a(i) = a(j)
    a(j) = temp
  }
  while(i < j-1) {
   if(a(i) <= pivot) {
    i+=1
   }
   else {
    swap(i,j)
    j-=1
   }
  }
  swap(j-1, a.length-1)
  ops.par(Arrays.sort(a,0,a.length/2), Arrays.sort(a,a.length/2+1,a.length))
  a
}

После увеличения размера массива до 100 тыс., я вижу параллельную версию, которая работает примерно вдвое быстрее на процессоре Intel E5300.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню