Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PageRank #88

Merged
merged 13 commits into from
Mar 18, 2024
46 changes: 37 additions & 9 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Algorithms/BFS.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace GraphBLAS.FSharp.Benchmarks.Algorithms.BFS
namespace GraphBLAS.FSharp.Benchmarks.Algorithms.BFS

open System.IO
open BenchmarkDotNet.Attributes
Expand Down Expand Up @@ -27,7 +27,7 @@ type Benchmarks<'elem when 'elem : struct>(
let mutable matrix = Unchecked.defaultof<ClMatrix<'elem>>
let mutable matrixHost = Unchecked.defaultof<_>

member val ResultLevels = Unchecked.defaultof<ClVector<'elem>> with get,set
member val ResultLevels = Unchecked.defaultof<ClVector<int>> with get,set

[<ParamsSource("AvailableContexts")>]
member val OclContextInfo = Unchecked.defaultof<Utils.BenchmarkContext * int> with get, set
Expand Down Expand Up @@ -113,10 +113,12 @@ type WithoutTransferBenchmark<'elem when 'elem : struct>(
override this.GlobalSetup() =
this.ReadMatrix()
this.LoadMatrixToGPU()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much is it necessary to duplicate this? Is it possible to make an extension?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to make extension for specialization of generic MailboxProcessor and failed, as far as I know it is possible only for static members and static method would look not much better than this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Seems like that. Than maybe just

module Queue = 
    let block (q: MailboxProcessor<Msg>) = q.PostAndReply(Msg.MsgNotifyMe)

in GraphBlas-sharp.Backend.Objects

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was told that this may work.

[<System.Runtime.CompilerServices.Extension>] // На F# 8 можно убрать.
type MailboxProcessorExts =
    [<System.Runtime.CompilerServices.Extension>]
    static member Block(this : MailboxProcessor<int>) =
        this.Post 42

We can choose!

[<IterationCleanup>]
override this.IterationCleanup() =
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() =
Expand All @@ -127,10 +129,34 @@ type WithoutTransferBenchmark<'elem when 'elem : struct>(
this.BFS()
this.Processor.PostAndReply Msg.MsgNotifyMe

type BFSWithoutTransferBenchmarkInt32() =
type BFSWithoutTransferBenchmarkBool() =

inherit WithoutTransferBenchmark<bool>(
(Algorithms.BFS.singleSource ArithmeticOperations.boolSumOption ArithmeticOperations.boolMulOption),
(fun _ -> true),
(fun _ -> true),
0,
(fun context matrix -> ClMatrix.CSR <| matrix.ToCSR.ToDevice context))

static member InputMatrixProvider =
Benchmarks<_>.InputMatrixProviderBuilder "BFSBenchmarks.txt"

type BFSPushPullWithoutTransferBenchmarkBool() =

inherit WithoutTransferBenchmark<bool>(
(Algorithms.BFS.singleSourcePushPull ArithmeticOperations.boolSumOption ArithmeticOperations.boolMulOption),
(fun _ -> true),
(fun _ -> true),
0,
(fun context matrix -> ClMatrix.CSR <| matrix.ToCSR.ToDevice context))

static member InputMatrixProvider =
Benchmarks<_>.InputMatrixProviderBuilder "BFSBenchmarks.txt"

type SSSPWithoutTransferBenchmarkInt32() =

inherit WithoutTransferBenchmark<int>(
(Algorithms.BFS.singleSource ArithmeticOperations.intSumOption ArithmeticOperations.intMulOption),
Algorithms.SSSP.run,
int32,
(fun _ -> Utils.nextInt (System.Random())),
0,
Expand All @@ -156,6 +182,7 @@ type WithTransferBenchmark<'elem when 'elem : struct>(
[<GlobalSetup>]
override this.GlobalSetup() =
this.ReadMatrix()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() =
Expand All @@ -165,6 +192,7 @@ type WithTransferBenchmark<'elem when 'elem : struct>(
override this.IterationCleanup() =
this.ClearInputMatrix()
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark() =
Expand All @@ -176,12 +204,12 @@ type WithTransferBenchmark<'elem when 'elem : struct>(
this.Processor.PostAndReply Msg.MsgNotifyMe
| _ -> failwith "Impossible"

type BFSWithTransferBenchmarkInt32() =
type BFSWithTransferBenchmarkBool() =

inherit WithTransferBenchmark<int>(
(Algorithms.BFS.singleSource ArithmeticOperations.intSumOption ArithmeticOperations.intMulOption),
int32,
(fun _ -> Utils.nextInt (System.Random())),
inherit WithTransferBenchmark<bool>(
(Algorithms.BFS.singleSource ArithmeticOperations.boolSumOption ArithmeticOperations.boolMulOption),
(fun _ -> true),
(fun _ -> true),
0,
(fun context matrix -> ClMatrix.CSR <| matrix.ToCSR.ToDevice context))

Expand Down
134 changes: 134 additions & 0 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Algorithms/PageRank.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
namespace GraphBLAS.FSharp.Benchmarks.Algorithms.PageRank

open System.IO
open BenchmarkDotNet.Attributes
open GraphBLAS.FSharp
open GraphBLAS.FSharp.IO
open Brahma.FSharp
open Microsoft.FSharp.Core
open GraphBLAS.FSharp.Objects.ArraysExtensions
open GraphBLAS.FSharp.Benchmarks
open GraphBLAS.FSharp.Objects

[<AbstractClass>]
[<IterationCount(10)>]
[<WarmupCount(3)>]
[<Config(typeof<Configs.Matrix>)>]
type Benchmarks(
buildFunToBenchmark,
converter: string -> float32,
binaryConverter,
buildMatrix)
=

let mutable funToBenchmark = None
let mutable matrix = Unchecked.defaultof<ClMatrix<float32>>
let mutable matrixPrepared = Unchecked.defaultof<PageRankMatrix<float32>>
let mutable matrixHost = Unchecked.defaultof<_>

let accuracy = 0.00000001f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are predefined constants in Expecto.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, why is it necessary separately? Perhaps we need to create a module with constants and actions on them in order to avoid mistakes in the future?

Abstract types could also be used here, so that they could be used only with the help of this most magical module.


member val Result = Unchecked.defaultof<ClVector<float32>> with get,set

[<ParamsSource("AvailableContexts")>]
member val OclContextInfo = Unchecked.defaultof<Utils.BenchmarkContext * int> with get, set

[<ParamsSource("InputMatrixProvider")>]
member val InputMatrixReader = Unchecked.defaultof<MtxReader> with get, set

member this.OclContext = (fst this.OclContextInfo).ClContext
member this.WorkGroupSize = snd this.OclContextInfo

member this.Processor =
let p = (fst this.OclContextInfo).Queue
p.Error.Add(fun e -> failwithf "%A" e)
p

static member AvailableContexts = Utils.availableContexts

static member InputMatrixProviderBuilder pathToConfig =
let datasetFolder = ""
pathToConfig
|> Utils.getMatricesFilenames
|> Seq.map
(fun matrixFilename ->
printfn "%A" matrixFilename

match Path.GetExtension matrixFilename with
| ".mtx" -> MtxReader(Utils.getFullPathToMatrix datasetFolder matrixFilename)
| _ -> failwith "Unsupported matrix format")

member this.FunToBenchmark =
match funToBenchmark with
| None ->
let x = buildFunToBenchmark this.OclContext this.WorkGroupSize
funToBenchmark <- Some x
x
| Some x -> x

member this.PageRank() =
this.Result <- this.FunToBenchmark this.Processor matrixPrepared accuracy

member this.ClearInputMatrix() =
matrix.Dispose this.Processor

member this.ClearPreparedMatrix() =
matrixPrepared.Dispose this.Processor

member this.ClearResult() = this.Result.Dispose this.Processor

member this.ReadMatrix() =
let converter =
match this.InputMatrixReader.Field with
| Pattern -> binaryConverter
| _ -> converter

matrixHost <- this.InputMatrixReader.ReadMatrix converter

member this.LoadMatrixToGPU() =
matrix <- buildMatrix this.OclContext matrixHost

member this.PrepareMatrix() =
matrixPrepared <- Algorithms.PageRank.prepareMatrix this.OclContext this.WorkGroupSize this.Processor matrix

abstract member GlobalSetup : unit -> unit

abstract member IterationCleanup : unit -> unit

abstract member GlobalCleanup : unit -> unit

abstract member Benchmark : unit -> unit

type PageRankWithoutTransferBenchmarkFloat32() =

inherit Benchmarks(
Algorithms.PageRank.run,
float32,
(fun _ -> float32 <| Utils.nextInt (System.Random())),
(fun context matrix -> ClMatrix.CSR <| matrix.ToCSR.ToDevice context))

static member InputMatrixProvider =
Benchmarks.InputMatrixProviderBuilder "BFSBenchmarks.txt"

[<GlobalSetup>]
override this.GlobalSetup() =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've always thought about it. Don't you need to make the preparatory methods blocking? Then is it possible to avoid blocking in the [<Benchmark>] itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a good idea to block after preparotory methods in case some of them are not blocking by itself, but blocking in the [<Benchmark>] is still necessary to wait for all kernels to execute.

this.ReadMatrix()
this.LoadMatrixToGPU()
this.Processor.PostAndReply(Msg.MsgNotifyMe)
this.PrepareMatrix()
this.ClearInputMatrix()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<IterationCleanup>]
override this.IterationCleanup() =
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() =
this.ClearPreparedMatrix()

[<Benchmark>]
override this.Benchmark() =
this.PageRank()
this.Processor.PostAndReply(Msg.MsgNotifyMe)
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
<Compile Include="Matrix/Map2/MathNET.fs" />
<Compile Include="Vector/Map2.fs" />
<Compile Include="Algorithms/BFS.fs" />
<Compile Include="Algorithms/PageRank.fs" />
<Compile Include="Program.fs" />
<Folder Include="Datasets" />
</ItemGroup>
<Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>
</Project>
2 changes: 2 additions & 0 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Matrix/Map2/Map2.fs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ module WithTransfer =
[<GlobalSetup>]
override this.GlobalSetup() =
this.ReadMatrices()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() = ()
Expand All @@ -259,6 +260,7 @@ module WithTransfer =
override this.IterationCleanup() =
this.ClearInputMatrices()
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark() =
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Matrix/SpGeMM/Expand.fs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ module WithoutTransfer =
override this.GlobalSetup() =
this.ReadMatrices()
this.LoadMatricesToGPU()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark() =
Expand All @@ -124,6 +125,7 @@ module WithoutTransfer =
[<IterationCleanup>]
override this.IterationCleanup () =
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup () =
Expand Down
4 changes: 4 additions & 0 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Matrix/SpGeMM/Masked.fs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type MxmBenchmarksMultiplicationOnly<'elem when 'elem : struct>(
this.ReadMatrices ()
this.LoadMatricesToGPU ()
this.ConvertSecondMatrixToCSC()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark () =
Expand All @@ -161,6 +162,7 @@ type MxmBenchmarksMultiplicationOnly<'elem when 'elem : struct>(
[<IterationCleanup>]
override this.IterationCleanup () =
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup () =
Expand All @@ -182,6 +184,7 @@ type MxmBenchmarksWithTransposing<'elem when 'elem : struct>(
override this.GlobalSetup() =
this.ReadMatrices()
this.LoadMatricesToGPU ()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark() =
Expand All @@ -194,6 +197,7 @@ type MxmBenchmarksWithTransposing<'elem when 'elem : struct>(
override this.IterationCleanup() =
this.ClearResult()
this.ConvertSecondMatrixToCSR()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() =
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/GraphBLAS-sharp.Benchmarks/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open BenchmarkDotNet.Running
[<EntryPoint>]
let main argv =
let benchmarks =
BenchmarkSwitcher [| typeof<Algorithms.BFS.BFSWithoutTransferBenchmarkInt32> |]
BenchmarkSwitcher [| typeof<Algorithms.BFS.BFSWithoutTransferBenchmarkBool> |]

benchmarks.Run argv |> ignore
0
3 changes: 3 additions & 0 deletions benchmarks/GraphBLAS-sharp.Benchmarks/Vector/Map2.fs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ module WithoutTransfer =
override this.IterationCleanup() =
this.ClearResult()
this.ClearInputVectors()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() = ()
Expand Down Expand Up @@ -159,6 +160,7 @@ module WithTransfer =
[<IterationSetup>]
override this.IterationSetup() =
this.CreateVectors()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<Benchmark>]
override this.Benchmark () =
Expand All @@ -175,6 +177,7 @@ module WithTransfer =
override this.IterationCleanup () =
this.ClearInputVectors()
this.ClearResult()
this.Processor.PostAndReply(Msg.MsgNotifyMe)

[<GlobalCleanup>]
override this.GlobalCleanup() = ()
Expand Down
21 changes: 21 additions & 0 deletions src/GraphBLAS-sharp.Backend/Algorithms/Algorithms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,24 @@ module Algorithms =

module SSSP =
let run = SSSP.run

module PageRank =
/// <summary>
/// Computes PageRank of the given matrix.
/// Matrix should be prepared in advance using "PageRank.prepareMatrix" method.
/// Accepts accuracy as a parameter which determines how many iterations will be performed.
/// Values of accuracy lower than 1e-06 are not recommended since the process may never stop.
/// </summary>
/// <example>
/// <code>
/// let preparedMatrix = PageRank.prepareMatrix clContext workGroupSize queue matrix
/// let accuracy = 1e-05
/// let pageRank = PageRank.run clContext workGroupSize queue preparedMatrix accuracy
/// </code>
/// </example>
let run = PageRank.run
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think documentation is needed. At least we should say that matrix has to be prepared first by calling prepareMatrix method.


/// <summary>
/// Converts matrix representing a graph to a format suitable for PageRank algorithm.
/// </summary>
let prepareMatrix = PageRank.prepareMatrix
8 changes: 4 additions & 4 deletions src/GraphBLAS-sharp.Backend/Algorithms/BFS.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ open GraphBLAS.FSharp.Objects.ClCellExtensions

module internal BFS =
let singleSource
(add: Expr<int option -> int option -> int option>)
(mul: Expr<'a option -> int option -> int option>)
(add: Expr<bool option -> bool option -> bool option>)
(mul: Expr<bool option -> bool option -> bool option>)
(clContext: ClContext)
workGroupSize
=
Expand All @@ -34,14 +34,14 @@ module internal BFS =
let containsNonZero =
Vector.exists Predicates.isSome clContext workGroupSize

fun (queue: MailboxProcessor<Msg>) (matrix: ClMatrix<'a>) (source: int) ->
fun (queue: MailboxProcessor<Msg>) (matrix: ClMatrix<bool>) (source: int) ->
let vertexCount = matrix.RowCount

let levels =
zeroCreate queue DeviceOnly vertexCount Dense

let front =
ofList queue DeviceOnly Dense vertexCount [ source, 1 ]
ofList queue DeviceOnly Dense vertexCount [ source, true ]

let mutable level = 0
let mutable stop = false
Expand Down
Loading
Loading