Generated functions instead of `fx_string`
leios opened this issue · comments
James Schloss commented
The current method with @invokelatest
adds ~400 ns cost, whereas using an @generated function
we could save this time.
The sticky points are that:
- The user is currently writing multiple function systems that they are combining into a larger Hutchinson operator.
- These functions need to be configured depending on the method used to solve the IFS (random, semi-random, etc), which means they do not write the function that is actually being executed
- The user might specify different symbols / kwargs for different functions and we need to find a way for the generated function to reason about this (maybe just an eval of a namedtuple?)
Other solutions:
- runtimegeneratedfunctions
- https://github.com/JuliaLabs/Poly.jl/blob/aed538b1fa58b9906aacb812bc6618aac0ba4bc2/src/macros.jl#L402 .
- If the user can specify functions that can be called on the GPU, then we can have them specify functions as
f(; a = 1, kwargs...)
so they can be called in the kernels with different kwargs. Modified NamedTuple interface... more info below - Make the macro more powerful to combine multiple function bodies. This can happen if the user can provide a function that is actually called
More info:
julia> k(; a=3, kwargs...) = a
k (generic function with 1 method)
julia> struct FunctionDescriptor{Desc, F}
f::F
FunctionDescriptor{Desc}(f::F) where {Desc, F} = new{Desc, F}(f)
end
julia> f = FunctionDescriptor{(:a,)}(k)
FunctionDescriptor{(:a,), typeof(k)}(k)
julia> function (f::FunctionDescriptor{Desc, F})(;kwargs...) where {F, Desc}
filtered_kwargs = NamedTuple{Desc}(NamedTuple(kwargs))
f.f(;filtered_kwargs...)
end
julia> struct FunctionDescriptor{Desc, KWDesc, F}
f::F
FunctionDescriptor{Desc, KWDesc}(f::F) where {Desc, KWDesc, F} = new{Desc, KWDesc, F}(f)
end
julia> function (f::FunctionDescriptor{Desc, KWDesc, F})(;kwargs...) where {Desc, KWDesc, F}
filtered_kwargs = NamedTuple{KWDesc}(NamedTuple(kwargs))
filtered_args = NamedTuple{Desc}(NamedTuple(kwargs))
f.f(filtered_args...; filtered_kwargs...)
end
julia> function (f::FunctionDescriptor{Desc, KWDesc, F})(;kwargs...) where {Desc, KWDesc, F}
kwargs = NamedTuple(kwargs)
__KWDesc = intersect(keys(kwargs), KWDesc)
filtered_kwargs = NamedTuple{(__KWDesc...,)}(kwargs)
filtered_args = NamedTuple{Desc}(kwargs)
f.f(filtered_args...; filtered_kwargs...)
end
James Schloss commented
Ok, so I think the plan is to:
- re-write all
fums
to just be functions that return a tuple of either xy or rgb. The kernels will then manually set the shared tiles appropriately. - Lift
fid
stuff into the kernel, itself and let users write ((f1, f2), (f3, f4)) and ((f5, f6), f7) as postprocessing operators. Then we just do the "traversal" in the kernel, itself. - use what is written in "more info" above to make sure users can write functions with arbitrary args / kwargs
Note that:
- this will remove
FractalInput
s as that can be dealt with by just using better filtering args / kwargs. - We can probably find a way to refactor Fae to remove
FractalOperator
s as well. - This might fix some of the GPU issues we have been having as well
James Schloss commented
Done in #64