Als @rickster vorgeschlagen hatte ich einen Blick auf die erzeugte x86 Montage dieser Datei (simple.swift):
func thingWithClosure(a: Int, b: (() -> Void)?) {
println(a)
b?()
}
thingWithClosure(3) {
println("i'm a closure")
}
thingWithClosure(5, nil)
thingWithClosure(2) {}
Meine Montage ziemlich rostig ist, aber ich kann ein bisschen es ein bisschen schielen. ..
der .main Abschnitt der nicht optimierte x86-Assembler erzeugt oder zumindest ein Teil davon, sieht wie folgt aus:
callq _swift_once
movq __TZvO[email protected]GOTPCREL(%rip), %rax
movq -64(%rbp), %rcx
movq %rcx, (%rax)
leaq l_metadata+16(%rip), %rdi
movl $32, %r9d
movl %r9d, %eax
movl $7, %r9d
movl %r9d, %edx
movq %rax, %rsi
movq %rdx, -80(%rbp)
movq %rax, -88(%rbp)
callq _swift_allocObject
leaq __TF6simpleU_FT_T_(%rip), %rcx
movq %rcx, 16(%rax)
movq $0, 24(%rax)
leaq __TPA__TTRXFo__dT__XFo_iT__iT__(%rip), %rcx
movq %rcx, -16(%rbp)
movq %rax, -8(%rbp)
movq -16(%rbp), %rsi
movl $3, %r9d
movl %r9d, %edi
movq %rax, %rdx
--> callq __TF6simple16thingWithClosureFTSiGSqFT_T___T_
movq $0, -24(%rbp)
movq $0, -32(%rbp)
movl $5, %r9d
movl %r9d, %edi
movq -72(%rbp), %rsi
movq -72(%rbp), %rdx
--> callq __TF6simple16thingWithClosureFTSiGSqFT_T___T_
leaq l_metadata2+16(%rip), %rdi
movq -88(%rbp), %rsi
movq -80(%rbp), %rdx
callq _swift_allocObject
leaq __TF6simpleU0_FT_T_(%rip), %rcx
movq %rcx, 16(%rax)
movq $0, 24(%rax)
leaq __TPA__TTRXFo__dT__XFo_iT__iT__3(%rip), %rcx
movq %rcx, -48(%rbp)
movq %rax, -40(%rbp)
movq -48(%rbp), %rsi
movl $2, %r9d
movl %r9d, %edi
movq %rax, %rdx
--> callq __TF6simple16thingWithClosureFTSiGSqFT_T___T_
xorl %eax, %eax
addq $96, %rsp
popq %rbp
retq
.cfi_endproc
ich habe darauf hingewiesen, wo die Funktion mit 012.391.031 aufgerufen. Wenn Sie sich einige Anweisungen von jeder callq
Anweisung ansehen, können Sie sehen, wo das a
Argument in das r9d
Register verschoben wird.
Auch die optimierte Ausgabe:
callq _swift_once
movq __TZvO[email protected]GOTPCREL(%rip), %rax
movq %r14, (%rax)
movq $3, -24(%rbp)
movq [email protected](%rip), %rbx
addq $8, %rbx
leaq -24(%rbp), %rdi
movq %rbx, %rsi
callq __TFSs7printlnU__FQ_T_
leaq L___unnamed_1(%rip), %rax
movq %rax, -48(%rbp)
movq $13, -40(%rbp)
movq $0, -32(%rbp)
movq [email protected](%rip), %rsi
addq $8, %rsi
leaq -48(%rbp), %rdi
--> callq __TFSs7printlnU__FQ_T_
movq $5, -56(%rbp)
leaq -56(%rbp), %rdi
movq %rbx, %rsi
--> callq __TFSs7printlnU__FQ_T_
movq $2, -64(%rbp)
leaq -64(%rbp), %rdi
movq %rbx, %rsi
--> callq __TFSs7printlnU__FQ_T_
xorl %eax, %eax
addq $48, %rsp
popq %rbx
popq %r14
popq %rbp
retq
.cfi_endproc
Hier hat der Compiler die Funktion inlined, also habe ich stattdessen die println
Anrufe mit -->
hingewiesen.
Ich nahm ein Intro zu x86 Assembly mit einer emulierten 16-Bit-CPU vor Jahren, so werde ich nicht tun, ich weiß genau, was hier vor sich geht, aber es scheint mir, wenn kompiliert mit -O
, der Compiler gibt ungefähr gleichwertigen Code aus (in Bezug auf die Befehlsanzahl, aber möglicherweise nicht im Hinblick auf Speicher-Look-Ups usw.). Es scheint, als wären die Aufrufe an println
mit leaq
(load effektive Adresse) Anweisungen durchsetzt, so dass wir überall springen könnten, aber ich bin mir nicht sicher, wo (könnte mehr Anweisungen sein? Könnte statische Daten laden?), Oder wenn es darauf ankommt.
Die nicht optimierte Version gibt merklich mehr Anweisungen für den nil
-Parameter case, so dass der Hauptunterschied Debug-Leistung sein kann.
Natürlich ist das x86, also kann es auf ARM ganz anders sein .... Vielleicht würden die ARM-Assembly, LLVM IR oder Swift IR-Ausgänge mehr Licht abgeben?
Wenn jemand mit einem besseren Verständnis klären kann, werde ich gerne diese Antwort aktualisieren.
Dunno sicher selbst, aber Sie könnten herausfinden, indem Sie auf die generierte Baugruppe in Xcode. – rickster
Ich bin nicht sicher, dass dies eine gültige Optimierung wäre. 1 ist ein Verschluss, der nichts tut, während 2 keinen Verschluss anzeigt. Kein Abschluss könnte "Standardverhalten verwenden" bedeuten, aber ein leerer Abschluss bedeutet "tue nichts", was nicht unbedingt dasselbe ist. – Ferruccio