2016-08-29 4 views
3

die Leistung dieser Refs zu messen, abgeladen ich die Montage von GHC auf dem folgenden Code erzeugt:Erstellung von IOREF und STref

import Data.IORef 

main = do 
    r <- newIORef 18 
    v <- readIORef r 
    print v 

ich die IOREF erwartet entfernt vollständig optimiert werden, nur eine syscall verlassen zu schreibe stdout mit der Zeichenkette "18". Stattdessen bekomme ich 250 Zeilen Assembly. Weißt du wie viele tatsächlich ausgeführt werden? Hier ist, was ich denke, das Herz des Programms ist:

.globl Main.main1_info 
Main.main1_info: 
_c1Zi: 
    leaq -8(%rbp),%rax 
    cmpq %r15,%rax 
    jb _c1Zj 
_c1Zk: 
    movq $block_c1Z9_info,-8(%rbp) 
    movl $Main.main2_closure+1,%ebx 
    addq $-8,%rbp 
    jmp stg_newMutVar# 
_c1Zn: 
    movq $24,904(%r13) 
    jmp stg_gc_unpt_r1 
.align 8 
    .long S1Zo_srt-(block_c1Z9_info)+0 
    .long 0 
    .quad 0 
    .quad 30064771104 
block_c1Z9_info: 
_c1Z9: 
    addq $24,%r12 
    cmpq 856(%r13),%r12 
    ja _c1Zn 
_c1Zm: 
    movq 8(%rbx),%rax 
    movq $sat_s1Z2_info,-16(%r12) 
    movq %rax,(%r12) 
    movl $GHC.Types.True_closure+2,%edi 
    leaq -16(%r12),%rsi 
    movl $GHC.IO.Handle.FD.stdout_closure,%r14d 
    addq $8,%rbp 
    jmp GHC.IO.Handle.Text.hPutStr2_info 
_c1Zj: 
    movl $Main.main1_closure,%ebx 
    jmp *-8(%r13) 

Ich bin besorgt über diese jmp stg_newMutVar#. Es ist nirgendwo in der Versammlung, also vielleicht GHC löst es in einer späteren Verknüpfungsphase. Aber warum ist es überhaupt hier und was macht es? Kann ich die endgültige Baugruppe ohne ungelöste Haskell-Symbole ausgeben?

+3

Ich bezweifle, dass GHC jede relevante Optimierung auf den IO-Referenzen durchführen kann. GCC macht das auf dem äquivalenten "malloc" -Snippet, aber das ist viel mehr idiomatischer Code in C als in Haskell. Das 'stg_' Zeug ist die GHC Laufzeit, es sollte irgendwo in den GHC Quellen in C implementiert werden. Nicht für schwache Nerven. – chi

Antwort

9

Beginnend mit ein paar Links:

Die cmm und C Quellen sind nicht besonders lesbar, wenn Sie nicht bereits vertraut sind mit die macros und primops. Leider kenne ich keine gute Möglichkeit, die Baugruppe zu sehen, die für cmm primops erzeugt wurde, kurz, um in eine ausführbare Datei mit objdump oder einem anderen Disassembler zu schauen.

Noch kann ich die Laufzeit Semantik von IORef zusammenfassen.

IORef ist ein Wrapper um MutVar# von GHC.Prim. Wie das Dokument sagt, ist MutVar# wie ein veränderbares Array mit einem Element. Es nimmt zwei Maschinenwörter auf, das erste ist der Header, das zweite ist der gespeicherte Wert (der ein Zeiger auf ein GHC-Objekt ist). Ein Wert von MutVar# ist selbst ein Zeiger auf dieses Zwei-Wort-Objekt.

MutVar -s unterscheiden sich von normalen unveränderlichen Objekten vor allem durch die Teilnahme an einem Schreibbarrierenmechanismus. GHC hat generationale Speicherbereinigung, so dass alle MutVar, die in einer älteren Generation leben, auch eine GC-Wurzel sein müssen, wenn sie die jüngeren Generationen sammeln, da die Mutation einer MutVar dazu führen kann, dass jüngere Objekte erreichbar werden. Wenn eine MutVar von Generation 0 (die jüngste) heraufgestuft wird, wird es daher zu einer sogenannten "veränderbaren Liste" hinzugefügt, die Verweise auf alle solche veränderbaren Objekte enthält. Die veränderbare Liste wird während GC alter Generationen neu erstellt. Kurz gesagt, MutVar -s in alten Generationen sind immer auf der veränderbaren Liste vorhanden.

Dies ist eine ziemlich einfache Art, mit veränderbaren Variablen umzugehen, und wenn wir eine große Anzahl von ihnen in alten Generationen haben, verlangsamt sich die geringfügige Speicherbereinigung wegen der aufgeblähten veränderbaren Liste und als Ergebnis der entire program slows down.

Da veränderbare Variablen im Produktionscode nicht prominent verwendet werden, gab es nicht viel Nachfrage oder Druck für die Optimierung der RTS für ihre starke Nutzung.

Wenn Sie eine große Anzahl von änderbaren Variablen benötigen, sollten Sie stattdessen einen einzigen veränderbaren Box Array verwenden, denn das ist nur ein einziger Verweis auf der wandelbaren Liste und hat auch ein bitmap-based optimization für GC-Traversal von Elementen, die mutiert worden sein könnten.

Auch, wie Sie sehen newMutVar# ist nur statisch verknüpft, aber nicht inlined, obwohl es ein eher kleines Stück Code ist. Daher ist es auch nicht weg optimiert. Dies ist wiederum weitgehend auf den Mangel an Aufwand und Aufmerksamkeit für die Optimierung von Mutationscode zurückzuführen. Im Gegensatz dazu ist das Zuweisen und Kopieren kleiner primitiver Arrays bekannter Größe derzeit inlined und stark optimiert, weil Johan Tibell, die eine große Menge an Arbeit durchführten, die die unordered-containers-Bibliothek implementierte, dies machte (um unordered-containers schneller zu machen).

+0

Danke, das ist klar. Ich bin hauptsächlich an Vektoralgorithmen interessiert, daher ist es gut zu wissen, dass GHC nur einen IORef für den gesamten Vektor verwendet. Für einen ungeboxten Vektor von Ints liest oder schreibt man eine Zelle, die teurer ist als dieselbe Operation an einem int [] in C? –

+0

@ V.Semeria Unboxed-Arrays (veränderbar oder unveränderlich) haben keine GC-Kosten, da GC nur Verweisen auf Heap-Objekte folgen muss. Unboxed-Array-Operationen haben die gleichen Kosten wie in C. –

+0

Aber beachten Sie, dass unsichere Operationen auf Box-Arrays zu den gleichen Kosten wie in C führen, nur bei veränderbaren Box-Arrays zahlen wir zusätzliche GC-Kosten (unveränderliche Arrays haben den erwähnten GC nicht) Overhead). –