Распараллеливание поэлементного умножения двух матриц в F#
-
24-10-2019 - |
Вопрос
Я пытаюсь распараллелить поэлементное умножение двух матриц в F#.Я не совсем могу в этом разобраться.Я продолжаю пытаться создавать задачи, но они никогда не хотят компилироваться.Мой нерабочий грязный код следующий:
let myBigElemMultiply (m:matrix) (n:matrix) =
let AddTwoRows (row:int) (destination:matrix) (source1:matrix) (source2:matrix) =
for i in 0 .. destination.NumCols
destination.[row, i] <- source1.[row,i] + source2.[row,i]
destination
let result = Matrix.zero(m.NumRows)
let operations = [ for i in 0 .. m.NumRows -> AddTwoRows i result m n ]
let parallelTasks = Async.Parallel operations
Async.RunSynchronously parallelTasks
result
Решение
Вы допустили несколько небольших ошибок, например, вы не поняли, как делать умножение матрицы.
let myBigElemMultiply (m:matrix) (n:matrix) =
let AddTwoRows (row:int) (destination:matrix) (source1:matrix) (source2:matrix) =
for col=0 to destination.NumCols-1 do
let mutable sum = 0.0
for k=0 to m.NumCols-1 do
sum <- sum + source1.[row,k] * source2.[k,col]
destination.[row,col] <- sum
let result = Matrix.zero m.NumRows n.NumCols
let operations = [ for i=0 to m.NumRows-1 do yield async { AddTwoRows i result m n} ]
let parallelTasks = Async.Parallel operations
Async.RunSynchronously parallelTasks |> ignore
result
Одна вещь, которую нужно заметить, это то, что этот код будет работать очень плохо, потому что m.[i,j]
является неэффективным способом доступа к элементам в матрице. Вам лучше использовать 2D -массив:
let myBigElemMultiply2 (m:matrix) (n:matrix) =
let AddTwoRows (row:int) (destination:matrix) (source1:matrix) (source2:matrix) =
let destination = destination.InternalDenseValues
let source1 = source1.InternalDenseValues
let source2 = source2.InternalDenseValues
for col=0 to Array2D.length2 destination - 1 do
let mutable sum = 0.0
for k=0 to Array2D.length1 source2 - 1 do
sum <- sum + source1.[row,k] * source2.[k,col]
destination.[row,col] <- sum
let result = Matrix.zero m.NumRows n.NumCols
let operations = [ for i=0 to m.NumRows-1 do yield async { AddTwoRows i result m n} ]
let parallelTasks = Async.Parallel operations
Async.RunSynchronously parallelTasks |> ignore
result
Тестирование:
let r = new Random()
let A = Matrix.init 280 10340 (fun i j -> r.NextDouble() )
let B = A.Transpose
Некоторое время:
> myBigElemMultiply A B;;
Real: 00:00:22.111, CPU: 00:00:41.777, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
> myBigElemMultiply2 A B;;
Real: 00:00:08.736, CPU: 00:00:15.303, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
> A*B;;
Real: 00:00:13.635, CPU: 00:00:13.166, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
>
Проверять здесь Используя паралльфор, который должен иметь лучшую производительность, чем асинхронность.
Другие советы
Вот, по крайней мере, какой -то код, который компилирует, возможно, это заставит вас направиться в правильном направлении?
let myBigElemMultiply (m:matrix) (n:matrix) =
let AddTwoRows (row:int) (destination:matrix) (source1:matrix) (source2:matrix) =
async {
for i in 0 .. destination.NumCols do
destination.[row, i] <- source1.[row,i] + source2.[row,i]
}
let result = Matrix.zero m.NumRows m.NumCols
let operations = [ for i in 0 .. m.NumRows -> AddTwoRows i result m n ]
let parallelTasks = Async.Parallel operations
Async.RunSynchronously parallelTasks |> ignore
result
Нет никакого смысла.Неуместное поэлементное умножение пары матриц — это не что иное, как копирование, при котором одно ядро с радостью максимально исчерпает всю пропускную способность памяти вашей машины, а добавление большего количества ядер не улучшит производительность.Так что это почти наверняка пустая трата времени.