2017-09-04 82 views
3

作爲參考,在C/C++中,等效(sizeof運算符)是編譯時間,可以與模板編程(泛型)一起使用。是MemoryLayout <T> .size/stride/alignment編譯時間?

我一直在尋找通過SWIFT算法俱樂部常見的數據結構的實現,並在其執行位set的傳來:

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> 

是sizeof存在的編譯時間常數操作語言,我會將N設置爲sizeof(Word) * 8或者更確切地說MemoryLayout<UInt64>.size * 8而不是「幻數」64.我承認,它在這裏並不是非常神奇,但是這一點僅僅是爲了使它在語義上清楚發生了什麼。

此外,我注意到了適用於相同問題的函數族(ref)。

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

編輯:從通用/非通用版本的函數中添加一些反彙編。

非通用快捷:

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

產生以下拆解:

(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 

基礎上不斷0x8中,這看起來像基於@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的情況下)。

編輯:

如果我們談論的通用功能,顯然更多的工作也要做,因爲這個功能不知道的它越來越大小在編譯時的類型,因此不能隨便放在一個常數。但是,如果你定義了你的函數採取而不是類型的實例,它確實少一點的工作比在你的榜樣:

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

順便說一下,上面的通用impl拆卸與-O0和-O3相同。我看到你的新增加 - 你說的話很有道理,很有趣。在C++中,甚至templatized版本的sizeof是在編譯時計算的,因爲它*在編譯時是已知的。這就是C++中模板化函數的重點,您專注於類型T並保留類型安全性。我將更深入地探討爲什麼在這種情況下使用這些寄存器並將您的答案標記爲正確。 – Josh

+0

編輯答案以回答您編輯的問題。 –