From 26ad23bf8d6ebdf6f6298b56db9c12c7992bd10d Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 5 Nov 2022 22:38:40 +0300 Subject: [PATCH 1/7] BoolSum now supports dense vectors --- .../Common/StandardOperations.fs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/GraphBLAS-sharp.Backend/Common/StandardOperations.fs b/src/GraphBLAS-sharp.Backend/Common/StandardOperations.fs index fac07c0c..68c25e50 100644 --- a/src/GraphBLAS-sharp.Backend/Common/StandardOperations.fs +++ b/src/GraphBLAS-sharp.Backend/Common/StandardOperations.fs @@ -50,7 +50,16 @@ module StandardOperations = if res = zero then None else Some res @> let boolSum = - <@ fun (_: bool option) (_: bool option) -> Some true @> + <@ fun (x: bool option) (y: bool option) -> + let mutable res = false + + match x, y with + | Some f, Some s -> res <- f || s + | Some f, None -> res <- f + | None, Some s -> res <- s + | _ -> () + + if res then Some res else None @> let intSum = mkNumericSum 0 let byteSum = mkNumericSum 0uy From 0ea4d0f1012d2b0d92fd9d8a0fc680d67023c86e Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 5 Nov 2022 22:38:52 +0300 Subject: [PATCH 2/7] SpMV --- src/GraphBLAS-sharp.Backend/Common/Utils.fs | 4 + .../GraphBLAS-sharp.Backend.fsproj | 4 +- src/GraphBLAS-sharp.Backend/Vector/SpMV.fs | 154 ++++++++++++++++++ 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 src/GraphBLAS-sharp.Backend/Vector/SpMV.fs diff --git a/src/GraphBLAS-sharp.Backend/Common/Utils.fs b/src/GraphBLAS-sharp.Backend/Common/Utils.fs index 5efc0a98..809889a9 100644 --- a/src/GraphBLAS-sharp.Backend/Common/Utils.fs +++ b/src/GraphBLAS-sharp.Backend/Common/Utils.fs @@ -21,3 +21,7 @@ module internal Utils = >> fun x -> x ||| (x >>> 8) >> fun x -> x ||| (x >>> 16) >> fun x -> x + 1 + + let floorToMultiple multiple x = x / multiple * multiple + + let ceilToMultiple multiple x = ((x - 1) / multiple + 1) * multiple diff --git a/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj b/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj index b4340f6a..958454bb 100644 --- a/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj +++ b/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj @@ -1,4 +1,4 @@ - + @@ -28,7 +28,7 @@ - + diff --git a/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs b/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs new file mode 100644 index 00000000..223be475 --- /dev/null +++ b/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs @@ -0,0 +1,154 @@ +namespace GraphBLAS.FSharp.Backend + +open Brahma.FSharp +open GraphBLAS.FSharp.Backend +open GraphBLAS.FSharp.Backend.ArraysExtensions +open GraphBLAS.FSharp.Backend.Common +open Microsoft.FSharp.Quotations + +module Vector = + let spMV + (clContext: ClContext) + (add: Expr<'c option -> 'c option -> 'c option>) + (mul: Expr<'a option -> 'b option -> 'c option>) + workGroupSize + = + //Until LocalMemSize added to ClDevice as member + let error = ref Unchecked.defaultof + + let localMemorySize = + Cl + .GetDeviceInfo(clContext.ClDevice.Device, OpenCL.Net.DeviceInfo.LocalMemSize, error) + .CastTo() + + let localArraySize1 = workGroupSize + 1 + + let localMemoryLeft = + localMemorySize - localArraySize1 * sizeof + + let optionTypeClSizeInBytes = + 4 + sizeof<'c> + |> Utils.ceilToMultiple (max sizeof<'c> sizeof) + + let localArraySize2 = + localMemoryLeft / optionTypeClSizeInBytes + |> Utils.floorToMultiple workGroupSize + + let kernel1 = + <@ fun (ndRange: Range1D) matrixLength (matrixColumns: ClArray) (matrixValues: ClArray<'a>) (vectorValues: ClArray<'b option>) (intermediateArray: ClArray<'c option>) -> + + let i = ndRange.GlobalID0 + let value = matrixValues.[i] + let column = matrixColumns.[i] + + if i < matrixLength then + intermediateArray.[i] <- (%mul) (Some value) vectorValues.[column] @> + + let kernel2 = + <@ fun (ndRange: Range1D) (numberOfRows: int) (intermediateArray: ClArray<'c option>) (matrixPtr: ClArray) (outputVector: ClArray<'c option>) -> + + let gid = ndRange.GlobalID0 + let lid = ndRange.LocalID0 + + if gid <= numberOfRows then + let threadsPerBlock = + min (numberOfRows - gid + lid) workGroupSize //If number of rows left is lesser than number of threads in a block + + let localPtr = localArray localArraySize1 + localPtr.[lid] <- matrixPtr.[gid] + + if lid = 0 then + localPtr.[threadsPerBlock] <- matrixPtr.[gid + threadsPerBlock] + + barrierLocal () + + let localValues = localArray<'c option> localArraySize2 + let workEnd = localPtr.[threadsPerBlock] + let mutable blockLowerBound = localPtr.[0] + let numberOfBlocksFitting = localArraySize2 / threadsPerBlock + let workPerIteration = threadsPerBlock * numberOfBlocksFitting + + let mutable sum: 'c option = None + + while blockLowerBound < workEnd do + let mutable index = blockLowerBound + lid + + barrierLocal () + //Loading values to the local memory + for block in 0 .. numberOfBlocksFitting - 1 do + if index < workEnd then + localValues.[lid + block * threadsPerBlock] <- intermediateArray.[index] + index <- index + threadsPerBlock + + barrierLocal () + //Reduction + //Check if any part of the row is loaded into local memory on this iteration + if (localPtr.[lid + 1] > blockLowerBound + && localPtr.[lid] < blockLowerBound + workPerIteration) then + let rowStart = max (localPtr.[lid] - blockLowerBound) 0 + + let rowEnd = + min (localPtr.[lid + 1] - blockLowerBound) workPerIteration + + for jj in rowStart .. rowEnd - 1 do + match (%add) sum localValues.[jj] with + | Some v -> sum <- Some v + | None -> sum <- None + + blockLowerBound <- blockLowerBound + workPerIteration + + if gid < numberOfRows then + outputVector.[gid] <- sum @> + + let kernel1 = clContext.Compile kernel1 + let kernel2 = clContext.Compile kernel2 + + fun (queue: MailboxProcessor<_>) (matrix: CSRMatrix<'a>) (vector: ClArray<'b option>) -> + + let matrixLength = matrix.Values.Length + + let ndRange1 = + Range1D.CreateValid(matrixLength, workGroupSize) + + let ndRange2 = + Range1D.CreateValid(matrix.RowCount, workGroupSize) + + let intermediateArray = + clContext.CreateClArray<'c option>( + matrixLength, + deviceAccessMode = DeviceAccessMode.ReadWrite, + hostAccessMode = HostAccessMode.NotAccessible, + allocationMode = AllocationMode.Default + ) + + let kernel1 = kernel1.GetKernel() + + queue.Post( + Msg.MsgSetArguments + (fun () -> + kernel1.KernelFunc ndRange1 matrixLength matrix.Columns matrix.Values vector intermediateArray) + ) + + queue.Post(Msg.CreateRunMsg<_, _>(kernel1)) + + let outputArray = + clContext.CreateClArray<'c option>( + matrix.RowCount, + deviceAccessMode = DeviceAccessMode.ReadWrite, + hostAccessMode = HostAccessMode.NotAccessible, + allocationMode = AllocationMode.Default + ) + + let kernel2 = kernel2.GetKernel() + + queue.Post( + Msg.MsgSetArguments + (fun () -> + kernel2.KernelFunc ndRange2 matrix.RowCount intermediateArray matrix.RowPointers outputArray) + ) + + queue.Post(Msg.CreateRunMsg<_, _>(kernel2)) + + queue.Post(Msg.CreateFreeMsg intermediateArray) + + outputArray From 3abd3d480588cde0b930118002e1752b0b21d053 Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 5 Nov 2022 22:44:43 +0300 Subject: [PATCH 3/7] SpMV tests --- .../BackendCommonTests/SpMVTests.fs | 137 ++++++++++++++++++ .../GraphBLAS-sharp.Tests.fsproj | 1 + tests/GraphBLAS-sharp.Tests/Program.fs | 1 + 3 files changed, 139 insertions(+) create mode 100644 tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs new file mode 100644 index 00000000..ff16e282 --- /dev/null +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs @@ -0,0 +1,137 @@ +module Backend.SpMV + +open Expecto +open Brahma.FSharp +open GraphBLAS.FSharp.Backend +open GraphBLAS.FSharp.Backend.ArraysExtensions +open GraphBLAS.FSharp +open GraphBLAS.FSharp.Tests.Utils +open Microsoft.FSharp.Collections +open Microsoft.FSharp.Core +open OpenCL.Net +open Backend.Common.StandardOperations + +let checkResult isEqual sumOp mulOp zero (baseMtx: 'a [,]) (baseVtr: 'b []) (actual: 'c array) = + let rows = Array2D.length1 baseMtx + let columns = Array2D.length2 baseMtx + + let expected = Array.create rows zero + + for i in 0 .. rows - 1 do + let mutable sum = zero + + for v in 0 .. columns - 1 do + sum <- sumOp sum (mulOp baseMtx.[i, v] baseVtr.[v]) + + expected.[i] <- sum + + for i in 0 .. actual.Size - 1 do + match actual.[i] with + | Some v -> + if isEqual zero v then + failwith "Resulting zeroes should be implicit." + | None -> () + + for i in 0 .. actual.Size - 1 do + match actual.[i] with + | Some v -> + Expect.isTrue (isEqual v expected.[i]) $"Values should be the same. Actual is {v}, expected {expected.[i]}." + | None -> + Expect.isTrue + (isEqual zero expected.[i]) + $"Values should be the same. Actual is {zero}, expected {expected.[i]}." + +let correctnessGenericTest + zero + sumOp + mulOp + (spMV: MailboxProcessor<_> -> Backend.CSRMatrix<'a> -> ClArray<'b option> -> ClArray<'c option>) + (isEqual: 'a -> 'a -> bool) + q + (testContext: TestContext) + (matrix: 'a [,], vector: 'a [], mask: bool []) + = + + let mtx = + createMatrixFromArray2D CSR matrix (isEqual zero) + + let vtr = + createVectorFromArray Dense vector (isEqual zero) + + if mtx.NNZCount > 0 && vtr.Size > 0 then + try + let m = mtx.ToBackend testContext.ClContext + + match vtr, m with + | VectorDense vtr, Backend.MatrixCSR m -> + let v = vtr.ToDevice testContext.ClContext + + let res = spMV testContext.Queue m v + + (Backend.MatrixCSR m).Dispose q + v.Dispose q + let hostRes = res.ToHost q + res.Dispose q + + checkResult isEqual sumOp mulOp zero matrix vector hostRes + | _ -> failwith "Impossible" + with + | ex when ex.Message = "InvalidBufferSize" -> () + | ex -> raise ex + +let testFixturesSpMV (testContext: TestContext) = + [ let config = defaultConfig + let wgSize = 32 + + let getCorrectnessTestName datatype = sprintf "Correctness on %s" datatype + + let context = testContext.ClContext + let q = testContext.Queue + q.Error.Add(fun e -> failwithf "%A" e) + + let boolSpMV = + Vector.spMV context boolSum boolMul wgSize + + testContext + |> correctnessGenericTest false (||) (&&) boolSpMV (=) q + |> testPropertyWithConfig config (getCorrectnessTestName "bool") + + let intSpMV = Vector.spMV context intSum intMul wgSize + + testContext + |> correctnessGenericTest 0 (+) (*) intSpMV (=) q + |> testPropertyWithConfig config (getCorrectnessTestName "int") + + let floatSpMV = + Vector.spMV context floatSum floatMul wgSize + + testContext + |> correctnessGenericTest 0.0 (+) (*) floatSpMV (fun x y -> abs (x - y) < Accuracy.medium.absolute) q + |> testPropertyWithConfig config (getCorrectnessTestName "float") + + let byteAdd = + Vector.spMV context byteSum byteMul wgSize + + let byteToCOO = Matrix.toCOO context wgSize + + testContext + |> correctnessGenericTest 0uy (+) (*) byteAdd (=) q + |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] + +let tests = + availableContexts "" + |> List.ofSeq + |> List.filter + (fun testContext -> + let mutable e = ErrorCode.Unknown + let device = testContext.ClContext.ClDevice.Device + + let deviceType = + Cl + .GetDeviceInfo(device, DeviceInfo.Type, &e) + .CastTo() + + deviceType = DeviceType.Gpu) + |> List.distinctBy (fun testContext -> testContext.ClContext.ClDevice.DeviceType) + |> List.collect testFixturesSpMV + |> testList "Backend.Common.SpMV tests" diff --git a/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj b/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj index 580ff51c..11e9e133 100644 --- a/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj +++ b/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj @@ -20,6 +20,7 @@ + diff --git a/tests/GraphBLAS-sharp.Tests/Program.fs b/tests/GraphBLAS-sharp.Tests/Program.fs index 6e9523b4..dc91d920 100644 --- a/tests/GraphBLAS-sharp.Tests/Program.fs +++ b/tests/GraphBLAS-sharp.Tests/Program.fs @@ -22,6 +22,7 @@ let allTests = Backend.Elementwise.elementwiseAddAtLeastOneToCOOTests Backend.Elementwise.elementwiseMulAtLeastOneTests Backend.Transpose.tests + Backend.SpMV.tests //Matrix.GetTuples.tests //Matrix.Mxv.tests //Algo.Bfs.tests From ef998aa7d629b42657a06e4de068cc257f168cfb Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 5 Nov 2022 22:50:17 +0300 Subject: [PATCH 4/7] Merge fix --- .../GraphBLAS-sharp.Tests.fsproj | 36 ++----------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj b/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj index 44e2ff8d..bb44482c 100644 --- a/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj +++ b/tests/GraphBLAS-sharp.Tests/GraphBLAS-sharp.Tests.fsproj @@ -1,38 +1,5 @@  - - Exe - net5.0 - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Exe net5.0 @@ -55,6 +22,7 @@ + @@ -62,4 +30,4 @@ - \ No newline at end of file + From 0c4add802f4db14870e61431ed15064a1262efb3 Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 12 Nov 2022 07:38:32 +0300 Subject: [PATCH 5/7] Methods to compute local array size and SpMV vars names --- src/GraphBLAS-sharp.Backend/Common/Utils.fs | 15 +++++ src/GraphBLAS-sharp.Backend/Vector/SpMV.fs | 69 +++++++++++---------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/GraphBLAS-sharp.Backend/Common/Utils.fs b/src/GraphBLAS-sharp.Backend/Common/Utils.fs index 809889a9..490b0d79 100644 --- a/src/GraphBLAS-sharp.Backend/Common/Utils.fs +++ b/src/GraphBLAS-sharp.Backend/Common/Utils.fs @@ -25,3 +25,18 @@ module internal Utils = let floorToMultiple multiple x = x / multiple * multiple let ceilToMultiple multiple x = ((x - 1) / multiple + 1) * multiple + + let getLocalMemorySize (clContext: ClContext) = + let error = ref Unchecked.defaultof + + Cl + .GetDeviceInfo(clContext.ClDevice.Device, OpenCL.Net.DeviceInfo.LocalMemSize, error) + .CastTo() + + let getClArrayOfValueTypeSize<'a when 'a: struct> localMemorySize = localMemorySize / sizeof<'a> + + //Option type in C is represented as structure with additional integer field + let getClArrayOfOptionTypeSize<'a> localMemorySize = + localMemorySize + / (sizeof + sizeof<'a> + |> ceilToMultiple (max sizeof<'a> sizeof)) diff --git a/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs b/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs index 223be475..6c151461 100644 --- a/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs +++ b/src/GraphBLAS-sharp.Backend/Vector/SpMV.fs @@ -1,4 +1,4 @@ -namespace GraphBLAS.FSharp.Backend +namespace GraphBLAS.FSharp.Backend open Brahma.FSharp open GraphBLAS.FSharp.Backend @@ -14,27 +14,18 @@ module Vector = workGroupSize = //Until LocalMemSize added to ClDevice as member - let error = ref Unchecked.defaultof + let localMemorySize = Utils.getLocalMemorySize clContext - let localMemorySize = - Cl - .GetDeviceInfo(clContext.ClDevice.Device, OpenCL.Net.DeviceInfo.LocalMemSize, error) - .CastTo() - - let localArraySize1 = workGroupSize + 1 + let localPointersArraySize = workGroupSize + 1 let localMemoryLeft = - localMemorySize - localArraySize1 * sizeof - - let optionTypeClSizeInBytes = - 4 + sizeof<'c> - |> Utils.ceilToMultiple (max sizeof<'c> sizeof) + localMemorySize + - localPointersArraySize * sizeof - let localArraySize2 = - localMemoryLeft / optionTypeClSizeInBytes - |> Utils.floorToMultiple workGroupSize + let localValuesArraySize = + Utils.getClArrayOfOptionTypeSize localMemoryLeft - let kernel1 = + let multiplyValues = <@ fun (ndRange: Range1D) matrixLength (matrixColumns: ClArray) (matrixValues: ClArray<'a>) (vectorValues: ClArray<'b option>) (intermediateArray: ClArray<'c option>) -> let i = ndRange.GlobalID0 @@ -44,7 +35,7 @@ module Vector = if i < matrixLength then intermediateArray.[i] <- (%mul) (Some value) vectorValues.[column] @> - let kernel2 = + let reduceValuesByRows = <@ fun (ndRange: Range1D) (numberOfRows: int) (intermediateArray: ClArray<'c option>) (matrixPtr: ClArray) (outputVector: ClArray<'c option>) -> let gid = ndRange.GlobalID0 @@ -54,7 +45,7 @@ module Vector = let threadsPerBlock = min (numberOfRows - gid + lid) workGroupSize //If number of rows left is lesser than number of threads in a block - let localPtr = localArray localArraySize1 + let localPtr = localArray localPointersArraySize localPtr.[lid] <- matrixPtr.[gid] if lid = 0 then @@ -62,10 +53,12 @@ module Vector = barrierLocal () - let localValues = localArray<'c option> localArraySize2 + let localValues = + localArray<'c option> localValuesArraySize + let workEnd = localPtr.[threadsPerBlock] let mutable blockLowerBound = localPtr.[0] - let numberOfBlocksFitting = localArraySize2 / threadsPerBlock + let numberOfBlocksFitting = localValuesArraySize / threadsPerBlock let workPerIteration = threadsPerBlock * numberOfBlocksFitting let mutable sum: 'c option = None @@ -90,18 +83,17 @@ module Vector = let rowEnd = min (localPtr.[lid + 1] - blockLowerBound) workPerIteration - for jj in rowStart .. rowEnd - 1 do - match (%add) sum localValues.[jj] with - | Some v -> sum <- Some v - | None -> sum <- None + for j in rowStart .. rowEnd - 1 do + let newSum = (%add) sum localValues.[j] //For some reason sum <- (%add) ... causes Brahma exception + sum <- newSum blockLowerBound <- blockLowerBound + workPerIteration if gid < numberOfRows then outputVector.[gid] <- sum @> - let kernel1 = clContext.Compile kernel1 - let kernel2 = clContext.Compile kernel2 + let multiplyValues = clContext.Compile multiplyValues + let reduceValuesByRows = clContext.Compile reduceValuesByRows fun (queue: MailboxProcessor<_>) (matrix: CSRMatrix<'a>) (vector: ClArray<'b option>) -> @@ -121,15 +113,21 @@ module Vector = allocationMode = AllocationMode.Default ) - let kernel1 = kernel1.GetKernel() + let multiplyValues = multiplyValues.GetKernel() queue.Post( Msg.MsgSetArguments (fun () -> - kernel1.KernelFunc ndRange1 matrixLength matrix.Columns matrix.Values vector intermediateArray) + multiplyValues.KernelFunc + ndRange1 + matrixLength + matrix.Columns + matrix.Values + vector + intermediateArray) ) - queue.Post(Msg.CreateRunMsg<_, _>(kernel1)) + queue.Post(Msg.CreateRunMsg<_, _>(multiplyValues)) let outputArray = clContext.CreateClArray<'c option>( @@ -139,15 +137,20 @@ module Vector = allocationMode = AllocationMode.Default ) - let kernel2 = kernel2.GetKernel() + let reduceValuesByRows = reduceValuesByRows.GetKernel() queue.Post( Msg.MsgSetArguments (fun () -> - kernel2.KernelFunc ndRange2 matrix.RowCount intermediateArray matrix.RowPointers outputArray) + reduceValuesByRows.KernelFunc + ndRange2 + matrix.RowCount + intermediateArray + matrix.RowPointers + outputArray) ) - queue.Post(Msg.CreateRunMsg<_, _>(kernel2)) + queue.Post(Msg.CreateRunMsg<_, _>(reduceValuesByRows)) queue.Post(Msg.CreateFreeMsg intermediateArray) From 03527831fe61d5e6a84466d52b78b8c9f9a6e74e Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 12 Nov 2022 07:39:16 +0300 Subject: [PATCH 6/7] Old SpMV removed --- .../GraphBLAS-sharp.Backend.fsproj | 1 - .../Matrix/CSRMatrix/SpMV.fs | 69 ------------------- 2 files changed, 70 deletions(-) delete mode 100644 src/GraphBLAS-sharp.Backend/Matrix/CSRMatrix/SpMV.fs diff --git a/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj b/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj index 3a4e0a87..8af988cc 100644 --- a/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj +++ b/src/GraphBLAS-sharp.Backend/GraphBLAS-sharp.Backend.fsproj @@ -30,7 +30,6 @@ - diff --git a/src/GraphBLAS-sharp.Backend/Matrix/CSRMatrix/SpMV.fs b/src/GraphBLAS-sharp.Backend/Matrix/CSRMatrix/SpMV.fs deleted file mode 100644 index e8ac951c..00000000 --- a/src/GraphBLAS-sharp.Backend/Matrix/CSRMatrix/SpMV.fs +++ /dev/null @@ -1,69 +0,0 @@ -namespace rec GraphBLAS.FSharp.Backend - -open Brahma.FSharp.OpenCL -open GraphBLAS.FSharp - -// let pcsr (matrix: CSRMatrix<'a>) (vector: BitmapVector<'a>) mask (semiring: ISemiring<'a>) = opencl { -// let (ClosedBinaryOp plus) = semiring.Plus -// let (ClosedBinaryOp times) = semiring.Times - -// let matrixLength = matrix.Values.Length - -// let kernel1 = -// <@ -// fun (ndRange: _1D) -// (matrixColumns: int[]) -// (matrixValues: 'a[]) -// (vectorBitmap: bool[]) -// (vectorValues: 'a[]) -// (intermediateArray: 'a[]) -> - -// let i = ndRange.GlobalID0 -// if i < matrixLength && vectorBitmap.[i] then -// let value = matrixValues.[i] -// let column = matrixColumns.[i] -// intermediateArray.[i] <- (%times) value vectorValues.[column] -// @> - -// let kernel2 = -// <@ -// fun (ndRange: _1D) -// (intermediateArray: 'a[]) -// (matrixPtr: int[]) -// (outputVector: 'a[]) -> - -// let gid = ndRange.GlobalID0 -// let lid = ndRange.LocalID0 - -// let localPtr = localArray (Utils.defaultWorkGroupSize + 1) -// localPtr.[lid] <- matrixPtr.[gid] -// if lid = 0 then -// localPtr.[Utils.defaultWorkGroupSize] <- matrixPtr.[gid + Utils.defaultWorkGroupSize] -// barrier () -// @> - -// let intermediateArray = Array.zeroCreate<'a> matrixLength -// do! RunCommand kernel1 <| fun kernelPrepare -> -// let range = _1D(Utils.getDefaultGlobalSize matrixLength, Utils.defaultWorkGroupSize) -// kernelPrepare -// range -// matrix.ColumnIndices -// matrix.Values -// vector.Bitmap -// vector.Values -// intermediateArray - -// let outputVector = Array.zeroCreate<'a> matrix.RowCount -// do! RunCommand kernel2 <| fun kernelPrepare -> -// let range = _1D(Utils.getDefaultGlobalSize matrixLength, Utils.defaultWorkGroupSize) -// kernelPrepare -// range -// intermediateArray -// matrix.RowPointers -// outputVector - -// return { -// Bitmap = vector.Bitmap -// Values = outputVector -// } -// } From 58e8a388008587dffda805e645b0cdbddb06c9d4 Mon Sep 17 00:00:00 2001 From: kirillgarbar Date: Sat, 12 Nov 2022 12:00:17 +0300 Subject: [PATCH 7/7] Reduced code duplication in tests, new helpers modules --- .../BackendCommonTests/BitonicSortTests.fs | 1 + .../BackendCommonTests/ConvertTests.fs | 2 +- .../BackendCommonTests/CopyTests.fs | 4 +- .../MatrixElementwiseTests.fs | 68 +--------- .../BackendCommonTests/MxmTests.fs | 1 + .../BackendCommonTests/PrefixSumTests.fs | 1 + .../RemoveDuplicatesTests.fs | 4 +- .../BackendCommonTests/ReplicateTests.fs | 4 +- .../BackendCommonTests/ScatterTests.fs | 1 + .../BackendCommonTests/SpMVTests.fs | 24 +--- .../BackendCommonTests/TransposeTests.fs | 19 +-- tests/GraphBLAS-sharp.Tests/Helpers.fs | 123 ++++++++++++------ 12 files changed, 106 insertions(+), 146 deletions(-) diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/BitonicSortTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/BitonicSortTests.fs index c0211c37..37bf27fe 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/BitonicSortTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/BitonicSortTests.fs @@ -6,6 +6,7 @@ open Expecto.Logging.Message open GraphBLAS.FSharp.Backend.Common open Brahma.FSharp open GraphBLAS.FSharp.Tests.Utils +open GraphBLAS.FSharp.Tests.Context let logger = Log.create "BitonicSort.Tests" diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ConvertTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ConvertTests.fs index aa38ea79..5348baa1 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ConvertTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ConvertTests.fs @@ -4,7 +4,7 @@ open Expecto open Expecto.Logging open Expecto.Logging.Message open GraphBLAS.FSharp.Tests.Utils - +open GraphBLAS.FSharp.Tests.Context open GraphBLAS.FSharp.Backend open GraphBLAS.FSharp diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/CopyTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/CopyTests.fs index eb5f73df..5be7f4a7 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/CopyTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/CopyTests.fs @@ -9,10 +9,10 @@ open GraphBLAS.FSharp.Tests let logger = Log.create "Copy.Tests" -let context = Utils.defaultContext.ClContext +let context = Context.defaultContext.ClContext let testCases = - let q = Utils.defaultContext.Queue + let q = Context.defaultContext.Queue q.Error.Add(fun e -> failwithf "%A" e) let getCopyFun copy = diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MatrixElementwiseTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MatrixElementwiseTests.fs index 282a57f8..5614fa2f 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MatrixElementwiseTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MatrixElementwiseTests.fs @@ -8,7 +8,7 @@ open Expecto.Logging.Message open Brahma.FSharp open GraphBLAS.FSharp.Backend open GraphBLAS.FSharp -open GraphBLAS.FSharp.Tests +open GraphBLAS.FSharp.Tests.TestCases open GraphBLAS.FSharp.Tests.Utils open Microsoft.FSharp.Collections open OpenCL.Net @@ -52,7 +52,7 @@ let correctnessGenericTest toCOOFun (isEqual: 'a -> 'a -> bool) q - (case: OperationCase) + (case: MatrixOperationCase) (leftMatrix: 'a [,], rightMatrix: 'a [,]) = @@ -135,21 +135,7 @@ let testFixturesEWiseAdd case = |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] let elementwiseAddTests = - testCases - |> List.filter - (fun case -> - let mutable e = ErrorCode.Unknown - let device = case.ClContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun case -> case.ClContext.ClContext.ClDevice.DeviceType, case.MatrixCase) - |> List.collect testFixturesEWiseAdd - |> testList "Backend.Matrix.EWiseAdd tests" + matrixOperationGPUTests "Backend.Matrix.EWiseAdd tests" testFixturesEWiseAdd let testFixturesEWiseAddAtLeastOne case = [ let config = defaultConfig @@ -199,21 +185,7 @@ let testFixturesEWiseAddAtLeastOne case = |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] let elementwiseAddAtLeastOneTests = - testCases - |> List.filter - (fun case -> - let mutable e = ErrorCode.Unknown - let device = case.ClContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun case -> case.ClContext.ClContext.ClDevice.DeviceType, case.MatrixCase) - |> List.collect testFixturesEWiseAddAtLeastOne - |> testList "Backend.Matrix.EWiseAddAtLeastOne tests" + matrixOperationGPUTests "Backend.Matrix.EWiseAddAtLeastOne tests" testFixturesEWiseAddAtLeastOne let testFixturesEWiseAddAtLeastOneToCOO case = [ let config = defaultConfig @@ -263,21 +235,7 @@ let testFixturesEWiseAddAtLeastOneToCOO case = |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] let elementwiseAddAtLeastOneToCOOTests = - testCases - |> List.filter - (fun case -> - let mutable e = ErrorCode.Unknown - let device = case.ClContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun case -> case.ClContext.ClContext.ClDevice.DeviceType, case.MatrixCase) - |> List.collect testFixturesEWiseAddAtLeastOneToCOO - |> testList "Backend.Matrix.EWiseAddAtLeastOneToCOO tests" + matrixOperationGPUTests "Backend.Matrix.EWiseAddAtLeastOneToCOO tests" testFixturesEWiseAddAtLeastOneToCOO let testFixturesEWiseMulAtLeastOne case = [ let config = defaultConfig @@ -327,18 +285,4 @@ let testFixturesEWiseMulAtLeastOne case = |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] let elementwiseMulAtLeastOneTests = - testCases - |> List.filter - (fun case -> - let mutable e = ErrorCode.Unknown - let device = case.ClContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun case -> case.ClContext.ClContext.ClDevice.DeviceType, case.MatrixCase) - |> List.collect testFixturesEWiseMulAtLeastOne - |> testList "Backend.Matrix.eWiseMulAtLeastOne tests" + matrixOperationGPUTests "Backend.Matrix.eWiseMulAtLeastOne tests" testFixturesEWiseMulAtLeastOne diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MxmTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MxmTests.fs index ad2426fb..5d6cb2e5 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MxmTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/MxmTests.fs @@ -5,6 +5,7 @@ open Expecto.Logging open Expecto.Logging.Message open GraphBLAS.FSharp.Tests.Utils +open GraphBLAS.FSharp.Tests.Context open GraphBLAS.FSharp.Tests open GraphBLAS.FSharp.Backend open GraphBLAS.FSharp diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/PrefixSumTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/PrefixSumTests.fs index d1b93c57..d83b4dc9 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/PrefixSumTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/PrefixSumTests.fs @@ -5,6 +5,7 @@ open Expecto.Logging open Expecto.Logging.Message open Brahma.FSharp open GraphBLAS.FSharp.Backend.Common +open GraphBLAS.FSharp.Tests.Context open GraphBLAS.FSharp.Tests.Utils let logger = Log.create "PrefixSum.Tests" diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/RemoveDuplicatesTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/RemoveDuplicatesTests.fs index bbf1991d..744a56f1 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/RemoveDuplicatesTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/RemoveDuplicatesTests.fs @@ -9,13 +9,13 @@ open GraphBLAS.FSharp.Tests let logger = Log.create "RemoveDuplicates.Tests" -let context = Utils.defaultContext.ClContext +let context = Context.defaultContext.ClContext let testCases = let removeDuplicates_wg_1 = ClArray.removeDuplications context 1 let removeDuplicates_wg_2 = ClArray.removeDuplications context 2 let removeDuplicates_wg_32 = ClArray.removeDuplications context 32 - let q = Utils.defaultContext.Queue + let q = Context.defaultContext.Queue q.Error.Add(fun e -> failwithf "%A" e) [ testCase "Simple correctness test" diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ReplicateTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ReplicateTests.fs index 73fe834e..88338b74 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ReplicateTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ReplicateTests.fs @@ -9,10 +9,10 @@ open GraphBLAS.FSharp.Tests let logger = Log.create "Replicate.Tests" -let context = Utils.defaultContext.ClContext +let context = Context.defaultContext.ClContext let testCases = - let q = Utils.defaultContext.Queue + let q = Context.defaultContext.Queue q.Error.Add(fun e -> failwithf "%A" e) let getReplicateFun replicate = diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ScatterTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ScatterTests.fs index 9facea47..13cf30e0 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ScatterTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/ScatterTests.fs @@ -5,6 +5,7 @@ open Expecto.Logging open Expecto.Logging.Message open Brahma.FSharp open GraphBLAS.FSharp.Backend.Common +open GraphBLAS.FSharp.Tests.Context open GraphBLAS.FSharp.Tests.Utils let logger = Log.create "Scatter.Tests" diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs index ff16e282..25b7031e 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/SpMVTests.fs @@ -6,6 +6,8 @@ open GraphBLAS.FSharp.Backend open GraphBLAS.FSharp.Backend.ArraysExtensions open GraphBLAS.FSharp open GraphBLAS.FSharp.Tests.Utils +open GraphBLAS.FSharp.Tests.Context +open GraphBLAS.FSharp.Tests.TestCases open Microsoft.FSharp.Collections open Microsoft.FSharp.Core open OpenCL.Net @@ -83,7 +85,8 @@ let testFixturesSpMV (testContext: TestContext) = [ let config = defaultConfig let wgSize = 32 - let getCorrectnessTestName datatype = sprintf "Correctness on %s" datatype + let getCorrectnessTestName datatype = + sprintf "Correctness on %s, %A" datatype testContext.ClContext let context = testContext.ClContext let q = testContext.Queue @@ -112,26 +115,9 @@ let testFixturesSpMV (testContext: TestContext) = let byteAdd = Vector.spMV context byteSum byteMul wgSize - let byteToCOO = Matrix.toCOO context wgSize - testContext |> correctnessGenericTest 0uy (+) (*) byteAdd (=) q |> testPropertyWithConfig config (getCorrectnessTestName "byte") ] let tests = - availableContexts "" - |> List.ofSeq - |> List.filter - (fun testContext -> - let mutable e = ErrorCode.Unknown - let device = testContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun testContext -> testContext.ClContext.ClDevice.DeviceType) - |> List.collect testFixturesSpMV - |> testList "Backend.Common.SpMV tests" + gpuTests "Backend.Vector.SpMV tests" testFixturesSpMV diff --git a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/TransposeTests.fs b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/TransposeTests.fs index a400b34c..1ad3f929 100644 --- a/tests/GraphBLAS-sharp.Tests/BackendCommonTests/TransposeTests.fs +++ b/tests/GraphBLAS-sharp.Tests/BackendCommonTests/TransposeTests.fs @@ -6,6 +6,7 @@ open Expecto.Logging.Message open GraphBLAS.FSharp.Backend open GraphBLAS.FSharp open GraphBLAS.FSharp.Tests.Utils +open GraphBLAS.FSharp.Tests.TestCases open OpenCL.Net let logger = Log.create "Transpose.Tests" @@ -121,7 +122,7 @@ let makeTestTwiceTranspose context q transposeFun areEqual zero case (array: 'a let testFixtures case = let getCorrectnessTestName datatype = - sprintf "Correctness on %s, %A" datatype case.MatrixCase + sprintf "Correctness on %s, %A, %A" datatype case.MatrixCase case.ClContext let areEqualFloat x y = System.Double.IsNaN x && System.Double.IsNaN y @@ -173,18 +174,4 @@ let testFixtures case = |> testPropertyWithConfig config (getCorrectnessTestName "bool (twice transpose)") ] let tests = - testCases - |> List.filter - (fun case -> - let mutable e = ErrorCode.Unknown - let device = case.ClContext.ClContext.ClDevice.Device - - let deviceType = - Cl - .GetDeviceInfo(device, DeviceInfo.Type, &e) - .CastTo() - - deviceType = DeviceType.Gpu) - |> List.distinctBy (fun case -> case.ClContext.ClContext.ClDevice.DeviceType, case.MatrixCase) - |> List.collect testFixtures - |> testList "Transpose tests" + matrixOperationGPUTests "Transpose tests" testFixtures diff --git a/tests/GraphBLAS-sharp.Tests/Helpers.fs b/tests/GraphBLAS-sharp.Tests/Helpers.fs index 4ba92d10..13a09a52 100644 --- a/tests/GraphBLAS-sharp.Tests/Helpers.fs +++ b/tests/GraphBLAS-sharp.Tests/Helpers.fs @@ -533,11 +533,7 @@ module Generators = <| Arb.generate |> Arb.fromGen - module Utils = - type TestContext = - { ClContext: ClContext - Queue: MailboxProcessor } let defaultConfig = { FsCheckConfig.defaultConfig with @@ -553,6 +549,40 @@ module Utils = typeof typeof ] } + let createMatrixFromArray2D matrixCase array isZero = + match matrixCase with + | CSR -> MatrixCSR <| CSRMatrix.FromArray2D(array, isZero) + | COO -> MatrixCOO <| COOMatrix.FromArray2D(array, isZero) + | CSC -> MatrixCSC <| CSCMatrix.FromArray2D(array, isZero) + + let createVectorFromArray vectorCase array isZero = + match vectorCase with + | Backend.VectorFormat.Sparse -> + Backend.VectorSparse + <| Backend.SparseVector.FromArray(array, isZero) + | Backend.VectorFormat.Dense -> + Backend.VectorDense + <| Backend.ArraysExtensions.DenseVectorFromArray(array, isZero) + + let compareArrays areEqual (actual: 'a []) (expected: 'a []) message = + sprintf "%s. Lengths should be equal. Actual is %A, expected %A" message actual expected + |> Expect.equal actual.Length expected.Length + + for i in 0 .. actual.Length - 1 do + if not (areEqual actual.[i] expected.[i]) then + sprintf "%s. Arrays differ at position %A of %A. Actual value is %A, expected %A" + <| message + <| i + <| (actual.Length - 1) + <| actual.[i] + <| expected.[i] + |> failtestf "%s" + + let listOfUnionCases<'a> = + FSharpType.GetUnionCases typeof<'a> + |> Array.map (fun caseInfo -> FSharpValue.MakeUnion(caseInfo, [||]) :?> 'a) + |> List.ofArray + let rec cartesian listOfLists = match listOfLists with | [ x ] -> List.fold (fun acc elem -> [ elem ] :: acc) [] x @@ -565,10 +595,11 @@ module Utils = (cartesian t) | _ -> [] - let listOfUnionCases<'a> = - FSharpType.GetUnionCases typeof<'a> - |> Array.map (fun caseInfo -> FSharpValue.MakeUnion(caseInfo, [||]) :?> 'a) - |> List.ofArray +module Context = + + type TestContext = + { ClContext: ClContext + Queue: MailboxProcessor } let availableContexts (platformRegex: string) = let mutable e = ErrorCode.Unknown @@ -645,45 +676,53 @@ module Utils = { ClContext = context; Queue = queue } - type OperationCase = - { ClContext: TestContext + let gpuOnlyContextFilter = + Seq.filter + (fun (context: TestContext) -> + let mutable e = ErrorCode.Unknown + let device = context.ClContext.ClDevice.Device + + let deviceType = + Cl + .GetDeviceInfo(device, DeviceInfo.Type, &e) + .CastTo() + + deviceType = DeviceType.Gpu) + +module TestCases = + + type MatrixOperationCase = + { ClContext: Context.TestContext MatrixCase: MatrixFormat } - let testCases = - [ availableContexts "" |> Seq.map box - listOfUnionCases |> Seq.map box ] + let defaultPlatformRegex = "" + + let testCases contextFilter = + Context.availableContexts defaultPlatformRegex + |> contextFilter + |> List.ofSeq + + let matrixTestCases contextFilter = + [ Context.availableContexts defaultPlatformRegex + |> contextFilter + |> Seq.map box + Utils.listOfUnionCases + |> Seq.map box ] |> List.map List.ofSeq - |> cartesian + |> Utils.cartesian |> List.map (fun list -> { ClContext = unbox list.[0] MatrixCase = unbox list.[1] }) - let createMatrixFromArray2D matrixCase array isZero = - match matrixCase with - | CSR -> MatrixCSR <| CSRMatrix.FromArray2D(array, isZero) - | COO -> MatrixCOO <| COOMatrix.FromArray2D(array, isZero) - | CSC -> MatrixCSC <| CSCMatrix.FromArray2D(array, isZero) - - let createVectorFromArray vectorCase array isZero = - match vectorCase with - | Backend.VectorFormat.Sparse -> - Backend.VectorSparse - <| Backend.SparseVector.FromArray(array, isZero) - | Backend.VectorFormat.Dense -> - Backend.VectorDense - <| Backend.ArraysExtensions.DenseVectorFromArray(array, isZero) - - let compareArrays areEqual (actual: 'a []) (expected: 'a []) message = - sprintf "%s. Lengths should be equal. Actual is %A, expected %A" message actual expected - |> Expect.equal actual.Length expected.Length - - for i in 0 .. actual.Length - 1 do - if not (areEqual actual.[i] expected.[i]) then - sprintf "%s. Arrays differ at position %A of %A. Actual value is %A, expected %A" - <| message - <| i - <| (actual.Length - 1) - <| actual.[i] - <| expected.[i] - |> failtestf "%s" + let matrixOperationGPUTests name testFixtures = + matrixTestCases Context.gpuOnlyContextFilter + |> List.distinctBy (fun case -> case.ClContext.ClContext, case.MatrixCase) + |> List.collect testFixtures + |> testList name + + let gpuTests name testFixtures = + testCases Context.gpuOnlyContextFilter + |> List.distinctBy (fun testContext -> testContext.ClContext) + |> List.collect testFixtures + |> testList name