On Thu, Sep 22, 2016 at 8:44 AM, Jamie Brandon <ja...@scattered-thoughts.net> wrote: > Well, I managed to answer the first part of my question. A variable won't be > stack-allocated unless the compiler can prove it is always defined before > being used. Mine were, but the control flow was too complex for the compiler > to deal with. I added `local finger = Finger{1}(0,0)` in a few places to > make life easier for the compiler and my allocations went away. > > I'm still interested in the second half of the question though - is there a > better way to debug allocation problems other than guesswork?
For most cases code_warntype should be able to tell you that. The use-before-assign is one of the few cases that's not captured by it. (Other cases are mostly due to the compiler heuristics specializing on types/functions). Ideally, use-before-assign issue should be fixed and avoid having to box the variable. > > On 22 September 2016 at 14:06, Jamie Brandon <ja...@scattered-thoughts.net> > wrote: >> >> Oh, for comparison, this simper function contains no heap allocation at >> all, so the compiler is definitely willing to do this under some >> circumstances. >> >> function foo() >> finger = Finger{1}(1,1) >> for _ in 1:1000 >> finger = Finger{1}(finger.hi + finger.lo, finger.lo) >> end >> finger >> end >> >> On 22 September 2016 at 14:01, Jamie Brandon >> <ja...@scattered-thoughts.net> wrote: >>> >>> I have a query compiler which emits Julia code. The code contains lots of >>> calls to generated functions, which include sections like this: >>> >>> hi = gallop(column, column[finger.lo], finger.lo, finger.hi, <=) >>> Finger{$(C+1)}(finger.lo, hi) >>> >>> Finger is an immutable, isbits type: >>> >>> immutable Finger{C} >>> lo::Int64 >>> hi::Int64 >>> end >>> >>> When I run the generated code I see many millions of allocations. Using >>> code_warntype I can see that all the generated functions have been inlined, >>> every variable has a concrete inferred type and there are no generic calls. >>> And yet I see many sections in the llvm code like this: >>> >>> %235 = call %jl_value_t* @jl_gc_pool_alloc(i8* %ptls_i8, i32 1456, i32 >>> 32) >>> %236 = getelementptr inbounds %jl_value_t, %jl_value_t* %235, i64 -1, >>> i32 0 >>> store %jl_value_t* inttoptr (i64 139661604385936 to %jl_value_t*), >>> %jl_value_t** %236, align 8 >>> %237 = bitcast %jl_value_t* %235 to i64* >>> store i64 %229, i64* %237, align 8 >>> %238 = getelementptr inbounds %jl_value_t, %jl_value_t* %235, i64 1 >>> %239 = bitcast %jl_value_t* %238 to i64* >>> store i64 %234, i64* %239, align 8 >>> store %jl_value_t* %235, %jl_value_t** %finger_2_2, align 8 >>> %.pr = load %jl_value_t*, %jl_value_t** %finger_1_2, align 8 >>> >>> The pointer on the third line is: >>> >>> unsafe_pointer_to_objref(convert(Ptr{Any}, 139661604385936)) >>> # => Data.Finger{1} >>> >>> So it appears that these fingers are still being heap-allocated. >>> >>> What could cause this? And more generally, how does one debug issues like >>> this? Is there any way to introspect on the decision? >> >> >