I have some naïve and probably stupid questions about writing efficient
code in Julia.
I almost didn't post this because I'm not sure if this is the right place
for asking for this type of conceptual help. I do hope it is appropriate. I
apologise in advance if it's not.
I have been trying to benchmark some code (using @time) but I'm coming up
against several concepts I would like to understand better, and will no
doubt help to better guide my hitherto "black box" approach.
These concepts are related in what I'm trying to understand, but
understanding them separately might also help me.
- splatting/slurping
- inlining
- tuple access
- function chaining
Not really sure how to present my questions.
Maybe first read through to *Separate Arguments vs Tuple* where all
concepts seem to come into play and there are more direct questions.
*Splatting/Slurping*
I have read that splatting/slurping incurs a penalty.
Is it the splatting or the slurping or both?
I have also read that this is not the case if the function is inlined. Is
this true?
*Inlining*
How do I know if a function is inlined?
When is it necessary to explicitly inline? (say with @inline, or
Exrp(:meta, :inline))
Does this guarantee inlining, or is it just a hint to the compiler?
Is the compiler usually smart enough to inline optimally.
Why wouldn't I explicitly inline all my small functions?
*Overhead of accessing tuples compared to separate variables/arguments*
Is there overhead in accessing tuples vs separate arguments?
Is the expression assigned to x slower than expression assigned to y?
I = (1,2,3)
i1 = 1, i2 = 2, i3 = 3
x = I[1]+I[2]+I[3]
y = i1+i2+i3
*"Chained" function calls *(not sure if chained is the right word)
It seems sometimes I have lots of small "convenience" functions,
effectively chained until a final "working" function is called.
A calls B calls C calls D calls ... calls Z. If A, B, C, ... are small
(maybe getters, convenience functions etc), is this still efficient?
Is there any penalty for this?
How does this relate to inlining?
Presumably there is no penalty if and only if functions are inlined? Is
this true?
If so, is there a limit to the number of levels (functions in call chain)
that can be inlined?
Should I be worried about any of this?
So I guess all this comes together when trying to understand the following:
*Separate Arguments vs Tuple*
I have seen code where there are two forms of a function, one accepts a
tuple, the other accepts separate arguments.
foo(I::Tuple{Vararg{Integer}) = (some code here) -OR- foo{N}(I::NTuple{N,
Integer}) = (some code here)
foo(I::Integer...) = foo(I)
Is calling foo((1,2,3)) more efficient than calling foo(1,2,3)?
It seems foo(1,2,3) requires a slurp and then calls foo((1,2,3)) anyway.
Is there overhead in the slurp?
Is there overhead in the extra call, or will inlining remove the overhead?
If so, how do I know if it will be inlined automatically? Do I need to
explicitly @inline or equivalent?
Does defining foo as a function of a tuple incur tuple access overhead?
Would it be faster to define a "primary" function with separate arguments
and a convenience function with tuple argument?
bar(i1:Integer, i2::Integer, i3::Integer) = (some code here)
bar(I::NTuple{3,Integer) = foo(I...)
Is bar(i1,i2,i3) faster than foo(I) (simply because foo requires tuple
access I[1], I[2],I[3])
Does calling bar(I) incur splatting overhead?
Is there overhead in the extra call, or will inlining remove the overhead?
- Greg