I don’t understand your use case well enough to give recommendations specific to your package, but here are a few things you can play around with.
1
Code that looks like
if haskey(d, k)
return d[k]
end
is not as fast as it could be, because two dictionary lookups are performed: one for haskey(d, k)
and one for d[k]
. You can get away with only one lookup by using get
. Here’s a benchmark:
using BenchmarkTools
function foo(d, k)
foreach(1:1000) do _
if haskey(d, k)
d[k]
end
end
end
function bar(d, k)
foreach(1:1000) do _
get(d, k, nothing)
end
end
d = Dict(:a => 1, :b => 2)
julia> @btime foo($d, :a);
12.001 μs (0 allocations: 0 bytes)
julia> @btime bar($d, :a);
5.867 μs (0 allocations: 0 bytes)
So your version could look something like this:
f = get(d, k, nothing)
if f !== nothing
return f(args...)
end
2
It’s sort of been hinted at in this thread, but I don’t think it’s been explicitly mentioned:
The type of a function is simply typeof(foo)
, so every function has it’s own type, even if the input and output types are ostensibly the same:
julia> foo() = 1;
julia> typeof(foo)
typeof(foo)
This means you can’t have a type stable dictionary who’s values are functions.
3
I don’t know how performant this would be, but you could potentially play around with types parameterized by Int
s, or use Val
types. Here’s an example:
struct Momentum{A, B} end
function Momentum(A, B)
Momentum{A, B}()
end
repulsion(::Momentum{0, 0}) = 1
repulsion(::Momentum{1, 0}) = 2
repulsion(::Momentum{0, 1}) = 3
julia> repulsion(Momentum(0, 0))
1
julia> repulsion(Momentum(1, 0))
2
julia> repulsion(Momentum(0, 1))
3
4
Your README says that one of the goals of your package is to provide “simple, easily understandable methods”. If that’s the case, then I would advise against using macros. Macros tend to be a bit of a black box and add a whole new syntax to learn. (Although it’s often best to follow the rule of thumb that the user should be able to predict the non-macro code that a macro will expand to.)
As you’re aware from watching the metaprogramming video from @stevengj, 99% of the time you should be able to achieve performance without using code generation. Macros are just for introducing new syntax (99% of the time). As I mentioned above, I don’t really know your domain, but instead of code generation maybe you can achieve what you need with higher-order functions.
5
This is just a shot in the dark, but it’s also possible that generated functions might suit your needs.