diff --git a/Content/Library/.github/workflows/benchmark.yml b/Content/Library/.github/workflows/benchmark.yml new file mode 100644 index 00000000..1097d09c --- /dev/null +++ b/Content/Library/.github/workflows/benchmark.yml @@ -0,0 +1,43 @@ +name: Benchmark + +on: + push: + branches: + - MyReleaseBranch + +permissions: + # contents permission to update benchmark contents in MyReleaseBranch + contents: write + +jobs: + benchmark: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Setup necessary dotnet SDKs + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 10.x + - name: Run benchmarks + run: | + chmod +x ./build.sh + ./build.sh RunBenchmarks + env: + CI: true + CONFIGURATION: Release + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + name: MyLib.1 Benchmark + tool: 'benchmarkdotnet' + output-file-path: 'BenchmarkDotNet.Artifacts/results/*.json' + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + gh-pages-branch: MyReleaseBranch + benchmark-data-dir-path: docs/benchmarks + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: false + alert-comment-cc-users: '@MyGithubUsername' diff --git a/Content/Library/.gitignore b/Content/Library/.gitignore index 3e65c32e..931f44f7 100644 --- a/Content/Library/.gitignore +++ b/Content/Library/.gitignore @@ -275,3 +275,6 @@ tmp/ temp/ .fsdocs docs/ + +# BenchmarkDotNet artifacts +BenchmarkDotNet.Artifacts/ diff --git a/Content/Library/Directory.Packages.props b/Content/Library/Directory.Packages.props index 70604705..e9917d1f 100644 --- a/Content/Library/Directory.Packages.props +++ b/Content/Library/Directory.Packages.props @@ -40,6 +40,7 @@ + diff --git a/Content/Library/MyLib.1.slnx b/Content/Library/MyLib.1.slnx index 2f5b17d0..3152f82a 100644 --- a/Content/Library/MyLib.1.slnx +++ b/Content/Library/MyLib.1.slnx @@ -16,5 +16,11 @@ + + + + + + diff --git a/Content/Library/README.md b/Content/Library/README.md index 14a85166..0d05217c 100644 --- a/Content/Library/README.md +++ b/Content/Library/README.md @@ -98,6 +98,7 @@ src/MyLib.1/bin/ - `GenerateCoverageReport` - Code coverage is run during `DotnetTest` and this generates a report via [ReportGenerator](https://github.com/danielpalme/ReportGenerator). - `ShowCoverageReport` - Shows the report generated in `GenerateCoverageReport`. - `WatchTests` - Runs [dotnet watch](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.0) with the test projects. Useful for rapid feedback loops. +- `RunBenchmarks` - Runs [BenchmarkDotNet](https://benchmarkdotnet.org/) benchmarks in the `benchmarks` folder. - `GenerateAssemblyInfo` - Generates [AssemblyInfo](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.assemblyinfo?view=netframework-4.8) for libraries. - `DotnetPack` - Runs [dotnet pack](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack). This includes running [Source Link](https://github.com/dotnet/sourcelink). - `SourceLinkTest` - Runs a Source Link test tool to verify Source Links were properly generated. diff --git a/Content/Library/benchmarks/Directory.Build.props b/Content/Library/benchmarks/Directory.Build.props new file mode 100644 index 00000000..8748fdee --- /dev/null +++ b/Content/Library/benchmarks/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs b/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs new file mode 100644 index 00000000..31768413 --- /dev/null +++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs @@ -0,0 +1,30 @@ +namespace MyLib._1.Benchmarks + +open BenchmarkDotNet.Attributes +open MyLib._1 +open System + +[] +type LibraryBenchmarks() = + + let samplePerson = { + Say.Person.Name = "Benchmark" + Say.Person.FavoriteNumber = 42 + Say.Person.FavoriteColor = Say.FavoriteColor.Blue + Say.Person.DateOfBirth = DateTimeOffset.Now + } + + [] + member _.HelloPerson() = Say.helloPerson samplePerson + + [] + member _.AddNumbers() = Say.add 100 200 + + [] + member _.AddNumbersLoop() = + let mutable result = 0 + + for i in 1..1000 do + result <- Say.add result i + + result diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj b/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj new file mode 100644 index 00000000..ebfb0db7 --- /dev/null +++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj @@ -0,0 +1,18 @@ + + + + Exe + net10.0 + false + + + + + + + + + + + + diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs b/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs new file mode 100644 index 00000000..19b8c4b0 --- /dev/null +++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs @@ -0,0 +1,11 @@ +module Program + +open BenchmarkDotNet.Running +open MyLib._1.Benchmarks + +[] +let main args = + BenchmarkRunner.Run() + |> ignore + + 0 diff --git a/Content/Library/build/build.fs b/Content/Library/build/build.fs index cdd665e2..66d3e9ed 100644 --- a/Content/Library/build/build.fs +++ b/Content/Library/build/build.fs @@ -509,6 +509,17 @@ let watchTests _ = cancelEvent.Cancel <- true +let benchmarksGlob = + rootDirectory + "benchmarks/**/*.??proj" + +let runBenchmarks _ = + !!benchmarksGlob + |> Seq.iter (fun proj -> + DotNet.exec id "run" $"--project \"{proj}\" --configuration Release" + |> ignore + ) + let generateAssemblyInfo _ = let (|Fsproj|Csproj|Vbproj|) (projFileName: string) = @@ -731,6 +742,7 @@ let initTargets () = Target.create "GenerateCoverageReport" generateCoverageReport Target.create "ShowCoverageReport" showCoverageReport Target.create "WatchTests" watchTests + Target.create "RunBenchmarks" runBenchmarks Target.create "GenerateAssemblyInfo" generateAssemblyInfo Target.create "DotnetPack" dotnetPack Target.create "SourceLinkTest" sourceLinkTest @@ -786,6 +798,9 @@ let initTargets () = "DotnetBuild" ==>! "WatchDocs" + "DotnetBuild" + ==>! "RunBenchmarks" + "DotnetTest" ==> "GenerateCoverageReport" ==>! "ShowCoverageReport" diff --git a/Content/Library/docsSrc/index.md b/Content/Library/docsSrc/index.md index bafd4aa4..ada08d01 100644 --- a/Content/Library/docsSrc/index.md +++ b/Content/Library/docsSrc/index.md @@ -57,4 +57,15 @@ I created it because I had to solve an issue with this other thing. +
+
+
+
Benchmarks
+

Performance benchmark results over time.

+
+ +
+
diff --git a/tests/MiniScaffold.Tests/Tests.fs b/tests/MiniScaffold.Tests/Tests.fs index 371469f6..a39ea912 100755 --- a/tests/MiniScaffold.Tests/Tests.fs +++ b/tests/MiniScaffold.Tests/Tests.fs @@ -129,8 +129,15 @@ module Tests = "-n MyCoolLib --githubUsername CoolPersonNo2", [ yield! projectStructureAsserts + Assert.``File exists`` ".github/workflows/benchmark.yml" + Assert.``File exists`` + "benchmarks/MyCoolLib.Benchmarks/MyCoolLib.Benchmarks.fsproj" + Assert.``File exists`` + "benchmarks/MyCoolLib.Benchmarks/Library.Benchmarks.fs" + Assert.``File exists`` "benchmarks/MyCoolLib.Benchmarks/Program.fs" Assert.``project can build target`` "DotnetPack" Assert.``project can build target`` "BuildDocs" + Assert.``project can build target`` "RunBenchmarks" ] @@ -187,6 +194,12 @@ module Tests = "-n fsharp-data-sample --githubUsername CoolPersonNo2", [ yield! projectStructureAsserts + Assert.``File exists`` ".github/workflows/benchmark.yml" + Assert.``File exists`` + "benchmarks/fsharp-data-sample.Benchmarks/fsharp-data-sample.Benchmarks.fsproj" + Assert.``File exists`` + "benchmarks/fsharp-data-sample.Benchmarks/Library.Benchmarks.fs" + Assert.``File exists`` "benchmarks/fsharp-data-sample.Benchmarks/Program.fs" Assert.``project can build target`` "DotnetPack" ] testCase,