docs.unity3d.com
    目次を表示する/隠す

    SkipLocalsInit 属性

    SkipLocalsInitAttribute を使用すると、メソッド内のスタック割り当てをゼロに初期化する必要がないことを Burst に伝えることができます。

    C# では、すべてのローカル変数がデフォルトでゼロに初期化されます。定義されていないデータに関する各種のバグがすべて解消されるので便利ですが、このデータをゼロに初期化するには以下のような処理が必要なので、ランタイムのパフォーマンスに影響する可能性があります。

    static unsafe int DoSomethingWithLUT(int* data);
    
    static unsafe int DoSomething(int size)
    {
        int* data = stackalloc int[size];
    
        // データのすべてのフィールドを初期化して増加していく一連の値にします。
        for (int i = 0; i < size; i++)
        {
            data[i] = i;
        }
    
        // データを他の場所で使用します。
        return DoSomethingWithLUT(data);
    }
    

    これに対応する X86 アセンブリは以下のとおりです。

            push    rbp
                    .seh_pushreg rbp
                    push    rsi
                    .seh_pushreg rsi
                    push    rdi
                    .seh_pushreg rdi
                    mov     rbp, rsp
                    .seh_setframe rbp, 0
                    .seh_endprologue
                    mov     edi, ecx
                    lea     r8d, [4*rdi]
                    lea     rax, [r8 + 15]
                    and     rax, -16
                    movabs  r11, offset __chkstk
                    call    r11
                    sub     rsp, rax
                    mov     rsi, rsp
                    sub     rsp, 32
                    movabs  rax, offset burst.memset.inline.X64_SSE4.i32@@32
                    mov     rcx, rsi
                    xor     edx, edx
                    xor     r9d, r9d
                    call    rax
                    add     rsp, 32
                    test    edi, edi
                    jle     .LBB0_7
                    mov     eax, edi
                    cmp     edi, 8
                    jae     .LBB0_3
                    xor     ecx, ecx
                    jmp     .LBB0_6
            .LBB0_3:
                    mov     ecx, eax
                    and     ecx, -8
                    movabs  rdx, offset __xmm@00000003000000020000000100000000
                    movdqa  xmm0, xmmword ptr [rdx]
                    mov     rdx, rsi
                    add     rdx, 16
                    movabs  rdi, offset __xmm@00000004000000040000000400000004
                    movdqa  xmm1, xmmword ptr [rdi]
                    movabs  rdi, offset __xmm@00000008000000080000000800000008
                    movdqa  xmm2, xmmword ptr [rdi]
                    mov     rdi, rcx
                    .p2align        4, 0x90
            .LBB0_4:
                    movdqa  xmm3, xmm0
                    paddd   xmm3, xmm1
                    movdqu  xmmword ptr [rdx - 16], xmm0
                    movdqu  xmmword ptr [rdx], xmm3
                    paddd   xmm0, xmm2
                    add     rdx, 32
                    add     rdi, -8
                    jne     .LBB0_4
                    cmp     rcx, rax
                    je      .LBB0_7
                    .p2align        4, 0x90
            .LBB0_6:
                    mov     dword ptr [rsi + 4*rcx], ecx
                    inc     rcx
                    cmp     rax, rcx
                    jne     .LBB0_6
            .LBB0_7:
                    sub     rsp, 32
                    movabs  rax, offset "DoSomethingWithLUT"
                    mov     rcx, rsi
                    call    rax
                    nop
                    mov     rsp, rbp
                    pop     rdi
                    pop     rsi
                    pop     rbp
                    ret
    

    この例の movabs rax, offset burst.memset.inline.X64_SSE4.i32@@32 行は、memset を挿入してデータをゼロにする必要があったことを意味しています。上記の例では、その後のループで配列全体が初期化されていることがわかりますが、そのことを Burst は認識していません。

    この問題を修正するには、Unity.Burst.CompilerServices.SkipLocalsInitAttribute を使用して、メソッド内のすべてのスタック割り当てをゼロに初期化する必要がないことを Burst に伝えます。

    Note

    この属性は、定義されていない動作のバグが発生しないことを確信している場合にのみ使用してください。

    以下に例を示します。

    using Unity.Burst.CompilerServices;
    
    static unsafe int DoSomethingWithLUT(int* data);
    
    [SkipLocalsInit]
    static unsafe int DoSomething(int size)
    {
        int* data = stackalloc int[size];
    
        // データのすべてのフィールドを初期化して増加していく一連の値にします。
        for (int i = 0; i < size; i++)
        {
            data[i] = i;
        }
    
        // データを他の場所で使用します。
        return DoSomethingWithLUT(data);
    }
    

    このメソッドに [SkipLocalsInit] を追加した後のアセンブリは、以下のとおりです。

            push    rbp
                    .seh_pushreg rbp
                    mov     rbp, rsp
                    .seh_setframe rbp, 0
                    .seh_endprologue
                    mov     edx, ecx
                    lea     eax, [4*rdx]
                    add     rax, 15
                    and     rax, -16
                    movabs  r11, offset __chkstk
                    call    r11
                    sub     rsp, rax
                    mov     rcx, rsp
                    test    edx, edx
                    jle     .LBB0_7
                    mov     r8d, edx
                    cmp     edx, 8
                    jae     .LBB0_3
                    xor     r10d, r10d
                    jmp     .LBB0_6
            .LBB0_3:
                    mov     r10d, r8d
                    and     r10d, -8
                    movabs  rax, offset __xmm@00000003000000020000000100000000
                    movdqa  xmm0, xmmword ptr [rax]
                    mov     rax, rcx
                    add     rax, 16
                    movabs  rdx, offset __xmm@00000004000000040000000400000004
                    movdqa  xmm1, xmmword ptr [rdx]
                    movabs  rdx, offset __xmm@00000008000000080000000800000008
                    movdqa  xmm2, xmmword ptr [rdx]
                    mov     r9, r10
                    .p2align        4, 0x90
            .LBB0_4:
                    movdqa  xmm3, xmm0
                    paddd   xmm3, xmm1
                    movdqu  xmmword ptr [rax - 16], xmm0
                    movdqu  xmmword ptr [rax], xmm3
                    paddd   xmm0, xmm2
                    add     rax, 32
                    add     r9, -8
                    jne     .LBB0_4
                    cmp     r10, r8
                    je      .LBB0_7
                    .p2align        4, 0x90
            .LBB0_6:
                    mov     dword ptr [rcx + 4*r10], r10d
                    inc     r10
                    cmp     r8, r10
                    jne     .LBB0_6
            .LBB0_7:
                    sub     rsp, 32
                    movabs  rax, offset "DoSomethingWithLUT"
                    call    rax
                    nop
                    mov     rsp, rbp
                    pop     rbp
                    ret
    

    メソッド内のすべてのスタック割り当てをゼロに初期化する必要がないことを Burst に示したので、もう memset の呼び出しは行いません。

    トップに戻る
    Copyright © 2023 Unity Technologies — 商標と利用規約
    • 法律関連
    • プライバシーポリシー
    • クッキー
    • 私の個人情報を販売または共有しない
    • Your Privacy Choices (Cookie Settings)