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?

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?
>>
>
>

Reply via email to