From c8263bd61e1d109d15668cf2c7a4bff9050c73e7 Mon Sep 17 00:00:00 2001 From: Stuart Daines Date: Sun, 1 Dec 2024 18:28:21 +0000 Subject: [PATCH] Test FunctionWrappers for dispatch_methodlist Test code to see if FunctionWrappers can be used to simplify the dispatch_methodlist code. Test with Julia 1.11 and atmospheric photochemistry model suggest all three approaches (dynamic dispatch, @generated dispatch, using FunctionWrappers) are now similar in both compile time and execution speed. Set generated_dispatch=false to run test code Uses a FunctionWrapper containing a MethodCommand to create a type stable dispatch for reaction methods. Test with atmospheric chemistry model shows speed is comparable, small reduction in compile time. --- Project.toml | 2 ++ src/Model.jl | 56 +++++++++++++++++++++++++++++++++++++++-------- src/PALEOboxes.jl | 2 ++ src/Types.jl | 14 +++++++----- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index 98fdc09..82dcb1c 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.21.33" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" @@ -34,6 +35,7 @@ BenchmarkTools = "1.0" DataFrames = "1.1" DocStringExtensions = "0.8, 0.9" Documenter = "1" +FunctionWrappers = "1.1.3" Graphs = "1.4" Infiltrator = "1.0" Interpolations = "0.13, 0.14, 0.15" diff --git a/src/Model.jl b/src/Model.jl index 2f0499d..72e8b47 100644 --- a/src/Model.jl +++ b/src/Model.jl @@ -758,6 +758,38 @@ function do_deriv(dispatchlists, pa::ParameterAggregator, deltat::Float64=0.0) return nothing end +### Test code using FunctionWrappers + +struct MethodCommand{M, V, C} + method::M + vardata::V + cellranges::C +end + +function(mc::MethodCommand)(deltat) + call_method(mc.method[], mc.vardata[], mc.cellranges, deltat) + # call_method(mc.method, mc.vardata, mc.cellranges, deltat) +end + +struct ReactionMethodDispatchListNoGen + # used for test with ParameterAggregator + reactions::Vector{AbstractReaction} + # methodwrappers[i].obj[] to retrieve the wrapped MethodCommand + methodwrappers::Vector{FunctionWrappers.FunctionWrapper{Nothing, Tuple{Float64}}} +end + +function ReactionMethodDispatchListNoGen(methodrefs::Vector, vardatarefs::Vector, cellranges::Vector) + reactions = [m[].reaction for m in methodrefs] + methodwrappers = [ + FunctionWrappers.FunctionWrapper{Nothing, Tuple{Float64}}( + MethodCommand(m, v, c) + ) + for (m, v, c) in zip(methodrefs, vardatarefs, cellranges) + ] + return ReactionMethodDispatchListNoGen(reactions, methodwrappers) +end + + """ dispatch_methodlist(dl::ReactionMethodDispatchList, deltat::Float64=0.0) dispatch_methodlist(dl::ReactionMethodDispatchList, pa::ParameterAggregator, deltat::Float64=0.0) @@ -781,18 +813,22 @@ function dispatch_methodlist( lasti = -1 try - for i in eachindex(dl.methods) + for i in eachindex(dl.methodwrappers) lasti = i - call_method(dl.methods[i], dl.vardatas[i], dl.cellranges[i], deltat) + # call_method(dl.methods[i], dl.vardatas[i], dl.cellranges[i], deltat) + # dl.methodcommands[i](deltat) + dl.methodwrappers[i](deltat) end catch - lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + # lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + lasti != -1 && _dispatch_methodlist_methoderror(dl.methodwrappers[lasti].obj[].method[]) rethrow() end return nothing end +# Untested guess at how to modify for FunctionWrappers function dispatch_methodlist( dl::ReactionMethodDispatchListNoGen, pa::ParameterAggregator, @@ -802,17 +838,19 @@ function dispatch_methodlist( lasti = -1 try - for i in eachindex(dl.methods) + for i in eachindex(dl.methodwrappers) lasti = i - methodref = dl.methods[i] - if has_modified_parameters(pa, methodref) - call_method(methodref, get_parameters(pa, methodref), dl.vardatas[i], dl.cellranges[i], deltat) + if has_modified_parameters(pa, dl.reactions[i]) + methodcommand = dl.methodwrappers[i].obj[] + call_method(methodcommand.method, get_parameters(pa, dl.reactions[i]), methodcommand.vardata, methodcommand.cellranges, deltat) else - call_method(methodref, dl.vardatas[i], dl.cellranges[i], deltat) + # call_method(methodref, dl.vardatas[i], dl.cellranges[i], deltat) + dl.methodwrappers[i](deltat) end end catch - lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + # lasti != -1 && _dispatch_methodlist_methoderror(dl.methods[lasti][]) + lasti != -1 && _dispatch_methodlist_methoderror(dl.methodwrappers[lasti].obj[].method[]) rethrow() end diff --git a/src/PALEOboxes.jl b/src/PALEOboxes.jl index ee3aea6..4bf9029 100644 --- a/src/PALEOboxes.jl +++ b/src/PALEOboxes.jl @@ -31,6 +31,8 @@ import Printf import PrecompileTools import TimerOutputs: @timeit, @timeit_debug +import FunctionWrappers # test code + include("utils/DocStrings.jl") include("Types.jl") diff --git a/src/Types.jl b/src/Types.jl index 334903d..5c19a74 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -186,11 +186,15 @@ end ReactionMethodDispatchList(methods::Vector, vardatas::Vector, cellranges::Vector) = ReactionMethodDispatchList(Tuple(methods), Tuple(vardatas), Tuple(cellranges)) -struct ReactionMethodDispatchListNoGen - methods::Vector - vardatas::Vector - cellranges::Vector -end +# Replaced with test code using FunctionWrappers in Model.jl file +# struct ReactionMethodDispatchListNoGen +# methods::Vector +# vardatas::Vector +# cellranges::Vector +# end + + + # See https://discourse.julialang.org/t/pretty-print-of-type/19555 # Customize typeof function, as full type name is too verbose (as Tuples are of length ~ number of ReactionMethods to call)