2017-09-04 5 views
3

Als Referenz ist in C/C++ der entsprechende Operator (sizeof) Kompilierzeit und kann mit Template-Programmierung (Generics) verwendet werden.Ist MemoryLayout <T> .size/stride/Ausrichtung kompilieren Zeit?

Ich war auf der Suche durch Swift Algorithmen Verein für Implementierungen von gemeinsamen Datenstrukturen und kam über ihre Durchführung eines Bit 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> 

In einer Sprache, wo sizeof als Kompilierung konstanten Operator vorhanden ist, würde ich Set N zu sizeof(Word) * 8 oder besser MemoryLayout<UInt64>.size * 8 anstatt die "magische Zahl" 64. Ich gebe zu, es ist nicht sehr Magie hier, aber der Punkt steht, wenn nur nur um es semantisch klar zu machen, was los ist.

Weiter notierte ich die Familie der Funktionen, für die die gleichen Fragen gelten (ref).

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

Bearbeiten: Hinzufügen von etwas Demontage von generischen/nicht generische Version von Funktionen.

Non-generic swift:

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

Erzeugt diese Demontage:

(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 

auf der 0x8 konstant Basierend, das sieht aus wie ein Kompilierung Konstante basierend auf @Charles Srstka Antwort.

Wie wäre es mit der generischen schnellen Implementierung?

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

diese Demontage Produziert:

(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) 

Die oben sieht nicht Zeit kompilieren ...? Ich bin noch nicht vertraut mit Assembler auf Mac/lldb.

Antwort

2

Dieser Code:

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

generiert diese Montage:

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

Daraus geht hervor, dass MemoryLayout<Int64>.size ist in der Tat zusammenstellen Zeit.

Die Überprüfung der Baugruppe für stride und alignment ist dem Leser überlassen, aber sie geben ähnliche Ergebnisse (tatsächlich identisch, im Fall von Int64).

EDIT:

Wenn wir über generische Funktionen sprechen, offensichtlich mehr Arbeit getan werden muss, da die Funktion den Typ nicht wissen, von denen es die Größe bei der Kompilierung bekommen, und kann somit nicht nur setze eine Konstante ein.Aber, wenn Sie Ihre Funktion definieren, nehmen Sie einen Typ statt einer Instanz des Typs, tut es ein bisschen weniger Arbeit als in Ihrem Beispiel:

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

genannt wie: getSizeOf(UInt64.self)

diese Baugruppe erzeugt :

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

Hey danke. Mein Assembler ist im Allgemeinen rostig und ich habe überhaupt nicht mit lldb gespielt. In der "generischen" Version dieses Codes, die ich zu meiner Antwort hinzugefügt habe, ist mir jedoch viel mehr Arbeit aufgefallen. – Josh

+0

Btw, die obige generische Impl Demontage ist die gleiche mit -O0 vs -O3. Ich sehe deinen neuen Zusatz - was du sagst, macht Sinn und ist interessant. In C++ werden sogar templatisierte Versionen von sizeof zur Kompilierzeit berechnet, weil sie * zur Kompilierzeit bekannt ist. Das ist der Punkt einer Vorlagenfunktion in C++, Sie spezialisieren sich für Typ T und behalten Typsicherheit bei. Ich werde mehr darüber nachdenken, warum diese Register in diesem Fall verwendet werden, und Ihre Antwort richtig markieren. – Josh

+0

Bearbeitete die Antwort, um Ihre bearbeitete Frage zu beantworten. –

Verwandte Themen