C 2010 Δημιουργώντας κώδικα για ολιστική επεξεργασία επερωτήσεων enerating code for holistic query evaluation Κωνσταντίνος Κρικέλλας α Στρατής Δ. Βίγλας β Marcelo Cintra β α reenplum nc. β chool of nformatics niversity of dinburgh M 2010, Αγία Νάπα
Περίληψη Περιγραφή του προβλήματος Ολιστική επεξεργασία επερωτήσεων Πειραματική μελέτη Συμπεράσματα Κρικέλλας, Βίγλας, Cintra M 2010
Εκτελώντας μία επερώτηση Πλάνο εκτέλεσης μετά από βελτιστοποίηση Συνήθως δυαδικό Επαναλήπτες (iterators) για επικοινωνία μεταξύ τελεστών Δυνατότητα σύνθεσης τελεστών Μετάδοση κλήσεων μεταξύ τελεστών Από ρίζα προς φύλλα Για κάθε επικοινωνία, μία κλήση συνάρτησης Για κάθε κλήση συνάρτησης ο επεργαστής σώζει την κατάσταση στη στοίβα Η κλήση συναρτήσεων σημαίνει μη ακολουθιακή πρόσβαση στα δεδομένα Ανεξαρτησία από τύπους δεδομένων σημαίνει ελαφρώς πολυπλοκότερες συγκρίσεις (π.χ., memcmp() αντί για <, >, =) Ανεξαρτησία από υλικό όχι πάντα καλή εγγραφές κλήσεις open() get_next() close() open() get_next() close() open() get_next() close() Κρικέλλας, Βίγλας, Cintra M 2010
Τι έχει γίνει ως τώρα Προ-μεταγλώττιση κατηγορημάτων ανάλογα με τον τύπο δεδομένων Καμία γενίκευση, αλλά και πάλι κλήση συνάρτησης Αυτή τη φορά σε επίπεδο κατηγορήματος, όχι σε επίπεδο τελεστή Όχι βέλτιστο, κι όχι τόσο επεκτάσιμο Αλλαγή των δομών δεδομένων/αποθήκευσης προκειμένου να γίνουν φιλικές στην cache (ertical partitioning, PAX, C-store, Monet,...) Αλλαγές και σε άλλες διαστάσεις του συστήματος (π.χ., concurrency control, επαναφορά, διαχείριση μνήμης) Χρήση reflection code (M) Δουλεύει, αλλά μόνο σε μία virtual machine και μόνο στην κυρίως μνήμη Όχι κλιμακούμενο Δύσκολη υλοποίηση και όχι γενικεύσιμο Κρικέλλας, Βίγλας, Cintra M 2010
Μία διαφορετική οπτική γωνία Αλλαγή της ροής του κώδικα εκτέλεσης με τρόπο που να εκμεταλλεύεται την ιεραρχία μνήμης πάνω από πρωτογεννή δεδομένα Αλλά αυτό δε γίνεται με γενικό και συγκεκριμένο για τύπους κώδικα, εκτός κι αν κάποιος τον δημιουργήσει δυναμικά ΟΚ. Γιατί όχι; Ίδια μέθοδος αποθήκευσης Για την ακρίβεια, ο,τιδήποτε δεν έχει να κάνει με εκτέλεση μένει ίδιο Αντί να υλοποιούμε ένα γενικό κομμάτι κώδικα, ας υλοποιήσουμε ένα πρότυπο κώδικα Σύνθεση και αρχικοποίηση των προτύπων ανάλογα με την επερώτηση Μετάφραση και επεξεργασία Φύλαξη του δημιουργημένου κώδικα Κρικέλλας, Βίγλας, Cintra M 2010
Q - olistic ntegrated Qery ngine Query esults ront-end chemas Parser valuator Pages yntax tree Library file tatistics ptimizer Linker Types Plan enerator Code inary Compiler Preparator xecutor Catalog tables uffer pool torage manager Κρικέλλας, Βίγλας, Cintra M 2010
Πλεονεκτήματα Με τη δημιουργία κώδικα, το μοντέλο εκτέλεσης δε χρειάζεται να είναι γενικό Πληροφορία για τύπους δεδομένων η οποία χρησιμοποιείται κατά τη μετάφραση Καμία κλήση συνάρτησης Πρότυπα αλγορίθμων με γνώση της cache και φιλικά προς τον επεξεργαστή Ελαχιστοποίηση σφαλμάτων cache Ελαχιστοποίηση του αριθμού των φορών αλληλεπίδρασης με την κυρίως μνήμη Οι αλγόριθμοι δεν αλλάζουν την / συμπεριφορά τους, μόνο τη ροή τους Η επεξεργασία επερωτήσεων γίνεται μία σειρά από εμφωλιασμένους βρόγχους πάνω από δεδομένα που βρίσκονται στην cache Κρικέλλας, Βίγλας, Cintra M 2010
Παράδειγμα (hash-join) /* υπόθεση: τα table_1 και table_2 έχουν χωρισθεί σε M υποσύνολα του ιδιοχαρακτηριστικού ζεύξης */ int *t1, int *t2; for (k = 0; k < M; k++) { for (p_1 = start_page_1[k]; p_1 <= end_page_1[k]; p_1++) { page_struct *page_1 = read_page(p_1, partition_1[k]); for (p_2 = start_page_2[k]; p_2 <= end_page_2[k]; p_2++) { page_struct *page_2 = read_page(p_2, partition_2[k]); for (t_1 = 1; t_1 <= page_1->num_tuples; t_1++) { void *tuple_1 = page_1->data + t_1 * tuple_size_1; for (t_2 = 1; t_2 <= page_2->num_tuples; t_2++) { void *tuple_2 = page_2->data + t_2 * tuple_size_2; t1 = tuple_1 + offset_1; t2 = tuple_2 + offset_2; if (*t1!= *t2) continue; add_to_result(tuple_1, tuple_2); }}}}} Κρικέλλας, Βίγλας, Cintra M 2010
Παράδειγμα (hash-join) /* υπόθεση: τα table_1 και table_2 έχουν χωρισθεί σε M υποσύνολα του ιδιοχαρακτηριστικού ζεύξης */ int *t1, int *t2; κώδικας με γνώση τύπων for (k = 0; k < M; k++) { for (p_1 = start_page_1[k]; p_1 <= end_page_1[k]; p_1++) { page_struct *page_1 = read_page(p_1, partition_1[k]); for (p_2 = start_page_2[k]; p_2 <= end_page_2[k]; p_2++) { page_struct *page_2 = read_page(p_2, partition_2[k]); for (t_1 = 1; t_1 <= page_1->num_tuples; t_1++) { void *tuple_1 = page_1->data + t_1 * tuple_size_1; for (t_2 = 1; t_2 <= page_2->num_tuples; t_2++) { void *tuple_2 = page_2->data + t_2 * tuple_size_2; t1 = tuple_1 + offset_1; t2 = tuple_2 + offset_2; if (*t1!= *t2) continue; add_to_result(tuple_1, tuple_2); }}}}} Κρικέλλας, Βίγλας, Cintra M 2010
Παράδειγμα (hash-join) /* υπόθεση: τα table_1 και table_2 έχουν χωρισθεί σε M υποσύνολα του ιδιοχαρακτηριστικού ζεύξης */ int *t1, int *t2; κώδικας με γνώση τύπων for (k = 0; k < M; k++) { for (p_1 = start_page_1[k]; p_1 <= end_page_1[k]; p_1++) { page_struct *page_1 = read_page(p_1, partition_1[k]); for (p_2 = start_page_2[k]; p_2 <= end_page_2[k]; p_2++) { page_struct *page_2 = read_page(p_2, partition_2[k]); for (t_1 = 1; t_1 <= page_1->num_tuples; t_1++) { void *tuple_1 = page_1->data + t_1 * tuple_size_1; for (t_2 = 1; t_2 <= page_2->num_tuples; t_2++) { void *tuple_2 = page_2->data + t_2 * tuple_size_2; t1 = tuple_1 + offset_1; t2 = tuple_2 + offset_2; if (*t1!= *t2) continue; add_to_result(tuple_1, tuple_2); }}}}} επανάληψη πάνω από σελίδες Κρικέλλας, Βίγλας, Cintra M 2010
Παράδειγμα (hash-join) /* υπόθεση: τα table_1 και table_2 έχουν χωρισθεί σε M υποσύνολα του ιδιοχαρακτηριστικού ζεύξης */ int *t1, int *t2; κώδικας με γνώση τύπων for (k = 0; k < M; k++) { for (p_1 = start_page_1[k]; p_1 <= end_page_1[k]; p_1++) { page_struct *page_1 = read_page(p_1, partition_1[k]); for (p_2 = start_page_2[k]; p_2 <= end_page_2[k]; p_2++) { page_struct *page_2 = read_page(p_2, partition_2[k]); for (t_1 = 1; t_1 <= page_1->num_tuples; t_1++) { void *tuple_1 = page_1->data + t_1 * tuple_size_1; for (t_2 = 1; t_2 <= page_2->num_tuples; t_2++) { void *tuple_2 = page_2->data + t_2 * tuple_size_2; t1 = tuple_1 + offset_1; t2 = tuple_2 + offset_2; if (*t1!= *t2) continue; add_to_result(tuple_1, tuple_2); }}}}} επανάληψη πάνω από σελίδες επανάληψη πάνω από τις εγγραφές των σελίδων Κρικέλλας, Βίγλας, Cintra M 2010
Παράδειγμα (hash-join) /* υπόθεση: τα table_1 και table_2 έχουν χωρισθεί σε M υποσύνολα του ιδιοχαρακτηριστικού ζεύξης */ int *t1, int *t2; κώδικας με γνώση τύπων for (k = 0; k < M; k++) { for (p_1 = start_page_1[k]; p_1 <= end_page_1[k]; p_1++) { page_struct *page_1 = read_page(p_1, partition_1[k]); for (p_2 = start_page_2[k]; p_2 <= end_page_2[k]; p_2++) { page_struct *page_2 = read_page(p_2, partition_2[k]); for (t_1 = 1; t_1 <= page_1->num_tuples; t_1++) { void *tuple_1 = page_1->data + t_1 * tuple_size_1; for (t_2 = 1; t_2 <= page_2->num_tuples; t_2++) { void *tuple_2 = page_2->data + t_2 * tuple_size_2; t1 = tuple_1 + offset_1; t2 = tuple_2 + offset_2; if (*t1!= *t2) continue; σταθερά διαστήματα add_to_result(tuple_1, tuple_2); }}}}} επανάληψη πάνω από σελίδες επανάληψη πάνω από τις εγγραφές των σελίδων Κρικέλλας, Βίγλας, Cintra M 2010
Πειραματική πλατφόρμα ntel Core 2 uo @ 1.86z, 2 μνήμης, Linux kernel, 2.6.27 TPC- και Wisconsin enchmarks (με λίγες παραλλαγές στο δεύτερο) Q PostgreQL 2 Τυπική σχεσιακή βάση με M Σχεσιακή βάση με M και βελτιστοποιήσεις για φιλικότητα προς τον επεξεργαστή Monet ertical partitioning Τα υπόλοιπα συστήματα διέθεταν όλους τους κατάλληλους indexes, επεξεργασία σε όλη την κυρίως μνήμη Συχνότητα 1.8z Cache line 64 1 cache 32k (per core) 1 cache 32k (per core) L2 cache 2M (shared) L1 latency (seq) 9 κύκλοι L1 latency (rnd) 14 κύκλοι L2 latency (seq) 28 κύκλοι L2 latency(rnd) 77 κύκλοι AM type 2x1 2 667Mz (μετρήσεις από ightmark Memory Analyzer) Κρικέλλας, Βίγλας, Cintra M 2010
Τι κερδίζουμε κι από πού Μέθοδος Join Query 1 Aggregate Query 1-0 -2-0 -2 Γενική επανάληψη 0.802 0.235 1.225 0.527 Βελτιστοποιημένη επανάληψη 0.618 0.231 1.199 0.509 Γενικευμένος κώδικας (γραμμένος με το χέρι) Βελτιστοποιημένος κώδικας (γραμμένος με το χέρι) 0.430 0.118 0.586 0.344 0.267 0.055 0.554 0.333 Q 0.178 0.054 0.543 0.326 (μετρήσεις σε δευτερόλεπτα) Κρικέλλας, Βίγλας, Cintra M 2010
Τι κερδίζουμε κι από πού Επίπεδο βελτιστοποίησης μεταφραστή Μέθοδος Join Query 1 Aggregate Query 1-0 -2-0 -2 Γενική επανάληψη 0.802 0.235 1.225 0.527 Βελτιστοποιημένη επανάληψη 0.618 0.231 1.199 0.509 Γενικευμένος κώδικας (γραμμένος με το χέρι) Βελτιστοποιημένος κώδικας (γραμμένος με το χέρι) 0.430 0.118 0.586 0.344 0.267 0.055 0.554 0.333 Q 0.178 0.054 0.543 0.326 (μετρήσεις σε δευτερόλεπτα) Κρικέλλας, Βίγλας, Cintra M 2010
Περισσότερα αποτελέσματα - επερώτηση ζεύξης!"#$%&'(%!"&$#!"&!#!"%$#!"%!# -)1.*>,40)#(?(,>40)# @(10>*,(#1./AA1# &9,/,8(#5+11(1# %9,/,8(#5+11(1#!"!$#!"!!# '()(*+,# -.(*/.0*1# 2345+6(7# -.(*/.0*1# '()(*+,#8/*79,07(7# 2345+6(7# 8/*79,07(7# :-;<=# Κρικέλλας, Βίγλας, Cintra M 2010
Περισσότερα αποτελέσματα - επερώτηση συνόλων!"#$%&'(%!")!#!"(!#!"'!#!"&!# 0,41-A/73,#++/A73,# C+43A-/+#4124# %</2/;+#8.44+4# $</2/;+#8.44+4#!"%!#!"$!#!"!!# *+,+-./# 01+-213-4# 5678.9+:# 01+-213-4# *+,+-./#;2-:< /3:+:# 5678.9+:# ;2-:</3:+:# =0>?@# Κρικέλλας, Βίγλας, Cintra M 2010
Ενδεικτικά TPC- αποτελέσματα TPC- Σύστημα επερώτηση Q Monet PostgreQ 2 1 0.356 1.725 L 37.185 59.353 3 0.411 0.517 2.477 4.549 5 0.793 0.664 1.340 3.668 10 0.971 1.131 2.091 5.091 Μετρήσεις σε δευτερόλεπτα Δεδομένα τάξης εκατοντάδων M, στα όρια (επεξεργασία στην κυρίως μνήμη) Επερωτήσεις με πολλαπές ζεύξεις και υπολογισμούς συνόλων (aggregates) Κρικέλλας, Βίγλας, Cintra M 2010
Και η κατάσταση χειροτερεύει CP CP CP CP L1 cache L1 cache L1 cache L1 cache L2 cache L2 cache Main memory Τυπικά, 64K L1 cache, 4M L2 cache, κάποια s κυρίως μνήμης Αρχιτεκτονική διαμοιρασμένης μνήμης Κρικέλλας, Βίγλας, Cintra M 2010
Αν έχεις ένα σφυρί......όλα μοιάζουν με καρφιά Προκειμένου να εκμεταλλευθούν οι αρχιτεκτονικές πολλών πυρήνων, δημιουργία νέου κώδικα Ευθγράμμιση του κώδικα με cache μνήμες, πυρήνες, και νήματα εκτέλεσης Πάντα σε σχέση με το υλικό Θα είναι δύσκολο να δημιουργηθούν μεταγλωττιστές παραλληλοποίησης Ίσως όχι τόσο δύσκολο με γενικά πρότυπα κώδικα (τρέχουσα εργασία) Κρικέλλας, Βίγλας, Cintra M 2010
Συμπεράσματα Το τοπίο του υλικού αλλάζει γρηγορότερα από ποτέ (Προσωπική άποψη:) Ως επί το πλείστον κυνηγάμε τις εξελίξεις Επεξεργασία επερωτήσεων φιλική προς το υλικό Ολιστική αντιμετώπιση: βελτιστοποίηση για ολόκληρη τη συγκεκριμένη ερώτηση και για το συγκεκριμένο περιβάλλον εκτέλεσης Δημιουργία κώδικα, μετάφραση, εκτέλεση Κρικέλλας, Βίγλας, Cintra M 2010