Μια ταπεινή προσπάθεια κατανόησης του χαλαρού μοντέλου μνήμης της C/C++ Βίκτωρ Βαφειάδης Max Planck Institute for Software Systems (MPI-SWS)
Σημασιολογία των παράλληλων προγραμμάτων Μοντέλα μνήμης (memory model) Ορίζουν τη σημασιολογία της ταυτόχρονης χρήσης μεταβλητών από διαφορετικά νήματα. Σειριακή συμφωνία (sequential consistency) Διαστρωμάτωση (interleaving) των νημάτων Ολική διάταξη των ατομικών πράξεων Χαλαρή συμφωνία (relaxed consistency) Μερική διάταξη των ατομικών πράξεων Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 2/14
Τρόποι κατανόησης των χαλαρών μοντέλων μνήμης 1 Καθαυτός ορισμός του μοντέλου Λειτουργικός (π.χ. x86-tso) Αξιωματικός (π.χ. C/C++) 2 Λειτουργία του μεταγλωττιστή (compiler) Μετατροπές που μπορεί να επιφέρει 3 Παραδείγματα προγραμμάτων 4 Μέθοδοι επαλήθευσης προγραμμάτων Γενικά θεωρήματα Προγραμματικές λογικές (program logics) Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 3/14
Το μοντέλο μνήμης x86-tso cpu 1... cpu n write read... write-back Μνήμη Αρχικά x = y = 0. x := 1; print(y); y := 1; print(x); Ο εικονιζόμενος κώδικας μπορεί να τυπώσει 00. Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 4/14
Το μοντέλο μνήμης της C11 Δύο τύποι μεταβλητών: κανονικές και ατομικές Ταυτόχρονη προσπέλαση κανονικής μεταβλητής (race) σφάλμα προγράμματος Διάφορα είδη ατομικών προσπελάσεων Relaxed no fence Consume reads no fence Release writes no fence (x86); lwsync(power) Acquire reads no fence (x86); isync(power) Seq. consistent full memory fence Εξήγηση των acquire/release προσπελάσεων Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 5/14
Αποστολή μηνύματος στη C11 atomic_int x = 0; int a = 0; ( a = 7; if (x.load(acquire) == 1) ) x.store(1, release); print(a); W na (a, 7) sb W rel (x, 1) sb sb W na (x, 0) sb W na (a, 0) rf, sw rf sb R acq (x, 1) sb R na (a,?) sb happens-before def = (sequenced-before sync-with) + sync-with(a, b) def = reads-from(b) = a release(a) acquire(b) Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 6/14
Data race freedom Ολα τα φυσιολογικά μοντέλα μνήμης έχουν την ιδιότητα DRF: Theorem (DRF-property) Αν Prg SC δεν περιέχει data races, τότε Prg Relaxed = Prg SC. Προγραμματικές λογικές που αποκλείουν data races είναι προφανώς ορθές. Δυστυχώς το θεώρημα αυτό δεν ισχύει στη C11 Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 7/14
Διαχωριστική λογική (separation logic) Ιδιοκτησία (ownership) {P} C {Q} Για την προσπέλαση μιας κανονικής διεύθυνσης μνήμης, πρέπει να μας ανήκει: {l v} [l] na := v {l v } {l v} x := [l] na {l v x = v} Η ιδιοκτησία είναι αποκλειστική: {P 1 } C 1 {Q 1 } {P 2 } C 2 {Q 2 } {P 1 P 2 } C 1 C 2 {Q 1 Q 2 } Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 8/14
Χαλαρή διαχωριστική λογική (relaxed separation logic) Επιτρέπεται η μεταβίβαση ιδιοκτησίας μέσω συγχρονισμού acq/rel. Atomic allocation επιλογή invariant Q {true} x := alloc() {Wri(x, Q) Read(x, Q)} Release write παράδοση ιδιοκτησίας {Q(v) Wri(l, Q)} [l] rel := v {true} Acquire read απόκτηση ιδιοκτησίας {Read(l, Q)} x := [l] acq {Q(x)} Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 9/14
Τεχνικές δυσκολίες Δεν έχουμε λειτουργική σημασιολογία = Βασιζόμαστε στην αξιωματική σημασιολογία Λείπει η συνήθης απόλυτη έννοια του χρόνου και της κατάστασης (state) = Ορίζουμε μια λογική έννοια κατάστασης = Αναγράφουμε λογικές καταστάσεις στις ακμές hb {P} C {Q} Ορθαπόδειξη στην Coq Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 10/14
Σφάλματα στο τυπικό μοντέλο της C11 1 Κύκλοι εξάρτησης (dependency cycles) 2 Ο συγχρονισμός release & acquire παραείναι χαλαρός...... όταν έχουμε αναγνώσεις consume 3 Παράξενη σημασιολογία των SC accesses...... έχουμε και μη-sc accesses 4 Ακολουθίες αποστολής (release sequences) πολύ μικρές Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 11/14
Κύκλοι εξάρτησης (Dependency cycles) Αρχικά x = y = 0. if (x.load(rlx) == 1) y.store(1, rlx); if (y.load(rlx) == 1) x.store(1, rlx); Η C11 επιτρέπει το αποτέλεσμα x = y = 1. Non-relational invariants are unsound. x = 0 y = 0 Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 12/14
Rel-acq παραείναι χαλαρός (τη παρουσία consume) Αρχικά x = y = 0. a = 1; x.store(1, release); while(x.read(consume) 1); y.store(1, release); ( ) while(y.load(acquire) 1); ( ) a = 2; Η C θεωρεί τις δυο εγγραφές του a ταυτόχρονες. Αλλά δεν είναι Στα x86-tso, Power/ARM, και Itanium. Η αν μεταφέρουμε τις ( ) γραμμές σε νέο νήμα. Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 13/14
Release sequences too strong (relaxed writes) Αρχικά x = y = 0. a = 1; x.store(1, release); x.store(3, relaxed); ( ) x.store(2, relaxed); while(x.read(acquire) 3); a = 2; Η C θεωρεί τις δυο εγγραφές του a ταυτόχρονες. Αλλά αν αφαιρέσουμε την γραμμή ( ), τότε οι δυο εγγραφές δεν θεωρούνται πλέον ταυτόχρονες. Βίκτωρ Βαφειάδης Το χαλαρό μοντέλο μνήμης της C/C++ 14/14