Reputation: 10982
Basically, I want to provide some type information in a struct A
that will be used later to define the return type of the foo_1
function.
Here is the Julia code I have so far:
struct A
type::DataType
end
foo_1(a::A)::a.type = 2018
Usage example:
julia> a=A(Float64)
julia> foo_1(a)
2018.0
-> This work as expected: 2018 is converted into a.type
type, here a Float64
However, my problem/interrogation come from that fact that, when compared with the function foo_2
that do basically the same thing without using x.type
foo_2(a::A)::Float64 = 2018
there is an important difference in the generated code:
julia> @code_native foo_2(a)
.text
Filename: REPL[4]
pushq %rbp
movq %rsp, %rbp
movabsq $139943184603960, %rax # imm = 0x7F470FCE9B38
Source line: 1
movsd (%rax), %xmm0 # xmm0 = mem[0],zero
popq %rbp
retq
nopw %cs:(%rax,%rax)
-> This is OK
However for foo_1(a::A)::a.type
function:
julia> @code_native foo_1(a)
.text
Filename: REPL[3]
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %rbx
subq $56, %rsp
movq %rdi, %r14
movq %fs:0, %r15
addq $-10888, %r15 # imm = 0xD578
leaq -48(%rbp), %rdi
xorps %xmm0, %xmm0
movups %xmm0, -48(%rbp)
movq $0, -32(%rbp)
movq $8, -72(%rbp)
movq (%r15), %rax
movq %rax, -64(%rbp)
leaq -72(%rbp), %rax
movq %rax, (%r15)
movq $0, -56(%rbp)
Source line: 1
movq (%r14), %rax
movabsq $139943153507008, %rcx # imm = 0x7F470DF41AC0
leaq 634701288(%rcx), %rdx
movq %rdx, -48(%rbp)
movq %rax, -40(%rbp)
movq %rcx, -32(%rbp)
movabsq $jl_apply_generic, %rax
movl $3, %esi
callq *%rax
movq %rax, %rbx
movq %rbx, -56(%rbp)
movq (%r14), %rsi
movabsq $jl_typeassert, %rax
movq %rbx, %rdi
callq *%rax
movq -64(%rbp), %rax
movq %rax, (%r15)
movq %rbx, %rax
addq $56, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
-> I am surprised that Julia JIT is not able to generate an optimized function. IMHO it has all the required information at the call site foo_1(a)
to do so.
--> I fear that the difference in generated code is so big that I will get bad performance if I use the approach with a.type
.
So my questions are:
1/ is there a better Julia way to define type information (I am thinking of typedef
C++ equivalent)
2/ how to explain the generated code difference between foo_1(a) and foo_2(a)
-> My explanation so far: I think that like foo_1 is not a parametric function Julia generates only one instance of the function and hence it is obliged to dynamically resolves the type (even if struct A is immutable). Am I right?
Note: I could have written
struct B{T}
end
foo_3{T}(b::B{T})::T = 2018
That works (in the sense that native code of foo_3
is the same as foo_2
), however this is an awkward solution to define several types:
struct B{T1,T2,T3,...}
end
C++ equivalent would be, without "parasite" template parameters:
struct B {
using T1 = ...;
using T2 = ...;
using T3 = ...;
...
};
Upvotes: 2
Views: 480
Reputation: 31362
The key is simply that Julia doesn't know what type A.type
is beyond the fact that it's just some DataType
. You can use a parametric type to provide this information:
julia> struct A{T}
typ::Type{T}
end
foo_1(a::A)::a.typ = 2018
foo_1 (generic function with 1 method)
julia> foo_1(A(Float64))
2018.0
julia> @code_native foo_1(A(Float64))
.section __TEXT,__text,regular,pure_instructions
; Function foo_1 {
; Location: REPL[9]:5
movabsq $4572864984, %rax ## imm = 0x1109061D8
movsd (%rax), %xmm0 ## xmm0 = mem[0],zero
retq
nop
;}
Upvotes: 2