Skip to content

Commit 3b304d0

Browse files
authored
Merge pull request #133 from vchuravy/vc/backends
Use preferences to switch between SIMD and KernelAbstractions
2 parents 0874150 + 7da0526 commit 3b304d0

File tree

7 files changed

+77
-30
lines changed

7 files changed

+77
-30
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@ jobs:
2626
fail-fast: false
2727
matrix:
2828
version:
29-
- '1.10'
29+
- '1.11'
3030
os:
3131
- ubuntu-latest
3232
- macOS-latest
3333
- windows-latest
3434
arch:
3535
- x64
36-
- x86
3736
nthreads:
3837
- '1'
3938
- 'auto'
@@ -56,10 +55,18 @@ jobs:
5655
${{ runner.os }}-test-${{ env.cache-name }}-
5756
${{ runner.os }}-test-
5857
${{ runner.os }}-
59-
- uses: julia-actions/julia-buildpkg@v1
60-
- uses: julia-actions/julia-runtest@v1
58+
- name: WaterLily tests
6159
env:
6260
JULIA_NUM_THREADS: ${{ matrix.nthreads }}
61+
shell: bash
62+
run: |
63+
if [ "${{ matrix.nthreads }}" = "auto" ]
64+
then
65+
printf "[WaterLily]\nbackend = \"KernelAbstractions\"" > LocalPreferences.toml
66+
else
67+
printf "[WaterLily]\nbackend = \"SIMD\"" > LocalPreferences.toml
68+
fi
69+
julia --proj --color=yes -e "using Pkg; Pkg.instantiate(); Pkg.test(; coverage=true);"
6370
- uses: julia-actions/julia-processcoverage@v1
6471
- uses: codecov/codecov-action@v5
6572
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.vscode/
33
.DS_Store
44
Manifest.toml
5+
LocalPreferences.toml
56
/docs/build/
67

78
# gfx

Project.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1010
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
1111
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1212
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
13+
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
1314
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
15+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1416
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1517
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1618
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
@@ -19,8 +21,8 @@ TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
1921
[weakdeps]
2022
AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"
2123
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
22-
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
2324
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
25+
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
2426
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
2527
Meshing = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73"
2628
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
@@ -43,6 +45,7 @@ EllipsisNotation = "1.8"
4345
ForwardDiff = "^0.10.18"
4446
KernelAbstractions = "0.9.1"
4547
LoggingExtras = "1.1"
48+
Random = "1.11.0"
4649
Reexport = "^1.2.2"
4750
Requires = "1.3"
4851
StaticArrays = "^1.1.0"

src/WaterLily.jl

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module WaterLily
66
using DocStringExtensions
77

88
include("util.jl")
9-
export L₂,BC!,@inside,inside,δ,apply!,loc,@log
9+
export L₂,BC!,@inside,inside,δ,apply!,loc,@log,set_backend,backend
1010

1111
using Reexport
1212
@reexport using KernelAbstractions: @kernel,@index,get_backend
@@ -166,15 +166,21 @@ export viz!, get_body, plot_body_obs!
166166

167167
# Check number of threads when loading WaterLily
168168
"""
169-
check_nthreads(::Val{1})
169+
check_nthreads()
170170
171171
Check the number of threads available for the Julia session that loads WaterLily.
172-
A warning is shown when running in serial (`JULIA_NUM_THREADS=1`).
173-
"""
174-
check_nthreads(::Val{1}) = @warn("\nUsing WaterLily in serial (ie. JULIA_NUM_THREADS=1) is not recommended because \
175-
it disables the GPU backend and defaults to serial CPU."*
176-
"\nUse JULIA_NUM_THREADS=auto, or any number of threads greater than 1, to allow multi-threading in CPU or GPU backends.")
177-
check_nthreads(_) = nothing
172+
A warning is shown when running in serial (JULIA_NUM_THREADS=1) with KernelAbstractions enabled.
173+
"""
174+
function check_nthreads()
175+
if backend == "KernelAbstractions" && Threads.nthreads() == 1
176+
@warn """
177+
Using WaterLily in serial (ie. JULIA_NUM_THREADS=1) is not recommended because it defaults to serial CPU execution.
178+
Use JULIA_NUM_THREADS=auto, or any number of threads greater than 1, to allow multi-threading in CPU backends.
179+
For a low-overhead single-threaded CPU only backend set: WaterLily.set_backend("SIMD")
180+
"""
181+
end
182+
end
183+
check_nthreads()
178184

179185
# Backward compatibility for extensions
180186
if !isdefined(Base, :get_extension)
@@ -191,7 +197,6 @@ function __init__()
191197
@require Meshing = "e6723b4c-ebff-59f1-b4b7-d97aa5274f73" include("../ext/WaterLilyMeshingExt.jl")
192198
@require JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" include("../ext/WaterLilyJLD2Ext.jl")
193199
end
194-
check_nthreads(Val{Threads.nthreads()}())
195200
end
196201

197202
end # module

src/util.jl

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ macro inside(ex)
8989
end |> esc
9090
end
9191

92+
# Could also use ScopedValues in Julia 1.11+
93+
using Preferences
94+
const backend = @load_preference("backend", "KernelAbstractions")
95+
function set_backend(new_backend::String)
96+
if !(new_backend in ("SIMD", "KernelAbstractions"))
97+
throw(ArgumentError("Invalid backend: \"$(new_backend)\""))
98+
end
99+
100+
# Set it in our runtime values, as well as saving it to disk
101+
@set_preferences!("backend" => new_backend)
102+
@info("New backend set; restart your Julia session for this change to take effect!")
103+
end
104+
92105
"""
93106
@loop <expr> over <I ∈ R>
94107
@@ -118,26 +131,34 @@ Note that `get_backend` is used on the _first_ variable in `expr` (`a` in this e
118131
"""
119132
macro loop(args...)
120133
ex,_,itr = args
121-
_,I,R = itr.args; sym = []
134+
_,I,R = itr.args
135+
sym = []
122136
grab!(sym,ex) # get arguments and replace composites in `ex`
123137
setdiff!(sym,[I]) # don't want to pass I as an argument
138+
symT = symtypes(sym) # generate a list of types for each symbol
124139
@gensym(kern, kern_) # generate unique kernel function names for serial and KA execution
125-
return quote
126-
function $kern($(rep.(sym)...),::Val{1})
127-
@simd for $I $R
140+
@static if backend == "KernelAbstractions"
141+
return quote
142+
@kernel function $kern_($(rep.(sym)...),@Const(I0)) # replace composite arguments
143+
$I = @index(Global,Cartesian)
144+
$I += I0
128145
@fastmath @inbounds $ex
129146
end
130-
end
131-
@kernel function $kern_($(rep.(sym)...),@Const(I0)) # replace composite arguments
132-
$I = @index(Global,Cartesian)
133-
$I += I0
134-
@fastmath @inbounds $ex
135-
end
136-
function $kern($(rep.(sym)...),_)
137-
$kern_(get_backend($(sym[1])),64)($(sym...),$R[1]-oneunit($R[1]),ndrange=size($R))
138-
end
139-
$kern($(sym...),Val{Threads.nthreads()}()) # dispatch to SIMD for -t 1, or KA otherwise
140-
end |> esc
147+
function $kern($(joinsymtype(rep.(sym),symT)...)) where {$(symT...)}
148+
$kern_(get_backend($(sym[1])),64)($(sym...),$R[1]-oneunit($R[1]),ndrange=size($R))
149+
end
150+
$kern($(sym...))
151+
end |> esc
152+
else # backend == "SIMD"
153+
return quote
154+
function $kern($(joinsymtype(rep.(sym),symT)...)) where {$(symT...)}
155+
@simd for $I $R
156+
@fastmath @inbounds $ex
157+
end
158+
end
159+
$kern($(sym...))
160+
end |> esc
161+
end
141162
end
142163
function grab!(sym,ex::Expr)
143164
ex.head == :. && return union!(sym,[ex]) # grab composite name and return
@@ -149,6 +170,10 @@ grab!(sym,ex::Symbol) = union!(sym,[ex]) # grab symbol name
149170
grab!(sym,ex) = nothing
150171
rep(ex) = ex
151172
rep(ex::Expr) = ex.head == :. ? Symbol(ex.args[2].value) : ex
173+
using Random
174+
symtypes(sym) = [Symbol.(Random.randstring('A':'Z',4)) for _ in 1:length(sym)]
175+
joinsymtype(sym::Symbol,symT::Symbol) = Expr(:(::), sym, symT)
176+
joinsymtype(sym,symT) = zip(sym,symT) .|> x->joinsymtype(x...)
152177

153178
using StaticArrays
154179
"""

test/alloctest.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using BenchmarkTools, Printf
22

3+
backend != "SIMD" && throw(ArgumentError("KernelAbstractions backend not allowed to run allocations tests, use SIMD backend"))
34
@testset "mom_step! allocations" begin
45
function Sim(θ;L=32,U=1,Re=100,perdir=())
56
function map(x,t)

test/maintests.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using GPUArrays
2-
using ReadVTK, WriteVTK, JLD2
2+
using ReadVTK, WriteVTK, JLD2, Random
33

4+
backend != "KernelAbstractions" && throw(ArgumentError("SIMD backend not allowed to run main tests, use KernelAbstractions backend"))
45
@info "Test backends: $(join(arrays,", "))"
56
@testset "util.jl" begin
67
I = CartesianIndex(1,2,3,4)
@@ -17,6 +18,10 @@ using ReadVTK, WriteVTK, JLD2
1718
WaterLily.grab!(sym,ex)
1819
@test ex == :(a[I, i] = Math.add(b[I], func(I, q)))
1920
@test sym == [:a, :I, :i, :(p.b), :q]
21+
sym = [:a,:b,:c]
22+
Random.seed!(99)
23+
symT = WaterLily.symtypes(sym)
24+
@test WaterLily.joinsymtype(sym,symT) == Expr[:(a::MDFZ), :(b::AXYU), :(c::RFIB)]
2025

2126
for f arrays
2227
p = zeros(4,5) |> f

0 commit comments

Comments
 (0)