2017-09-04 5 views
3

C/C++では、同等のもの(sizeofオペレータ)がコンパイル時に使用され、テンプレートプログラミング(Generics)で使用できます。MemoryLayout <T> .size/stride/alignmentはコンパイル時間ですか?

私は、共通のデータ構造の実装のためにスウィフトのアルゴリズムクラブを通して見ていると、ビットセットのその実施に出くわした:はsizeofはコンパイル時定数演算子として存在している言語で

public struct BitSet { 
    private(set) public var size: Int 

    private let N = 64 
    public typealias Word = UInt64 
    fileprivate(set) public var words: [Word] 

    public init(size: Int) { 
    precondition(size > 0) 
    self.size = size 

    // Round up the count to the next multiple of 64. 
    let n = (size + (N-1))/N 
    words = [Word](repeating: 0, count: n) 
    } 

    <clipped> 

、私が持っているでしょうNsizeof(Word) * 8に設定するか、「magic number」64ではなくMemoryLayout<UInt64>.size * 8に設定します。ここではそれほど魅力的ではありませんが、意味が分かりやすくするためのものです。

さらに、私は、同じ質問が適用される関数ファミリに気付いた(ref)。

static func size(ofValue value: T) -> Int 
static func stride(ofValue value: T) -> Int 
static func alignment(ofValue value: T) -> Int 

編集:ジェネリック/汎用ではないバージョンの関数から逆アセンブリを追加します。

非ジェネリックSWIFT:

func getSizeOfInt() -> Int { 
    return MemoryLayout<UInt64>.size 
} 

この分解を生成:0x8の定数に基づいて

(lldb) disassemble --frame 
MemoryLayout`getSizeOfInt() -> Int: 
    0x1000013c0 <+0>: pushq %rbp 
    0x1000013c1 <+1>: movq %rsp, %rbp 
    0x1000013c4 <+4>: movl $0x8, %eax 
-> 0x1000013c9 <+9>: popq %rbp 
    0x1000013ca <+10>: retq 

、これは@Charles Srstkaの回答に基づいてコンパイル時定数のように見えます。

一般的な迅速な実装はどうですか?この分解プロデュース

func getSizeOf<T>(_ t:T) -> Int { 
    return MemoryLayout<T>.size 
} 

(lldb) disassemble --frame 
MemoryLayout`getSizeOf<A> (A) -> Int: 
    0x100001390 <+0>: pushq %rbp 
    0x100001391 <+1>: movq %rsp, %rbp 
    0x100001394 <+4>: subq $0x20, %rsp 
    0x100001398 <+8>: movq %rsi, -0x8(%rbp) 
    0x10000139c <+12>: movq %rdi, -0x10(%rbp) 
-> 0x1000013a0 <+16>: movq -0x8(%rsi), %rax 
    0x1000013a4 <+20>: movq 0x88(%rax), %rcx 
    0x1000013ab <+27>: movq %rcx, -0x18(%rbp) 
    0x1000013af <+31>: callq *0x20(%rax) 
    0x1000013b2 <+34>: movq -0x18(%rbp), %rax 
    0x1000013b6 <+38>: addq $0x20, %rsp 
    0x1000013ba <+42>: popq %rbp 
    0x1000013bb <+43>: retq 
    0x1000013bc <+44>: nopl (%rax) 

上記のコンパイル時間を見ていませんか...?私はまだmac/lldbのアセンブラには馴染みがない。

答えて

2

このコード:

func getSizeOfInt64() -> Int { 
    return MemoryLayout<Int64>.size 
} 

は、このアセンブリを生成します。このことから

MyApp`getSizeOfInt64(): 
    0x1000015a0 <+0>: pushq %rbp 
    0x1000015a1 <+1>: movq %rsp, %rbp 
    0x1000015a4 <+4>: movl $0x8, %eax 
    0x1000015a9 <+9>: popq %rbp 
    0x1000015aa <+10>: retq 

MemoryLayout<Int64>.sizeは、コンパイル時に実際にあることが表示されます。

stridealignmentのアセンブリを確認しても、同様の結果が得られます(Int64の場合は実際と同じです)。

EDIT:私たちは、一般的な機能について話している場合は、明らかに、より多くの作業を行う必要があります

関数はコンパイル時にサイズを取得していたの種類を知っていないので、単にできないだろうので、定数を入れる。以下のように呼ばれる

func getSizeOf<T>(_: T.Type) -> Int { 
    return MemoryLayout<T>.size 
} 

:あなたの代わりにタイプのインスタンスの種類を取るためにあなたの関数を定義した場合でも、それはあなたの例よりも少し作業を行いgetSizeOf(UInt64.self)

は、このアセンブリを生成し、 :

MyApp`getSizeOf<A>(_:): 
    0x100001590 <+0>: pushq %rbp 
    0x100001591 <+1>: movq %rsp, %rbp 
    0x100001594 <+4>: movq %rsi, -0x8(%rbp) 
    0x100001598 <+8>: movq %rdi, -0x10(%rbp) 
-> 0x10000159c <+12>: movq -0x8(%rsi), %rsi 
    0x1000015a0 <+16>: movq 0x88(%rsi), %rax 
    0x1000015a7 <+23>: popq %rbp 
    0x1000015a8 <+24>: retq 
+0

おかげで感謝します。私のアセンブラは一般的には錆びていて、私はlldbと全く遊んでいません。しかし、このコードの「汎用」バージョンでは、さらに多くの作業が減っていることに気付きました。これを私の答えに追加しました。 – Josh

+0

Btwの場合、上記の一般的なimplの逆アセンブリは-O0と-O3で同じです。私はあなたの新しい追加を見ます - あなたが言うことは理にかなって面白いです。 C++では、sizeofのテンプレート化されたバージョンでさえ、コンパイル時に計算される*ため、コンパイル時に計算されます。それはC++のテンプレート関数のポイントなので、T型を専門とし、型の安全性を保持します。私はなぜこれらのレジスタがこの場合に使用されているかを詳しく見て、あなたの答えを正しいものにします。 – Josh

+0

編集した質問に回答するための回答を編集しました。 –

関連する問題