In my last post I was writing about parallelizing loops with Parallel.For in C#. Today I though it would be nice to try that in F#. So, here is the benchmarking of the matrix multiplication and the bubblesort algorithm in F4.
Matrices Multiplication
I started with a create_matrix function that creates and randomly initializes a matrix of doubles.
let create_matrix rows columns = let rnd = System.Random() Array2.init rows columns (fun i j -> rnd.NextDouble())
Sequential multiplication could look like this:
let multiply_sequential (m1:float[,]) (m2:float[,]) =
let rows1 = Array2.length1 m1
let cols1 = Array2.length2 m1
let rows2 = Array2.length1 m2
let cols2 = Array2.length2 m2
let result = Array2.create rows1 cols2 0.0
if(cols1 <> rows2) then
failwith "Matrices size incorrect for multiplication!"
for i = 0 to rows1-1 do
for j = 0 to cols2-1 do
for k = 0 to cols1-1 do
result.[i,j] <- result.[i,j] + m1.[i,k] * m2.[k,j]
done
done
done
result
Parallelizing it only implies replacing the outer loop with Parallel.For.
let multiply_parallel (m1:float[,]) (m2:float[,]) =
let rows1 = Array2.length1 m1
let cols1 = Array2.length2 m1
let rows2 = Array2.length1 m2
let cols2 = Array2.length2 m2
let result = Array2.create rows1 cols2 0.0
if(cols1 <> rows2) then
failwith "Matrices size incorrect for multiplication!"
Parallel.For(0, rows1, (fun i->
for j = 0 to cols2-1 do
for k = 0 to cols1-1 do
result.[i,j] <- result.[i,j] + m1.[i,k] * m2.[k,j]))
result
We can test those function and get the same output as I had in my previous post with this:
let main() =
let step = 100
let size = ref step
while (!size <= step*10) do
let m1 = create_matrix !size !size
let m2 = create_matrix !size !size
printfn "Matrices size: %dx%d" !size !size
printf "Sequential...\t"
let starts = DateTime.Now
let ms = multiply_sequential m1 m2
printfn "%a" output_any (DateTime.Now - starts)
printf "Parallel...\t"
let startp = DateTime.Now
let ms = multiply_parallel m1 m2
printfn "%a" output_any (DateTime.Now - startp)
size := !size + step
done
main()
Before running, you have to make sure you add System.Threading.dll to the referred assemblies. And since this one depends on System.Core.dll, you also have to add this one. In you are using Visual Studio and a F# project, you can add the two references from the project properties.
-r c:\Windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089\System.Core.dll -r "C:\Program Files\Microsoft Parallel Extensions Dec07 CTP\System.Threading.dll"
The results are shown below:
Matrices size: 100x100 Sequential... 00:00:00.1250000 Parallel... 00:00:00.1250000 Matrices size: 200x200 Sequential... 00:00:00.9218750 Parallel... 00:00:00.6093750 Matrices size: 300x300 Sequential... 00:00:03.1093750 Parallel... 00:00:01.9375000 Matrices size: 400x400 Sequential... 00:00:07.5000000 Parallel... 00:00:04.7343750 Matrices size: 500x500 Sequential... 00:00:15.1562500 Parallel... 00:00:09.3125000 Matrices size: 600x600 Sequential... 00:00:25.7031250 Parallel... 00:00:16.5312500 Matrices size: 700x700 Sequential... 00:00:41.9843750 Parallel... 00:00:26.4375000 Matrices size: 800x800 Sequential... 00:01:03.5781250 Parallel... 00:00:39.8281250 Matrices size: 900x900 Sequential... 00:01:32.1093750 Parallel... 00:00:57.3125000 Matrices size: 1000x1000 Sequential... 00:02:07.0468750 Parallel... 00:01:18.9687500
Array Sorting
First, I created a function, create_array, that creates and randomly initializes an array of doubles.
let create_array size =
let rnd = new Random()
let arr = Array.create size 0.0
for i = 0 to arr.Length-1 do
arr.(i) <- rnd.NextDouble()
arr
The sequential bubblesort implementation is quite straight forward, of course.
let bubblesort_seq (arr : double array) =
for i = 0 to arr.Length-1 do
for j = 0 to arr.Length-1 do
if (arr.(i).CompareTo(arr.(j)) < 0) then
let temp = arr.(j)
arr.(j) <- arr.(i)
arr.(i) <- temp
arr
Parallelizing it, again, only means replacing the outer for loop with Parallel.For.
let bubblesort_parallel (arr : double array) =
Parallel.For(0, arr.Length, (fun i ->
for j = 0 to arr.Length-1 do
if (arr.(i).CompareTo(arr.(j)) < 0) then
let temp = arr.(j)
arr.(j) <- arr.(i)
arr.(i) <- temp))
arr
And this is how the two functions were used:
let main()=
let step = 5000
let size = ref step
while (!size <= step*10) do
let arr = create_array !size
printfn "Array size: %d" arr.Length
printf "Sequential...\t"
let starts = DateTime.Now
let arrs = bubblesort_seq arr
printfn "%a" output_any (DateTime.Now - starts)
printf "Parallel...\t"
let startp = DateTime.Now
let arrp = bubblesort_parallel arr
printfn "%a" output_any (DateTime.Now - startp)
size := !size + step
done
main()
The output for the program is:
Array size: 5000 Sequential... 00:00:00.2343750 Parallel... 00:00:00.1562500 Array size: 10000 Sequential... 00:00:00.8593750 Parallel... 00:00:00.5156250 Array size: 15000 Sequential... 00:00:01.9531250 Parallel... 00:00:01.1718750 Array size: 20000 Sequential... 00:00:03.3125000 Parallel... 00:00:02.1562500 Array size: 25000 Sequential... 00:00:05.4062500 Parallel... 00:00:03.5312500 Array size: 30000 Sequential... 00:00:07.4062500 Parallel... 00:00:05.0312500 Array size: 35000 Sequential... 00:00:10.6562500 Parallel... 00:00:06.8906250 Array size: 40000 Sequential... 00:00:13.2343750 Parallel... 00:00:08.9375000 Array size: 45000 Sequential... 00:00:17.6406250 Parallel... 00:00:11.4687500 Array size: 50000 Sequential... 00:00:20.8281250 Parallel... 00:00:14.2187500
If you compare the output with the one from C#, you'll notice that the times are smaller. It looks like F# is faster than C#. Of course it can get faster if I replace the call to CompareTo() with operator <.
if (arr.(i).CompareTo(arr.(j)) < 0) then
if (arr.(i) < arr.(j)) then
In this case the results look like this:
Array size: 5000 Sequential... 00:00:00.1093750 Parallel... 00:00:00.1093750 Array size: 10000 Sequential... 00:00:00.4843750 Parallel... 00:00:00.2343750 Array size: 15000 Sequential... 00:00:01.1093750 Parallel... 00:00:00.4687500 Array size: 20000 Sequential... 00:00:01.9062500 Parallel... 00:00:00.8437500 Array size: 25000 Sequential... 00:00:03.0156250 Parallel... 00:00:01.2500000 Array size: 30000 Sequential... 00:00:04.3437500 Parallel... 00:00:01.8906250 Array size: 35000 Sequential... 00:00:05.9062500 Parallel... 00:00:02.4375000 Array size: 40000 Sequential... 00:00:07.7656250 Parallel... 00:00:03.3593750 Array size: 45000 Sequential... 00:00:09.8281250 Parallel... 00:00:04.0312500 Array size: 50000 Sequential... 00:00:12.2031250 Parallel... 00:00:05.2343750


















