Я думаю, что я неправильно выполнил часть своего кода. Я не могу понять, почему мой сорт (используя 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
Думаю, у вас здесь есть несколько разных вещей. Во-первых, в моей системе строка 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.