Τεχνικό Άρθρο

Βιβλιοθήκη Στοιχείων Alcinoe και Συμβατότητα με Delphi 11.1 Alexandria

Το Alcinoe είναι μια βιβλιοθήκη στοιχείων ανοιχτού κώδικα για Delphi και C++Builder, η οποία συντηρείται στο GitHub από τον Zeus64. Καλύπτει έδαφος που τα VCL και FireMonkey RTL αφήνουν σε τρίτους: ένα πρόγραμμα αναπαραγωγής βίντεο με επιτάχυνση GPU, ένα περιτύλιγμα WebRTC, εγγενή στοιχεία ελέγχου επεξεργασίας iOS και Android, έναν αναλυτή JSON/BSON διπλής λειτουργίας, ένα πρόγραμμα-πελάτη MongoDB με συγκέντρωση συνδέσεων, ένα περιτύλιγμα ImageMagick, και μια συλλογή στοιχείων ελέγχου FireMonkey που παρακάμπτουν εντελώς την προεπιλεγμένη διαδικασία απόδοσης. Η βιβλιοθήκη έχτισε τη φήμη της στα Rio (10.3.3) και Sydney (10.4.2), και έκτοτε παρακολουθεί κάθε έκδοση του Embarcadero. Τη στιγμή της συγγραφής, είναι πλήρως συμβατό με το Delphi 11.1 Alexandria και το Delphi Athens 12.3

Προσθήκη του Alcinoe σε ένα έργο

Η εγκατάσταση χωρίζεται σε μία ερώτηση: χρειάζεστε υποστήριξη στο χρόνο σχεδίασης για τα οπτικά στοιχεία ελέγχου του Alcinoe; Εάν όχι, παραλείψτε εντελώς το BPL. Προσθέστε το {alcinoe_rootdir}\source στη διαδρομή αναζήτησης βιβλιοθήκης του έργου και τελειώσατε. Κάθε μη οπτικό στοιχείο, συμπεριλαμβανομένων των αναλυτών, των πελατών βάσης δεδομένων και των βοηθητικών προγραμμάτων συμβολοσειρών, μεταγλωττίζεται από τον πηγαίο κώδικα χωρίς να γίνεται εγγραφή τίποτα

Όταν χρειάζεστε υποστήριξη στο χρόνο σχεδίασης, η διαδρομή είναι λίγο μεγαλύτερη. Ανοίξτε το Component > Install Packages στο Delphi IDE, περιηγηθείτε στο BPL που ταιριάζει με την έκδοσή σας (για παράδειγμα {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl), εγκαταστήστε το και, στη συνέχεια, προσθέστε ξανά το {alcinoe_rootdir}\source στη διαδρομή αναζήτησης. Το BPL καταχωρεί τα στοιχεία. Ο κατάλογος προέλευσης είναι αυτό που βρίσκει ο μεταγλωττιστής όταν μεταγλωττίζει το έργο σας

Το Alcinoe αποστέλλει προαιρετικές επιδιορθώσεις (patches) στις πηγές του Embarcadero RTL. Αν τις θέλετε, μεταβείτε στο {alcinoe_rootdir}\embarcadero\, επιλέξτε τον υποκατάλογο για την έκδοσή σας και εκτελέστε το update.bat. Το σενάριο αναμένει το GIT στο PATH και προϋποθέτει μια προεπιλεγμένη θέση εγκατάστασης Embarcadero. Λαμβάνει τον αρχικό πηγαίο κώδικα RTL και εφαρμόζει τις επιδιορθώσεις. Μόλις τελειώσετε, προσθέστε αυτόν τον διορθωμένο κατάλογο προέλευσης στη διαδρομή αναζήτησης του έργου σας, ώστε ο μεταγλωττιστής να τον πάρει μπροστά από το αντίγραφο μόνο για ανάγνωση στο δέντρο εγκατάστασης του Embarcadero. Τίποτα από αυτά δεν απαιτείται για να ξεκινήσετε. έχει σημασία μόνο αν συναντήσετε σφάλματα που αντιμετωπίζουν οι διορθώσεις

Το Android και ο διακομιστής μεσολάβησης αποσακχαροποίησης D8

Αρκετά στοιχεία του Alcinoe (WebRTC, βίντεο με υποστήριξη ExoPlayer) εξαρτώνται από βιβλιοθήκες Java που χρησιμοποιούν χαρακτηριστικά της γλώσσας Java 8. Η αλυσίδα εργαλείων Android που αποστέλλεται με παλαιότερες εκδόσεις Delphi χρησιμοποιεί το dx.bat για μετατροπή DEX, το οποίο δεν μπορεί να χειριστεί αυτούς τους bytecodes σε επίπεδα API κάτω από το 26. Η λύση είναι η αποσακχαροποίηση (desugaring), την οποία ο D8 χειρίζεται αυτόματα όταν καλείται άμεσα. Το Alcinoe παρέχει ένα σενάριο διακομιστή μεσολάβησης στο {alcinoe_rootdir}\tools\D8Proxy\dx.bat το οποίο προωθεί κλήσεις από το σύστημα μεταγλώττισης του Delphi στον D8, καθιστώντας την αποσακχαροποίηση διαφανή. Αντικαταστήστε το αρχικό dx.bat στον κατάλογο εργαλείων κατασκευής Android SDK (συνήθως C:\SDKs\android\build-tools\30.0.3\) με αυτόν τον διακομιστή μεσολάβησης. Το Embarcadero παρακολούθησε το υποκείμενο ζήτημα στο RSP-24155. Μεταγενέστερες εκδόσεις των εργαλείων SDK το αντιμετώπισαν άμεσα, οπότε ελέγξτε αν η τρέχουσα αλυσίδα εργαλείων σας εξακολουθεί να χρειάζεται αυτήν την παράκαμψη

Το πρόβλημα απόδοσης του FireMonkey και η απάντηση του Alcinoe

Ο προεπιλεγμένος κύκλος σχεδίασης του FireMonkey γίνεται σημείο συμφόρησης σε UI με έντονη κύλιση. Ένα απλό TRectangle με στρογγυλεμένες γωνίες μπορεί να χρειαστεί περίπου 3 ms για να επανασχεδιαστεί επειδή η προεπιλεγμένη υλοποίηση υπολογίζει ξανά τη διαδρομή σε κάθε καρέ. Με 20 τέτοια στοιχεία ελέγχου ορατά, αυτό προσθέτει έως και 60 ms ανά πέρασμα καρέ, γεγονός που περιορίζει τον πραγματικό ρυθμό καρέ πολύ κάτω από το όριο για ομαλή κύλιση

Το Alcinoe το αντιμετωπίζει αυτό με ένα buffer που εδρεύει στη GPU ανά στοιχείο ελέγχου. Η πρώτη σχεδίαση αποδίδει το στοιχείο ελέγχου σε ένα TTexture που αποθηκεύεται στη μνήμη της GPU. Στις επόμενες επανασχεδιάσεις, αντιγράφεται αυτή η υφή (blit) αντί να εκτελείται ξανά ο αλγόριθμος σχεδίασης. Το μετρούμενο αποτέλεσμα στο ίδιο στρογγυλεμένο ορθογώνιο πέφτει από περίπου 3 ms σε περίπου 0,1 ms. Πέρα από την προσωρινή αποθήκευση, το Alcinoe αντικαθιστά τη σχεδίαση διαδρομής του OpenGL για βασικά σχήματα με εγγενή API σχεδίασης Android και iOS, αποφεύγοντας τον συμβιβασμό ποιότητας/απόδοσης που συνδέεται με το Form.Quality. Τα σχετικά στοιχεία ελέγχου είναι το TALRectangle, το TALCircle, και ένα σύνολο βελτιωμένων κοντέινερ διάταξης συμπεριλαμβανομένων των ScrollBox και TabControl

TALJsonDocument: DOM και SAX σε έναν τύπο

Το TALJsonDocument είναι ο αναλυτής JSON και BSON του Alcinoe. Υποστηρίζει δύο λειτουργίες διέλευσης. Η λειτουργία DOM δημιουργεί ένα δέντρο αντικειμένων στη μνήμη, δίνοντας τυχαία πρόσβαση σε οποιονδήποτε κόμβο με κόστος σε μνήμη ανάλογο με το μέγεθος του εγγράφου. Η λειτουργία SAX ενεργοποιεί γεγονότα καθώς ο αναλυτής διαβάζει κάθε διακριτικό (token) χωρίς να διατηρεί κανένα δέντρο, κάτι που είναι η σωστή επιλογή όταν χρειάζεται να φιλτράρετε ένα μεγάλο έγγραφο και να κρατήσετε μόνο μερικές τιμές. Οι αναλυτές DOM στο Delphi (DBXJSON, SuperObject, και τα λοιπά) είναι συνήθως τρεις έως πέντε φορές πιο αργοί από μια προσέγγιση SAX για το ίδιο περιεχόμενο, επειδή κάθε κατανομή κόμβου φέρει έξοδα δημιουργίας αντικειμένων πάνω από την ίδια την εργασία ανάλυσης

Ο τύπος ακολουθεί το ίδιο μοτίβο πλοήγησης κόμβων με το TALXMLDocument. Μια ελάχιστη ανάγνωση DOM φαίνεται ως εξής:

MyJsonDoc.LoadFromJSON(AJsonStr, False {dom mode});
MyJsonDoc.ParseOptions := [poAllowComments];

// read scalar values
ShowMessage(MyJsonDoc.ChildNodes[‘name’].ChildNodes[‘first’].Text);
ShowMessage(IntToStr(MyJsonDoc.ChildNodes[‘_id’].Int32));

// iterate an array
for I := 0 to MyJsonDoc.ChildNodes[‘contribs’].ChildNodes.Count - 1 do
  Writeln(MyJsonDoc.ChildNodes[‘contribs’].ChildNodes[I].Text);

Για τη λειτουργία SAX, αναθέστε μια ανώνυμη διαδικασία στο OnParseText πριν καλέσετε το LoadFromJSON με το δεύτερο όρισμα ορισμένο σε True. Η επανάκληση λαμβάνει τη διαδρομή κόμβου, το όνομα, την τιμή και ένα TALJSONNodeSubType που προσδιορίζει τον τύπο JSON (συμβολοσειρά, ακέραιος, κινητής υποδιαστολής, boolean και ούτω καθεξής). Αυτή η λειτουργία δεν παράγει κατανομές στο σωρό (heap) για κόμβους, οπότε κλιμακώνεται σε αυθαίρετα μεγάλα έγγραφα χωρίς να ξεπερνά τον προϋπολογισμό της μνήμης

Το TALJsonDocument διαβάζει και γράφει επίσης εγγενώς BSON. περάστε το True ως σημαία BSON στο LoadFromFile ή το SaveToFile. Μια δεύτερη παραλλαγή, το TALJsonDocumentU, χρησιμοποιεί εσωτερικά το UnicodeString (UTF-16) αντί για το AnsiString (UTF-8) για περιβάλλοντα όπου ο περιβάλλων κώδικας λειτουργεί σε Unicode παντού

Πελάτης MongoDB και συγκέντρωση συνδέσεων

Το πρόγραμμα οδήγησης MongoDB του Alcinoe καλύπτει τις κοινές λειτουργίες ερωτημάτων και χειρίζεται τη συγκέντρωση συνδέσεων (connection pooling) εγγενώς. Ο απλός πελάτης, TAlMongoDBClient, ανοίγει και κλείνει μια μόνο σύνδεση ανά λειτουργία. Η συγκεντρωτική παραλλαγή, TAlMongoDBConnectionPoolClient, διατηρεί ένα σύνολο ενεργών συνδέσεων και παραδίδει μία σε κάθε νήμα κλήσης από τη δεξαμενή, επιστρέφοντάς την όταν ολοκληρωθεί η κλήση. Αυτό το μοντέλο εμποδίζει πολλαπλά νήματα να μπλοκάρουν το ένα το άλλο κατά τη ρύθμιση της σύνδεσης, κάτι που έχει σημασία κάθε φορά που οι εργαζόμενοι στο παρασκήνιο ρωτούν ταυτόχρονα την ίδια βάση δεδομένων. Για tailabe δρομείς σε περιορισμένες συλλογές (capped collections), το TAlMongoDBTailMonitoringThread παρακολουθεί για νέα έγγραφα και ενεργοποιεί μια επανάκληση όταν φτάσουν, το οποίο είναι το τυπικό μοτίβο για ροή καταγραφής (log streaming) ή ειδοποίηση αλλαγής χωρίς δημοσκόπηση (polling)

Άλλα στοιχεία που αξίζει να γνωρίζετε

Το ALVideoPlayer αποδίδει βίντεο σε ένα TTexture και όχι σε ένα παράθυρο επικάλυψης, επομένως άλλα στοιχεία ελέγχου FireMonkey μπορούν να τοποθετηθούν πάνω από αυτό στην τάξη Ζ (Z-order). Το backend του Android χρησιμοποιεί το ExoPlayer, το οποίο προσθέτει υποστήριξη DASH, HLS και SmoothStreaming πέρα από αυτό που χειρίζεται το ενσωματωμένο MediaPlayer του Android. Το backend του iOS χρησιμοποιεί το AVPlayer με αντίστοιχη υποστήριξη HLS

Το TALWebRTC τυλίγει τη στοίβα WebRTC για ήχο και βίντεο peer-to-peer. Δεν απαιτεί πρόγραμμα περιήγησης ή προσθήκη (plugin) και η σύνδεση διασχίζει το NAT μέσω της τυπικής διαπραγμάτευσης ICE/STUN/TURN που χειρίζεται η υποκείμενη βιβλιοθήκη

Η TALStringList αντικαθιστά την ταξινόμηση βάσει του AnsiCompareText της TStringList με μια τακτική σύγκριση ανεξάρτητη από τις ρυθμίσεις περιοχής (locale-independent) και ένα quicksort που είναι έως και 10x πιο γρήγορο σε μεγάλες λίστες. Η κατακερματισμένη παραλλαγή, TALHashedStringList, προσθέτει έναν εσωτερικό πίνακα κατακερματισμού (hash table) για αναζήτηση O(1) με κόστος λίγο μεγαλύτερο φόρτο (overhead) σε μικρές λίστες. Λάβετε υπόψη ότι το TALStringList είναι μια λίστα AnsiString 8-bit, όχι μια λίστα Unicode. ταιριάζει καλά στον κώδικα της πλευράς του διακομιστή όπου η UTF-8 είναι η κωδικοποίηση εργασίας και η ακατέργαστη διεκπεραίωση (throughput) έχει μεγαλύτερη σημασία από τη σύγκριση με επίγνωση των τοπικών ρυθμίσεων

Στα Windows 64-bit, η κληρονομιά FastCode που έδωσε σε πολλές από τις ρουτίνες συμβολοσειρών του Alcinoe το πλεονέκτημα ταχύτητάς τους (κυρίως χειρόγραφη assembly x86) δεν μεταφέρεται. Οι εκδόσεις (builds) Win64 επιστρέφουν (fall back) στις υλοποιήσεις Pascal, οι οποίες εκτελούνται αισθητά πιο αργά σε φόρτους εργασίας με ένταση συμβολοσειρών. Το έργο demo\ALStringBenchMark σάς επιτρέπει να μετρήσετε το κενό στο υλικό σας πριν δεσμευτείτε σε μια έκδοση 64-bit όπου η διεκπεραίωση συμβολοσειρών αποτελεί σημείο συμφόρησης

Ο πλήρης πηγαίος κώδικας βρίσκεται στο github.com/Zeus64/alcinoe