From 1fc8d5a12a1fd917bc31c8f76e12223b9d805789 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Fri, 21 Oct 2022 23:26:00 -0300 Subject: [PATCH 1/7] Add benchmarks --- benchmark/Project.toml | 5 +++++ benchmark/benchmarks.jl | 9 +++++++++ benchmark/suites/mnist.jl | 26 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 benchmark/Project.toml create mode 100644 benchmark/benchmarks.jl create mode 100644 benchmark/suites/mnist.jl diff --git a/benchmark/Project.toml b/benchmark/Project.toml new file mode 100644 index 0000000..6e9202a --- /dev/null +++ b/benchmark/Project.toml @@ -0,0 +1,5 @@ +[deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +MLDatasets = "eb30cadb-4394-5ae3-aed4-317e484a6458" +PkgBenchmark = "32113eaa-f34f-5b0d-bd6c-c81e245fc73d" +WiSARD = "2b71b7a6-5a26-4a05-9b8d-679d5aa759d4" diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl new file mode 100644 index 0000000..a317308 --- /dev/null +++ b/benchmark/benchmarks.jl @@ -0,0 +1,9 @@ +using BenchmarkTools +using WiSARD + +const SUITE = BenchmarkGroup() + +# -*- MNIST -*- # +include("suites/mnist.jl") + +mnist_benchmark!(SUITE) \ No newline at end of file diff --git a/benchmark/suites/mnist.jl b/benchmark/suites/mnist.jl new file mode 100644 index 0000000..291f33e --- /dev/null +++ b/benchmark/suites/mnist.jl @@ -0,0 +1,26 @@ +function mnist_benchmark!(SUITE::BenchmarkGroup) + mnist_wnn = () -> WiSARD.WNN{Int,UInt}(28, 28) + + SUITE["mnist"] = BenchmarkGroup(["MNIST"]) + + mnist_trainset = MNIST(Int, :train) + mnist_train_x = [mnist_trainset[i][:features] for i = 1:60_000] + mnist_train_y = [mnist_trainset[i][:targets] for i = 1:60_000] + + SUITE["mnist"]["train"] = @benchmarkable WiSARD.train!.( + $mnist_wnn(), + $mnist_train_x, + $mnist_train_y, + ) + + mnist_testset = MNIST(Int, :test) + mnist_test_x = [mnist_testset[i][:features] for i = 1:10_000] + # mnist_test_y = [mnist_testset[i][:targets] for i = 1:10_000] + + SUITE["mnist"]["test"] = @benchmarkable WiSARD.classify.( + $mnist_wnn(), + $mnist_test_x, + ) + + return nothing +end \ No newline at end of file From 93ef9a39f64f7b0b70f54aead3a815cab6d1556e Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Fri, 21 Oct 2022 23:42:47 -0300 Subject: [PATCH 2/7] Add benchmark action --- .github/workflows/ci.yml | 1 + benchmark/.gitignore | 5 +++++ benchmark/benchmarks.jl | 1 + benchmark/runbenchmarks.jl | 20 ++++++++++++++++++++ benchmark/suites/mnist.jl | 4 ++-- src/model/model.jl | 4 ++-- 6 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 benchmark/.gitignore create mode 100644 benchmark/runbenchmarks.jl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcb04c9..3ad1bc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: arch: ${{ matrix.arch }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 + - run: julia --project=./benchmark --color=yes ./benchmark/runbenchmarks.jl # Future: Add Code Cov # - uses: julia-actions/julia-processcoverage@v1 # - uses: codecov/codecov-action@v1 diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 0000000..ec6fd6d --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1,5 @@ +# Benchmark results +results.json + +# Benchmark Tunings +tune.json \ No newline at end of file diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index a317308..78ec134 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -1,5 +1,6 @@ using BenchmarkTools using WiSARD +using MLDatasets const SUITE = BenchmarkGroup() diff --git a/benchmark/runbenchmarks.jl b/benchmark/runbenchmarks.jl new file mode 100644 index 0000000..7363aac --- /dev/null +++ b/benchmark/runbenchmarks.jl @@ -0,0 +1,20 @@ +using Pkg + +Pkg.add(path=@__DIR__) +Pkg.instantiate() + +using WiSARD +using PkgBenchmark + +const RESULTS_PATH = joinpath(@__DIR__, "results") +const RESULTS_FILE = joinpath(RESULTS_PATH, "results.json") + +function main() + results = benchmarkpkg(WiSARD) + + writeresults(RESULTS_FILE, results) + + return nothing +end + +main() # Here we go! \ No newline at end of file diff --git a/benchmark/suites/mnist.jl b/benchmark/suites/mnist.jl index 291f33e..bbb1f00 100644 --- a/benchmark/suites/mnist.jl +++ b/benchmark/suites/mnist.jl @@ -3,7 +3,7 @@ function mnist_benchmark!(SUITE::BenchmarkGroup) SUITE["mnist"] = BenchmarkGroup(["MNIST"]) - mnist_trainset = MNIST(Int, :train) + mnist_trainset = MLDatasets.MNIST(Int, :train) mnist_train_x = [mnist_trainset[i][:features] for i = 1:60_000] mnist_train_y = [mnist_trainset[i][:targets] for i = 1:60_000] @@ -13,7 +13,7 @@ function mnist_benchmark!(SUITE::BenchmarkGroup) $mnist_train_y, ) - mnist_testset = MNIST(Int, :test) + mnist_testset = MLDatasets.MNIST(Int, :test) mnist_test_x = [mnist_testset[i][:features] for i = 1:10_000] # mnist_test_y = [mnist_testset[i][:targets] for i = 1:10_000] diff --git a/src/model/model.jl b/src/model/model.jl index 78c06cd..ac87cdf 100644 --- a/src/model/model.jl +++ b/src/model/model.jl @@ -93,8 +93,8 @@ function train!(wnn::WNN{S,T}, x::AbstractArray, y::S) where {S,T} c = wnn.cls[y] for i = 1:wnn.n - k = address(wnn, x, i) - @inbounds c[i, k] = get(c, (i, k), 0) + 1 + ω = WNNKEY{T}(i, address(wnn, x, i)) + @inbounds c[ω] = get(c, ω, 0) + 1 end nothing From 09ad1136c4a9b0d3df2624442fbb4e62ededb97b Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Fri, 21 Oct 2022 23:44:47 -0300 Subject: [PATCH 3/7] Update actions --- .github/workflows/ci.yml | 3 ++- benchmark/runbenchmarks.jl | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ad1bc3..2553eb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,8 @@ jobs: arch: ${{ matrix.arch }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - - run: julia --project=./benchmark --color=yes ./benchmark/runbenchmarks.jl + - run: julia --project=benchmark --color=yes -e 'import Pkg; Pkg.add(path=@__DIR__); Pkg.instantiate()' + - run: julia --project=benchmark --color=yes ./benchmark/runbenchmarks.jl # Future: Add Code Cov # - uses: julia-actions/julia-processcoverage@v1 # - uses: codecov/codecov-action@v1 diff --git a/benchmark/runbenchmarks.jl b/benchmark/runbenchmarks.jl index 7363aac..4ad1929 100644 --- a/benchmark/runbenchmarks.jl +++ b/benchmark/runbenchmarks.jl @@ -1,8 +1,3 @@ -using Pkg - -Pkg.add(path=@__DIR__) -Pkg.instantiate() - using WiSARD using PkgBenchmark From 2d898e4f69fdd27a51210042cf7f980158fa123e Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Sat, 22 Oct 2022 02:19:44 -0300 Subject: [PATCH 4/7] Add benchmarks --- benchmark/runbenchmarks.jl | 4 ++ src/WiSARD.jl | 3 +- src/images/images.jl | 47 ++++++++--------- src/interface.jl | 17 ++++++ src/model/model.jl | 105 +++++++++++++++++++++---------------- test/unit/address.jl | 12 +++++ test/unit/unit.jl | 2 + 7 files changed, 117 insertions(+), 73 deletions(-) create mode 100644 src/interface.jl create mode 100644 test/unit/address.jl diff --git a/benchmark/runbenchmarks.jl b/benchmark/runbenchmarks.jl index 4ad1929..59df5a3 100644 --- a/benchmark/runbenchmarks.jl +++ b/benchmark/runbenchmarks.jl @@ -1,3 +1,7 @@ +import Pkg +Pkg.add(path=joinpath(@__DIR__, "..")) +Pkg.instantiate() + using WiSARD using PkgBenchmark diff --git a/src/WiSARD.jl b/src/WiSARD.jl index eeb1383..da3c66e 100644 --- a/src/WiSARD.jl +++ b/src/WiSARD.jl @@ -2,10 +2,9 @@ module WiSARD using Random +include("interface.jl") include("model/model.jl") - include("encoding/encoding.jl") - include("images/images.jl") end # module diff --git a/src/images/images.jl b/src/images/images.jl index 2390849..82e749e 100644 --- a/src/images/images.jl +++ b/src/images/images.jl @@ -1,14 +1,14 @@ -function address(wnn::WNN{S, T}, k::T) where {S, T} +function address(wnn::WNN{S,T}, k::T) where {S,T} [j for j = 1:wnn.d if (k >> (j - 1)) % 2 == 1] end function images( N::Type{<:Any}, - wnn::WNN{S, T}, + wnn::WNN{S,T}, y::S; - w::Union{Int, Nothing} = nothing, - h::Union{Int, Nothing} = nothing, - ) where {S, T} + w::Union{Int,Nothing} = nothing, + h::Union{Int,Nothing} = nothing, +) where {S,T<:WNNINT} w, h = if isnothing(w) && isnothing(h) 1, wnn.n * wnn.d elseif isnothing(w) || isnothing(h) @@ -23,42 +23,39 @@ function images( M = 0 for i = 1:wnn.n + δ = wnn.n * (i - 1) for (k, v) in cls[i] for j in address(wnn, k) - M = max(M, img[wnn.map[(i - 1) * wnn.n + j]] += v) + M = max(M, img[wnn.map[δ+j]] += v) end end end - return if M == 0 - N[img[(i - 1) * w + j] for i=1:h, j=1:w] + if iszero(M) + return reshape(img, h, w) else - N[img[(i - 1) * w + j] / M for i=1:h, j=1:w] + return reshape(img, h, w) / M end end function images( - wnn::WNN{S, T}, + wnn::WNN{S,T}, y::S; - w::Union{Int, Nothing} = nothing, - h::Union{Int, Nothing} = nothing, - ) where{S, T} - images(Float64, wnn, y; w=w, h=h) + w::Union{Int,Nothing} = nothing, + h::Union{Int,Nothing} = nothing, +) where {S,T} + images(Float64, wnn, y; w = w, h = h) end function images( N::Type{<:Any}, - wnn::WNN{S, T}; - w::Union{Int, Nothing} = nothing, - h::Union{Int, Nothing} = nothing, - ) where {S, T} - Dict{S, Array{N}}(y => images(N, wnn, y; w=w, h=h) for y in keys(wnn.cls)) + wnn::WNN{S,T}; + w::Union{Int,Nothing} = nothing, + h::Union{Int,Nothing} = nothing, +) where {S,T} + Dict{S,Array{N}}(y => images(N, wnn, y; w = w, h = h) for y in keys(wnn.cls)) end -function images( - wnn::WNN; - w::Union{Int, Nothing} = nothing, - h::Union{Int, Nothing} = nothing, - ) - images(Float64, wnn; w=w, h=h) +function images(wnn::WNN; w::Union{Int,Nothing} = nothing, h::Union{Int,Nothing} = nothing) + images(Float64, wnn; w = w, h = h) end \ No newline at end of file diff --git a/src/interface.jl b/src/interface.jl new file mode 100644 index 0000000..ce79d85 --- /dev/null +++ b/src/interface.jl @@ -0,0 +1,17 @@ +@doc raw""" +""" function train! end + +@doc raw""" +""" function classhint! end + +@doc raw""" +""" function classify end + +@doc raw""" +""" function address end + +@doc raw""" +""" function images end + +@doc raw""" +""" function deaddress end \ No newline at end of file diff --git a/src/model/model.jl b/src/model/model.jl index ac87cdf..defda36 100644 --- a/src/model/model.jl +++ b/src/model/model.jl @@ -1,12 +1,16 @@ export WNN, train!, classify -struct WNNKEY{T<:Union{Unsigned,BigInt}} +const WNNINT = Union{Unsigned,BigInt} + +struct WNNKEY{T<:WNNINT} i::Int k::T - WNNKEY{T}(i::Int, k::T) where {T} = new{T}(i, k) + WNNKEY{T}(i::Int, k::T) where {T<:WNNINT} = new{T}(i, k) end +const WNNCLS{T<:WNNINT} = Dict{WNNKEY{T},Int} + @doc raw""" WNN{S, T}(d::Int, n::Int; seed::Union{Int, Nothing}=nothing) where {S <: Any, T <: Union{Unsigned, BigInt}} WNN{T}(d::Int, n::Int; seed::Union{Int, Nothing}=nothing) where {T <: Union{Unsigned, BigInt}} @@ -14,53 +18,52 @@ end ## References: * [1] Carvalho, Danilo & Carneiro, Hugo & França, Felipe & Lima, Priscila. (2013). B-bleaching : Agile Overtraining Avoidance in the WiSARD Weightless Neural Classifier. """ -struct WNN{S<:Any,T<:Union{Unsigned,BigInt}} +struct WNN{S<:Any,T<:WNNINT} d::Int n::Int - cls::Dict{S,Dict{WNNKEY{T},Int}} + cls::Dict{S,WNNCLS{T}} map::Vector{Int} - function WNN{S,T}( - d::Int, - n::Int; - seed::Union{Int,Nothing} = nothing, - ) where {S<:Any,T<:Union{Unsigned,BigInt}} + function WNN{S,T}(d::Integer, n::Integer, map::Vector{Int}) where {S,T<:WNNINT} if n <= 0 || d <= 0 error("Values for 'd' and 'n' must be positive") end if T !== BigInt && d > (T.size * 8) - error("'$T' is insufficient to guarantee 'd' addressing bits") - end - - if seed === nothing - seed = trunc(Int, time()) + error("'$T' is insufficient to provide 'd' addressing bits") end - rng = Random.MersenneTwister(seed) + cls = Dict{S,WNNCLS{T}}() - new{S,T}(d, n, Dict{S,Dict{WNNKEY{T},Int}}(), Random.shuffle(rng, 1:n*d)) + return new{S,T}(d, n, cls, map) end - function WNN{T}( + function WNN{S,T}( d::Int, n::Int; - seed::Union{Int,Nothing} = nothing, - ) where {T<:Union{Unsigned,BigInt}} - WNN{Symbol,T}(d, n; seed = seed) - end + seed::Union{Integer,Nothing} = nothing, + ) where {S<:Any,T<:Union{Unsigned,BigInt}} + if isnothing(seed) + seed = trunc(Int, time()) + end + + rng = Random.MersenneTwister(seed) + map = Random.shuffle(rng, 1:n*d) + WNN{S,T}(d, n, map) + end end +WNN{S}(args...; kws...) where {S} = WNN{S,UInt}(args...; kws...) +WNN(args...; kws...) = WNN{Any}(args...; kws...) + Base.isempty(wnn::WNN) = all(isempty.(values(wnn.cls))) -# function Base.empty!(wnn::WNN) -# for cls in values(wnn.cls) -# empty!.(cls) -# end +function Base.empty!(wnn::WNN) + empty!(wnn.cls) -# return wnn -# end + return wnn +end Base.show(io::IO, wnn::WNN{S,T}) where {S<:Any,T<:BigInt} = print(io, "WNN[∞ bits, $(wnn.d) × $(wnn.n)]") @@ -69,15 +72,15 @@ Base.show(io::IO, wnn::WNN{S,T}) where {S<:Any,T<:Unsigned} = Base.Broadcast.broadcastable(wnn::WNN) = Ref(wnn) -function address(wnn::WNN{<:Any,T}, x::AbstractArray, i::Int) where {T} - s = zero(T) +@inline function address(wnn::WNN{<:Any,T}, x::AbstractArray{<:Integer}, i::Int) where {T} + k = zero(T) + δ = wnn.d * (i - 1) - for j = 1:wnn.d - k = @inbounds wnn.map[(i-1)*wnn.d+j] - s = s + @inbounds (x[k] > 0) | (1 << (j - 1)) + @simd for j = 1:wnn.d + k += ifelse(!iszero(@inbounds x[wnn.map[δ+j]]), one(T) << (j - 1), zero(T)) end - return s + return WNNKEY{T}(i, k) end @doc raw""" @@ -85,19 +88,20 @@ end Train model with a single pair (class `x`, sample `y`) """ -function train!(wnn::WNN{S,T}, x::AbstractArray, y::S) where {S,T} +@inline function train!(wnn::WNN{S,T}, x::AbstractArray{<:Integer}, y::S) where {S,T} if !haskey(wnn.cls, y) wnn.cls[y] = Dict{WNNKEY{T},Int}() end c = wnn.cls[y] - for i = 1:wnn.n - ω = WNNKEY{T}(i, address(wnn, x, i)) + @simd for i = 1:wnn.n + ω = address(wnn, x, i) + @inbounds c[ω] = get(c, ω, 0) + 1 end - nothing + return nothing end @doc raw""" @@ -105,13 +109,13 @@ end Classifies input `y` returning some label `x`. If no training happened, `nothing` will be returned instead. """ -function classify(wnn::WNN, x::AbstractArray; bleach = 0::Int, gamma = 0.5::Float64) - +function classify(wnn::WNN, x::AbstractArray{<:Integer}; bleach = 0::Int, gamma = 0.5::Float64) r₁ = r₂ = 0 y₁ = y₂ = nothing for y₀ in keys(wnn.cls) r₀ = rate(wnn, y₀, x) + if r₀ >= r₁ r₁, r₂ = r₀, r₁ y₁, y₂ = y₀, y₁ @@ -128,8 +132,8 @@ function classify(wnn::WNN, x::AbstractArray; bleach = 0::Int, gamma = 0.5::Floa if γ >= gamma return y₁ else - r₁ = rate(wnn, y₁, x; bleach = bleach) - r₂ = rate(wnn, y₂, x; bleach = bleach) + r₁ = rate(wnn, y₁, x, bleach) + r₂ = rate(wnn, y₂, x, bleach) return r₁ > r₂ ? y₁ : y₂ end @@ -139,20 +143,29 @@ end function rate( wnn::WNN{S,T}, y::Union{S,Nothing}, - x::AbstractArray; - bleach = 0::Int, + x::AbstractArray, + bleach::Integer = 0, ) where {S,T} return if !haskey(wnn.cls, y) - 0.0 + return 0.0 else c = wnn.cls[y] s = 0.0 for i = 1:wnn.n - z = @inbounds get(c, (i, address(wnn, x, i)), 0) - s = z > bleach ? s + 1.0 : s + s += ifelse((get(c, address(wnn, x, i), 0) > bleach), 1.0, 0.0) end return s end end + +function classhint!(wnn::WNN{S,T}, keys::S...) where {S,T} + for key in keys + if !haskey(wnn.cls, key) + wnn.cls = WNNCLS{T}() + end + end + + return nothing +end \ No newline at end of file diff --git a/test/unit/address.jl b/test/unit/address.jl new file mode 100644 index 0000000..9be8790 --- /dev/null +++ b/test/unit/address.jl @@ -0,0 +1,12 @@ +function test_address() + map = collect(1:9) + wnn = WiSARD.WNN{Symbol}(3, 3, map) + + x = [1, 0, 0, 1, 0, 1, 0, 1, 0] + + @test WiSARD.address(wnn, x, 1) == UInt(1) + @test WiSARD.address(wnn, x, 2) == UInt(5) + @test WiSARD.address(wnn, x, 3) == UInt(2) + + return nothing +end \ No newline at end of file diff --git a/test/unit/unit.jl b/test/unit/unit.jl index eb87d21..64c38f7 100644 --- a/test/unit/unit.jl +++ b/test/unit/unit.jl @@ -1,8 +1,10 @@ +include("address.jl") include("encodings.jl") include("broadcast.jl") function test_unit() @testset "Unit tests" verbose=true begin + test_address() test_encodings() test_broadcast() end From 260f6943738e96841882ea3043108d6019aef321 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Sat, 22 Oct 2022 02:37:21 -0300 Subject: [PATCH 5/7] Fix test --- test/unit/address.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/address.jl b/test/unit/address.jl index 9be8790..ced974b 100644 --- a/test/unit/address.jl +++ b/test/unit/address.jl @@ -4,9 +4,9 @@ function test_address() x = [1, 0, 0, 1, 0, 1, 0, 1, 0] - @test WiSARD.address(wnn, x, 1) == UInt(1) - @test WiSARD.address(wnn, x, 2) == UInt(5) - @test WiSARD.address(wnn, x, 3) == UInt(2) + @test WiSARD.address(wnn, x, 1) == WiSARD.WNNKEY{UInt}(1, UInt(1)) + @test WiSARD.address(wnn, x, 2) == WiSARD.WNNKEY{UInt}(2, UInt(5)) + @test WiSARD.address(wnn, x, 3) == WiSARD.WNNKEY{UInt}(3, UInt(2)) return nothing end \ No newline at end of file From 81bd5d2d3ea40a52b94c42e8a2973248df687c4e Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Tue, 25 Oct 2022 13:39:26 -0300 Subject: [PATCH 6/7] Bump Version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4ef55bc..fd63819 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "WiSARD" uuid = "2b71b7a6-5a26-4a05-9b8d-679d5aa759d4" authors = ["pedromxavier "] -version = "0.2.0" +version = "0.3.0" [deps] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" From c94995aa8994a1b49ffa64ee7a8645973876a92f Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier Date: Tue, 25 Oct 2022 13:45:39 -0300 Subject: [PATCH 7/7] Remove benchmark from CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2553eb4..2d44c80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ jobs: arch: ${{ matrix.arch }} - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - - run: julia --project=benchmark --color=yes -e 'import Pkg; Pkg.add(path=@__DIR__); Pkg.instantiate()' - - run: julia --project=benchmark --color=yes ./benchmark/runbenchmarks.jl + # - run: julia --project=benchmark --color=yes -e 'import Pkg; Pkg.add(path=@__DIR__); Pkg.instantiate()' + # - run: julia --project=benchmark --color=yes ./benchmark/runbenchmarks.jl # Future: Add Code Cov # - uses: julia-actions/julia-processcoverage@v1 # - uses: codecov/codecov-action@v1