Δύο μηχανήματα κλάσης DGX Spark, ένα μοντέλο: ένα αντίγραφο σε κάθε μηχάνημα αντί για διαχωρισμό
Λειτουργίες
Για επτά εβδομάδες το τοπικό μας γλωσσικό μοντέλο λειτουργούσε διαχωρισμένο σε δύο μηχανήματα NVIDIA GB10 με tensor parallelism. Στις 22 Οκτωβρίου αντικαταστήσαμε αυτή τη διάταξη με ένα πλήρες αντίγραφο του μοντέλου σε κάθε μηχάνημα και έναν proxy least-connection μπροστά. Η απόδοση αυξήθηκε από περίπου 180 σε 210 tokens ανά δευτερόλεπτο, και τώρα το καθένα από τα δύο μηχανήματα μπορεί να κάνει επανεκκίνηση χωρίς να χαθεί το μοντέλο.
Γιατί το είχαμε διαχωρίσει
Κάθε μηχάνημα διαθέτει 128 GB μνήμης που μοιράζονται η CPU και η GPU. Τον Σεπτέμβριο το μοντέλο χωρούσε σε ένα μηχάνημα, αλλά η cache του μόλις: η έκδοση vLLM που χρησιμοποιούσαμε δεν μπορούσε να κρατήσει την KV cache αυτού του μοντέλου σε FP8, οπότε η cache ήταν σε BF16, και ένα μηχάνημα είχε χώρο για 499.110 tokens αυτής. Αυτό είναι λιγότερο από δύο αιτήματα στο πλήρες context του μοντέλου των 262.144 tokens. Διαχωρισμένο και στα δύο μηχανήματα με tensor parallelism μέσω της άμεσης δικτυακής τους σύνδεσης, η cache μεγάλωσε σε περισσότερα από τρία εκατομμύρια tokens. Είχαμε διαχωρίσει το μοντέλο για χώρο, όχι για ταχύτητα.
Τι άλλαξε
Μια νεότερη έκδοση 4-bit του ίδιου μοντέλου (NVFP4, φτιαγμένη με quantisation-aware distillation) καταλαμβάνει περίπου 71 GiB μνήμης, και μια κοινότητα έκδοση vLLM για GB10 (eugr/spark-vllm-b12x) τρέχει την cache της σε FP8. Τώρα ένα μηχάνημα χωράει ολόκληρο το μοντέλο και μια cache 0,76 έως 1,08 εκατομμυρίων tokens, χώρο για 2,9 έως 4,1 αιτήματα σε πλήρες context, ανάλογα με το πόση μνήμη του δίνει το μηχάνημα. Και τα δύο αντίγραφα χρησιμοποιούν το δικό του speculative decoding του μοντέλου (MTP, τέσσερα draft tokens). Πριν την αλλαγή τρέξαμε τη δοκιμή tool-calling μας στη νέα έκδοση: έβαλε 86, η έκδοση που αντικατέστησε έβαλε 75.
Τι μετρήσαμε
| Διαχωρισμένο σε δύο μηχανήματα | Ένα αντίγραφο σε κάθε μηχάνημα | |
|---|---|---|
| KV cache | περισσότερα από 3 εκατομμύρια tokens | 0,76 και 1,08 εκατομμύρια tokens |
| Αιτήματα σε πλήρες context | περίπου 12 | 2,9 και 4,1 |
| Απόδοση υπό το φορτίο μας | περίπου 180 tokens/s | 210 tokens/s (111 + 99) |
| Εκκίνηση | περίπου 20 λεπτά | 5 έως 13 λεπτά ανά αντίγραφο |
| Επανεκκίνηση ενός μηχανήματος | το μοντέλο είναι εκτός λειτουργίας | το άλλο αντίγραφο εξυπηρετεί |
Μία αντιγραφή από μόνη της, ανάλογα με τον αριθμό αιτημάτων που εξυπηρετεί ταυτόχρονα: με σύντομα prompts, 1 αίτημα απέδιδε 36 tokens ανά δευτερόλεπτο, 4 απέδιδαν 82, 8 απέδιδαν 109 και 16 απέδιδαν 150. Με τα μεγαλύτερα prompts του email agent μας, 4 απέδιδαν 81, 8 απέδιδαν 113 και 16 μόνο 87: πάνω από οκτώ τέτοια αιτήματα, περισσότερα τα καθιστούσαν πιο αργά.
Για ένα απόγευμα, η μία αντιγραφή δέχτηκε το πολύ 12 αιτήματα ταυτόχρονα και η άλλη 8. Η πρώτη παρήγαγε 91 έως 93 tokens ανά δευτερόλεπτο και ολοκλήρωνε 26 έως 27 αιτήματα ανά λεπτό· η δεύτερη 81 έως 83 tokens ανά δευτερόλεπτο και 22 αιτήματα ανά λεπτό. Και οι δύο τώρα δέχονται 12.
Τι θυσιάσαμε
Χώρος. Το split model είχε μία cache άνω των τριών εκατομμυρίων tokens· οι αντιγραφές έχουν δύο μικρότερες, και ένα αίτημα μπορεί να χρησιμοποιήσει μόνο την cache της αντιγραφής που το εξυπηρετεί. Οι agents μας στέλνουν πολλά σύντομα και μεσαία αιτήματα, και γι’ αυτά η απόδοση ήταν πιο σημαντική. Αν εξυπηρετείτε λίγα πολύ μεγάλα contexts ταυτόχρονα, ο διαχωρισμός μπορεί να παραμένει η καλύτερη επιλογή. Διατηρήσαμε τη ρύθμιση split, και η επιστροφή γίνεται με ένα script.
Τι πήγε στραβά κατά την εναλλαγή
- Έντεκα λεπτά χωρίς μοντέλο, μετά 30 δευτερόλεπτα με 502. Το split model έπρεπε να σταματήσει πριν φορτώσει μια αντιγραφή στις ίδιες μηχανές, οπότε οι agents μας περίμεναν περίπου έντεκα λεπτά. Όταν ανέβηκε η πρώτη αντιγραφή, ο proxy συνέχιζε να απαντά «502 no live upstreams» για περίπου 30 δευτερόλεπτα ακόμα: είχε σημάνει τα backends ως αποτυχημένα ενώ ήταν κάτω (max_fails=3, fail_timeout=30s) και δεν τα δοκίμαζε ξανά μέχρι να λήξει αυτός ο χρόνος.
- Ένας client που παρακάμπτει τον proxy. Μία από τις batch jobs μας είχε τη διεύθυνση της πρώτης μηχανής στο config της, οπότε όλο το φορτίο της πήγαινε σε μία αντιγραφή ενώ η άλλη είχε διαθέσιμη χωρητικότητα. Τώρα όλοι οι clients περνούν από τον proxy· οι δικές τους διευθύνσεις των μηχανών χρησιμοποιούνται μόνο για health checks.
- Μετρικές που «πηδούσαν». Ο παρατηρητής απόδοσής μας διάβαζε τα /metrics του vLLM μέσω του proxy. Με δύο backends, οι διαδοχικές αναγνώσεις προέρχονταν από διαφορετικές αντιγραφές, οι μετρητές πηδούσαν μπρος-πίσω και ο παρατηρητής ανέφερε το μοντέλο ως «starved». Τώρα διαβάζει κάθε αντιγραφή απευθείας και τις αθροίζει.
- Ένας παλιός Φύλακας που θα σταματούσε το νέο αντίγραφο. Η διαχωρισμένη διάταξη είχε έναν Φύλακα που επανεκκινούσε την υπηρεσία του όταν το μοντέλο σταματούσε να απαντά. Η νέα υπηρεσία δηλώνει Conflicts= με την παλιά, οπότε αυτή η επανεκκίνηση θα σταματούσε το νέο αντίγραφο. Έπρεπε να απενεργοποιηθεί με
systemctl disable --now, όχι απλώς να σταματήσει, επειδή το επίπεδο λειτουργιών μας επανεκκινεί μονάδες που είναι ενεργοποιημένες αλλά δεν εκτελούνται. - Μνήμη στη μηχανή που εκτελεί και άλλα έργα. Μία μηχανή εκτελεί επίσης τις βάσεις δεδομένων και τις συνθέσεις ιστοσελίδων μας. Με το gpu-memory-utilization του vLLM στο 0.78, απέμεναν μόνο 5 GiB για όλα τα υπόλοιπα, οπότε το μειώσαμε στο 0.74 και επανεκκινήσαμε μόνο αυτό το αντίγραφο, ενώ το άλλο ανέλαβε το φορτίο. Σε αυτές τις μηχανές, το μερίδιο που δίνετε στο vLLM προέρχεται από την ίδια μνήμη με όλα τα υπόλοιπα.
Αν έχετε δύο από αυτές τις μηχανές
- Αν το μοντέλο και η προσωρινή μνήμη που χρειάζεστε χωρούν σε μία μηχανή, εκτελέστε ένα αντίγραφο σε κάθε μία πίσω από έναν proxy ελάχιστων συνδέσεων. Διαχωρίστε το μοντέλο μόνο όταν μία μηχανή δεν μπορεί να χωρέσει την προσωρινή μνήμη που χρειάζεστε.
- Κατευθύνετε κάθε πελάτη στον proxy, συμπεριλαμβανομένων των μαζικών εργασιών και των παλιών ρυθμίσεων.
- Διαβάζετε τις μετρήσεις από κάθε αντίγραφο απευθείας.
- Δώστε σε κάθε μηχανή το δικό της μερίδιο μνήμης, ανάλογα με το τι άλλο εκτελείται σε αυτήν.
- Ρυθμίστε πόσα αιτήματα εξυπηρετεί ταυτόχρονα ένα αντίγραφο με βάση την κυκλοφορία σας: για το μείγμα μας, τα 12 ήταν καλύτερα από τα 8.
- Περιμένετε η εναλλαγή να αφαιρέσει το μοντέλο για όσο χρόνο χρειάζεται ένα αντίγραφο για να εκκινήσει, και σχεδιάστε ανάλογα.
Factory Floor περιλαμβάνει τον proxy και τις ρυθμίσεις εκκίνησης για μία ή δύο μηχανές, τον έλεγχο υγείας και τη δοκιμή χρήσης εργαλείων που εκτελούμε πριν από την εναλλαγή μοντέλων, με τους Φύλακες και τους μηχανισμούς ασφαλείας μνήμης για το υπόλοιπο της μηχανής.