2017-06-21 8 views
2

構造体の内部にあるフィールドのバイトオフセットを取得するための小さな関数を書いたが、これを達成するために、シンボルが見つかるまで各フィールドを繰り返し処理している。ただし、Cでは、offsetof()はコンパイル時に1回だけ計算されます。これは、構造体内でオフセットが変更されないためです。私は非常に基本的な機能であるため、可能な限りこの方法を最適化する方法を知っています。各フレーム(3Dゲームエンジン)で頻繁に呼び出す必要があります。offsetof()を一定値に最適化しますか?

function offsetof(type_, member::Symbol) 
    for (i, item) in enumerate(fieldnames(type_)) 
    if item == member 
     return fieldoffset(type_, i) 
    end 
    #print(typeof(i)) 
    end 
    # what to do when symbol not in type_? 
    throw("$type_ has no member named $member") 
end 

使用法:、それは多くのコード

type ABC 
    a::Int64 
    b::Int64 
    c::Int64 
end 
offsetof(ABC, :a) # 0 
offsetof(ABC, :b) # 8 
offsetof(ABC, :c) # 16 
LLVMのコードを見てみると

です:

@code_llvm offsetof(Model_s, :fov) 

出力:

; Function Attrs: uwtable 
define i64 @julia_offsetof_61750(i8**, i8**) #0 !dbg !5 { 
top: 
    %2 = call i8**** @jl_get_ptls_states() #6 
    %3 = alloca [13 x i8**], align 8 
    %.sub = getelementptr inbounds [13 x i8**], [13 x i8**]* %3, i64 0, i64 0 
    %4 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 8 
    %5 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 2 
    %6 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 3 
    %7 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 4 
    %8 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 5 
    %9 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 6 
    %10 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 7 
    %11 = bitcast i8*** %4 to i8* 
    call void @llvm.memset.p0i8.i32(i8* %11, i8 0, i32 40, i32 8, i1 false) 
    %12 = bitcast [13 x i8**]* %3 to i64* 
    %13 = bitcast i8*** %5 to i8* 
    call void @llvm.memset.p0i8.i64(i8* %13, i8 0, i64 40, i32 8, i1 false) 
    store i64 22, i64* %12, align 8 
    %14 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 1 
    %15 = bitcast i8**** %2 to i64* 
    %16 = load i64, i64* %15, align 8 
    %17 = bitcast i8*** %14 to i64* 
    store i64 %16, i64* %17, align 8 
    store i8*** %.sub, i8**** %2, align 8 
    store i8** null, i8*** %10, align 8 
    %18 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 12 
    %19 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 11 
    %20 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 10 
    %21 = getelementptr [13 x i8**], [13 x i8**]* %3, i64 0, i64 9 
    %22 = call i8** @jlsys_fieldnames_43495(i8** inttoptr (i64 375726352 to i8**)) 
    store i8** %22, i8*** %5, align 8 
    %23 = getelementptr inbounds i8*, i8** %22, i64 1 
    %24 = bitcast i8** %23 to i64* 
    %25 = getelementptr i8*, i8** %22, i64 3 
    %26 = bitcast i8** %25 to i64* 
    %27 = bitcast i8** %22 to i8**** 
    %28 = bitcast i8**** %2 to i8* 
    br label %L3 

L3:            ; preds = %cont2, %top 
    %"#temp#1.sroa.4.0" = phi i64 [ 1, %top ], [ %47, %cont2 ] 
    %29 = load i64, i64* %24, align 8 
    %30 = add i64 %29, 1 
    %31 = icmp eq i64 %"#temp#1.sroa.4.0", %30 
    br i1 %31, label %L33, label %if 

if:            ; preds = %L3 
    %32 = add i64 %"#temp#1.sroa.4.0", -1 
    %33 = load i64, i64* %26, align 8 
    %34 = icmp ult i64 %32, %33 
    br i1 %34, label %idxend, label %oob 

L33:            ; preds = %L3 
    store i8** inttoptr (i64 77348880 to i8**), i8*** %4, align 8 
    store i8** inttoptr (i64 77362488 to i8**), i8*** %21, align 8 
    store i8** inttoptr (i64 375726352 to i8**), i8*** %20, align 8 
    store i8** inttoptr (i64 377039808 to i8**), i8*** %19, align 8 
    store i8** %1, i8*** %18, align 8 
    %35 = call i8** @"jsys1_#print_to_string#229_39171"(i8** inttoptr (i64 77362448 to i8**), i8*** %4, i32 5) 
    store i8** %35, i8*** %10, align 8 
    call void @jl_throw(i8** %35) 
    unreachable 

oob:            ; preds = %if 
    %36 = alloca i64, align 8 
    store i64 %"#temp#1.sroa.4.0", i64* %36, align 8 
    call void @jl_bounds_error_ints(i8** %22, i64* nonnull %36, i64 1) 
    unreachable 

idxend:           ; preds = %if 
    %37 = load i8***, i8**** %27, align 8 
    %38 = getelementptr i8**, i8*** %37, i64 %32 
    %39 = load i8**, i8*** %38, align 8 
    %40 = icmp eq i8** %39, null 
    br i1 %40, label %fail, label %cont2 

fail:            ; preds = %idxend 
    call void @jl_throw(i8** inttoptr (i64 84505768 to i8**)) 
    unreachable 

cont2:           ; preds = %idxend 
    store i8** %39, i8*** %6, align 8 
    %41 = call i8** @jl_gc_pool_alloc(i8* %28, i32 1512, i32 32) 
    %42 = getelementptr i8*, i8** %41, i64 -1 
    %43 = bitcast i8** %42 to i8*** 
    store i8** inttoptr (i64 109041296 to i8**), i8*** %43, align 8 
    store i8** %41, i8*** %7, align 8 
    %44 = getelementptr i8*, i8** %41, i64 1 
    %45 = bitcast i8** %44 to i8*** 
    %46 = bitcast i8** %41 to i64* 
    store i64 %"#temp#1.sroa.4.0", i64* %46, align 16 
    store i8** %39, i8*** %45, align 8 
    %47 = add i64 %"#temp#1.sroa.4.0", 1 
    store i8** %39, i8*** %8, align 8 
    store i8** %39, i8*** %9, align 8 
    %48 = icmp eq i8** %39, %1 
    br i1 %48, label %if3, label %L3 

if3:            ; preds = %cont2 
    %sext = shl i64 %"#temp#1.sroa.4.0", 32 
    %49 = ashr exact i64 %sext, 32 
    %50 = icmp eq i64 %49, %"#temp#1.sroa.4.0" 
    br i1 %50, label %pass5, label %fail4 

fail4:           ; preds = %if3 
    call void @jl_throw(i8** inttoptr (i64 77358144 to i8**)) 
    unreachable 

pass5:           ; preds = %if3 
    %51 = trunc i64 %"#temp#1.sroa.4.0" to i32 
    %52 = call i64 inttoptr (i64 1693737504 to i64 (i8**, i32)*)(i8** inttoptr (i64 375726352 to i8**), i32 %51) 
    %53 = load i64, i64* %17, align 8 
    store i64 %53, i64* %15, align 8 
    ret i64 %52 
} 
+0

[生成された機能](https://docs.julialang.org/en/latest/manual/metaprogramming/#Generated-functions-1)のケースです。しかし、私はそれらを使用する経験がありません。 – phg

+0

私は(クロージャを使用して)関数をメモするか、オフセットを含むヘルパー構造体_once_を作成して、代わりにそれを照会します。 (または2つの組み合わせでさえ) –

+1

'Base.fieldindex(ABC、:a)#=> 1'関数があります。 – Gnimuc

答えて

4

これはuse-かもしれ[email protected]表記の場合質問の例では、それは行くだろう:

# note the [email protected] notation at beginning of definition: 

[email protected] function offsetof(type_, member::Symbol) 
    for (i, item) in enumerate(fieldnames(type_)) 
    if item == member 
     return fieldoffset(type_, i) 
    end 
    #print(typeof(i)) 
    end 
    # what to do when symbol not in type_? 
    throw("$type_ has no member named $member") 
end 

今、私たちは次のことができます。

type ABC 
    a::Int64 
    b::Int64 
    c::Int64 
end 
offsetof(ABC, :a) # 0 

そしてoffsetof@code_llvmはまだ長いです。しかし、関数内で使用すると、Juliaはコンパイル時にoffsetofを実行します(これは純粋な関数がそれらのパラメータに依存し、それらから計算された値を返すものではないためです)。例えば:

julia> f() = offsetof(ABC,:b) 
f (generic function with 1 method) 

julia> f() 
0x0000000000000008 

julia> @code_llvm f() 

define i64 @julia_f_63287() #0 !dbg !5 { 
top: 
    %ptls_i8 = call i8* asm "movq %fs:0, $0;\0Aaddq $$-10928, $0", "=r,~{dirflag},~{fpsr},~{flags}"() #1 
    ret i64 8 
} 

f()ちょうどret i64 80x8offsetof(ABC,:b)の値返すから成ることに留意されたいです。ここ

は@pure表記法について説明し、リンクされた:https://github.com/JuliaLang/julia/issues/414
(参照:https://github.com/JuliaLang/julia/issues/14324)を

注意の言葉は、必要である:純粋な表記法は、フラックス中におそらくあり、バージョン間で変更することができます。それは0.6で動作します。将来的には、定数伝播を行うよりスマートなコンパイラを使用して、この最適化をより簡単に行うことができます。

1

これは、手動でフィールドを反復処理するのではなく、インデックスを取得するためにBase.fieldindexを使用することをお勧めします:?

offsetof(type_, member::Symbol) = fieldoffset(type_, Base.fieldindex(type_, member)) 

ヘルプ> Base.fieldindex
FieldIndexに(T、名前::シンボル、ERR:ブール= true)

フィールドが が存在しない場合(err == trueの場合)または0を返した場合(err == falseの場合)、エラーをスローします。

julia> struct Foo 
     x::Int64 
     y::String 
    end 
julia> Base.fieldindex(Foo, :z) 
ERROR: type Foo has no field z 
Stacktrace:  
    [1] fieldindex at ./reflection.jl:319 [inlined](repeats 2 times) 
julia> Base.fieldindex(Foo, :z, false) 
0 

LLVM-IR出力は(ないダウン簡単なのconstへ)まだ少し長いですが、私はそれがすでに元のバージョンに対して十分に簡単だと思う:

define i64 @julia_offsetof_61272(i8**, i8**) #0 !dbg !5 { 
top: 
    %2 = call i32 inttoptr (i64 4332697664 to i32 (i8**, i8**, i32)*)(i8** inttoptr (i64 4713207440 to i8**), i8** %1, i32 1) 
    %3 = sext i32 %2 to i64 
    %4 = add nsw i64 %3, 1 
    %sext = shl i64 %4, 32 
    %5 = ashr exact i64 %sext, 32 
    %6 = icmp eq i64 %5, %4 
    br i1 %6, label %pass2, label %fail1 

fail1:           ; preds = %top 
    call void @jl_throw(i8** inttoptr (i64 4412470696 to i8**)) 
    unreachable 

pass2:           ; preds = %top 
    %7 = trunc i64 %4 to i32 
    %8 = call i64 inttoptr (i64 4332698112 to i64 (i8**, i32)*)(i8** inttoptr (i64 4713207440 to i8**), i32 %7) 
    ret i64 %8 
} 
0

おかげでたくさん

type ABC 
    a::Int64 
    b::Int64 
    c::Int64 
end 

[email protected] offsetof(type_, member::Symbol) = fieldoffset(type_, Base.fieldindex(type_, member)) 

code() = offsetof(ABC, :b) 

@code_llvm code() 

出力します:両方の答えを、私は、彼らが簡単に最速かつ最もコンパクトなバージョンを作るために組み合わせることができる考え出し

; Function Attrs: uwtable 
define i64 @julia_code_61654() #0 !dbg !5 { 
top: 
    ret i64 8 
} 
関連する問題