I have julia code which I’ve compiled into a C-library. We intend to run this on a real-time system to test some hardware. When I run a test on the function call in julia using the BenchmarkTools. The @benchmarks look fairly good with 6-us +/- 12us execution times. That is giving me 4%+/-2.2% garbage collection. But the Max time can be 592-us! with 98.17% GC. I’m not sure if that is like the first time it runs or something… very odd.
I’ve done some good whittling at the 4.12 kBs with 68 allocations to try to get rid of the GC all together. I do this by using the module level to hold const Ref{}'s to hold onto reusable mutable structures and databases which are passed into the functions. I have some composite types which I don’t think should allocate, which still end up allocating so I’m trying to understand that as well.
My main question, is one of the data sources that is accessed is a look-up table stored as a 4D array of non-mutable composite types. When this data is accessed, even though it is never written to in the high-rate loop, the garbage collection always runs according to the Profiler flame chart by view_profile().
It is being triggered by line 14 in essentials.jl
eval(:(getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@inline; arrayref($(Expr(:boundscheck)), A, i1, i2, I...))))
Is this expected? I access it 4 to 8 times in one execution and according to the Profiler, that garbage collection on every access, right in a row, is always there.
The access on my end looks something like this:
for i=1:N
val[i]=myfunc(data[i,j,k,f],data[i,j+1,k,f],data[i,j+1,k+1,f],data[i,j,k+1,f],...)
end
Why is that? When I had those direct data accesses wrapped with an inline function, I could see that garbage collection was happening on each one of them equally, now that I have changed to this format, it is the same length of time, so I think the same 4-GCs are running at each function call.
Do array accesses always trigger the garbage collection? Is the Reference hiding something and the garbage collector is confused? Is this expected?
The 5-us average isn’t a big deal, so maybe it isn’t a problem for the GC to do this every single time it looks at an array for reading, but that 500-us hit is what concerns me.
Should I restructure my database so that the first column that is looped, is stored as a static array? The last dimension, is never changed inside this function, should I move the last dimension to be a vector of 2D objects which contain StaticArray’s of Composite types? The database doesn’t change after initial loading.
Any ideas to mitigate that 100x increase spike in the GC?
Also, any info on why composite types allocate off the heap instead of the stack, would be great, or how to profile to be able to see why would be appreciated.
Best regards,
Allan Baker
julia> versioninfo()
Julia Version 1.9.2
Commit e4ee485e90 (2023-07-05 09:39 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: 12 × Intel(R) Core(TM) i7-10850H CPU @ 2.70GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-14.0.6 (ORCJIT, skylake)
Threads: 12 on 12 virtual cores
Environment:
JULIA_DIR = C:\Users\username\AppData\Local\Programs\Julia-1.9.2
JULIA_PKG_DEVDIR = D:\JULIA\DEVELOPMENT
JULIA_PKG_USE_CLI_GIT = true
JULIA_EDITOR = code
JULIA_NUM_THREADS =