Overleaf-Benchmark.pdf
Sammendrag
Selvdriftede Overleaf-distribusjoner dimensjoneres ofte etter én enkel tommelfingerregel: én CPU-kjerne og én gigabyte minne per fem til ti samtidige brukere. Vi viser at denne regelen ikke bare er unøyaktig, men strukturelt feil, fordi den antar at kapasiteten styres av én enkelt ressursdimensjon, mens den i virkeligheten styres av to uavhengige vegger, og fordi to programvareparametere — ingen av dem maskinvare — dominerer resultatet med faktorer på opptil fire. Vi måler en standard Ayakaleaf Pro v6.2.2-distribusjon med sandkassekompilering (TeX Live 2025) på tvers av 21 CPU-/minnekonfigurasjoner i QEMU/KVM-gjester der vertskjernene er klokkelåst til 3,0 GHz. Arbeidsbelastningen er en ekte XeLaTeX-avhandling på 63 sider som kompileres samtidig av opptil flere hundre ulike brukerkontoer. Vi finner at under 32 GiB gjesteminne er antall kjerner nesten irrelevant — ved 16 GiB skiller den målte kapasiteten for gjester med 4, 8 og 16 vCPU-er seg med mindre enn 8 % — og at kapasiteten i stedet styres av en superlineær minnevegg som oppstår fra delt sidebuffer over TeX Live-treet. For å undersøke om disse lovene holder ved en endring i skala på en størrelsesorden, gjentar vi målingene på én enkelt server med 64 kjerner og 995 GiB. Den håndterer 1024 samtidige kalde kompileringer med 100 % suksess — åtte ganger antall tråder — og vi når aldri taket. Det nyttige tallet er ikke dette taket, men kneet under det: halelatensen øker med 20–40 % per dobling opp til , og deretter med 190 % ved . Kapasitet rapportert som «den høyeste samtidigheten som ikke feiler» ville derfor overdrive det brukbare driftspunktet med en faktor på fire. På den maskinen er minnet aldri den begrensende ressursen; grensen er CPU sammen med hastigheten containerdaemonen kan ta imot nye sandkasser med, som metter seg nær 200 uavhengig av hvor mange kompileringer som etterspørres. Vi identifiserer videre to effekter på implementasjonsnivå som er usynlige for kapasitetsplanlegging. For det første håndhever CLSI et hardkodet tak på 65 samtidige kompileringer som ikke eksponeres via noen miljøvariabel; utover dette får brukerne HTTP 503 umiddelbart i stedet for å bli satt i kø. For det andre har minnegrensen per container i Docker-runneren vært virkningsløs siden den ble innført i 2018, både i størrelse og plassering, slik at en hendelse med tomt minne tar ned hele verten i stedet for én enkelt kompilering. Ved å fjerne samtidighetstaket og øke standard tidsavbrudd for kompilering fra 180 s til 300 s øker den målte kapasiteten for en gjest med 8 vCPU / 48 GiB fra 64 til 268 samtidige kompileringer — en faktor på 4,2 uten maskinvarekostnad. Til slutt viser vi at samtidighet i dette systemet ikke gir annet enn tidsdeling, og at arbeidsbelastningen kun er begrenset av klokkefrekvensen. En tilpasset degraderingslov gir , nær perfekt proporsjonal nedbremsing, og en klokkesveip over maskinens fulle område på 1,0–5,5 GHz samler tretti målinger på med og en restspredning på 5,1 %. En 5,5× klokke gir en 5,5× hastighetsøkning uten avtagende avkastning, og det er i denne forstand klokke og kjerner kjøper ulike ting: klokke gjør hver brukers kompilering raskere, kjerner slipper bare inn flere brukere.1. Innledning
Overleaf er den dominerende samarbeidsbaserte LaTeX-editoren, og den lokalt driftede distribusjonen er mye brukt av universiteter og forskningsgrupper som ikke kan sende upubliserte manuskripter til en tredjeparts sky. Dimensjonering av en slik distribusjon er et tilbakevendende praktisk spørsmål: gitt et fast maskinvarebudsjett, hvor mange personer kan faktisk trykke på «Recompile» samtidig? Den offisielle veiledningen er en lineær regel — omtrent én kjerne og én gigabyte per fem til ti samtidige brukere — som forutsetter at kapasiteten skalerer jevnt og samtidig i begge ressursene. Målingene våre motsier dette på tre måter.1.1 Kapasiteten styres av to uavhengige vegger, ikke én
En konfigurasjon feiler enten fordi minnet er oppbrukt, i så fall dør selve Overleaf-stakken og returnerer HTTP 502, eller fordi kompileringer overskrider tidsavbruddet på serversiden, i så fall rapporterer CLSItimedout mens gigabyte med minne står ubrukt. Disse to regimene har helt forskjellig skaleringsatferd og ulike løsninger. Å legge til kjerner i en minnebegrenset konfigurasjon er ikke bare ineffektivt, det er av og til kontraproduktivt: vi måler konfigurasjoner der flere kjerner reduserer kapasiteten, fordi flere kjerner får samtidige kompileringer til å gå i takt slik at minnetoppene deres sammenfaller i stedet for å veksle.
1.2 Programvareparametere dominerer over maskinvare
Tidsavbruddet for kompilering er et felt per bruker i MongoDB, og standardverdien på 180 s begrenser i det stille CPU-bundne konfigurasjoner. Ved å øke det til 300 s multipliseres den målte kapasiteten med opptil 4,2 på uendret maskinvare. Uavhengig av dette avviser CLSI mer enn 65 samtidige kompileringer via en hardkodet konstant. Enhver kapasitetsstudie — og enhver distribusjon — som ikke tar hensyn til begge deler, måler programvaren, ikke maskinen.1.3 Samtidighet er tidsdeling, ikke parallellitet
Fordi en LaTeX-kompilering er entrådet, blir ikke systemet raskere ferdig av å betjene samtidige brukere på kjerner; det får hver bruker til å vente proporsjonalt lenger. Spørsmålet «hvor mange samtidige brukere støttes» er derfor dårlig stilt inntil man fastsetter hvor lenge en bruker er villig til å vente. Vi gjør denne avhengigheten eksplisitt og kvantifiserer den.1.4 Bidrag
- En kapasitetsmatrise over 21 CPU-/minnekonfigurasjoner målt under klokkelåste, gjentakelsesverifiserte forhold, der den begrensende faktoren er identifisert per konfigurasjon ut fra feilsignaturen.
- To tilpassede modeller: en kapasitetsmodell som skiller en superlineær minnevegg fra et CPU-tak, og en latensmodell som påviser ren tidsdelingsatferd.
- Identifisering og eksperimentell bekreftelse av to implementasjonsproblemer i det distribuerte systemet, inkludert en minnegrense for containere som har vært virkningsløs siden 2018.
- En kvantifisering av avveiningen mellom tidsavbrudd for kompilering og kapasitet, som vi mener må oppgis sammen med ethvert samtidighetstall.
2. Bakgrunn
2.1 Kompileringsbanen
En kompileringsforespørsel i Overleaf gårweb clsi en kompileringscontainer. I en distribusjon med sandkassekompilering (SIBLING_CONTAINERS_ENABLED=true) kjører ikke CLSI latexmk i egen prosess; den ber vertens Docker-daemon, som nås via en bind-montert socket, om å starte en ny container fra et TeX Live-image med prosjektkatalogen bind-montert på /compile. Én kompilering er derfor én kortlivet container som kjører én latexmk-prosess.
Dette har tre konsekvenser, og alle tre former målingene i denne artikkelen. For det første er arbeidsenheten en entrådet prosess: XeLaTeX parallelliserer ikke. For det andre er ressursisolasjonen per kompilering det Docker-runneren ber om — vi viser i §6.2 at den i praksis ikke ber om noe. For det tredje domineres arbeidssettet ikke av dokumentet, men av TeX Live-treet, et skrivebeskyttet korpus på omtrent 32 GiB som hver samtidige kompilering leser fra og derfor deler via vertens sidebuffer. Denne delingen er opphavet til den superlineære minneskaleringen vi observerer.
2.2 Aktivere sandkassekompilering
Community Edition av Overleaf kjørerlatexmk inne i selve applikasjonscontaineren. Ayakaleaf Pro, i likhet med Overleaf Server Pro, kan i stedet kjøre hver kompilering i en søsken-container — en container som applikasjonen starter på vertens Docker-daemon i stedet for nestet inne i applikasjonscontaineren. To Toolkit-innstillinger slår dette på:
SANDBOXED_COMPILES=true, SANDBOXED_COMPILES_SIBLING_CONTAINERS=true og SANDBOXED_COMPILES_HOST_DIR, der den siste er vertens sti til kompileringskatalogen. Denne stien er viktig: fordi daemonen som starter kompileringscontaineren tilhører verten, må bind-monteringen den får, kunne løses i vertens navnerom, ikke i applikasjonscontainerens. Server Pros config/env.sh tvinger i tillegg TEXLIVE_IMAGE_USER=www-data i denne modusen, slik at filer skrevet av kompileringscontaineren får konsistent eierskap.
Verifiseringen er direkte: under en kompilering viser verten en container med navnet project-{projectId}-{userId}-{hash} som kjører latexmk fra TeX Live-imaget og avslutter med 0. Dette er enheten hvis antall vi måler gjennom hele artikkelen, og hvis fullstendige mangel på ressursgrenser vi rapporterer i §6.2.
Søskencontainere gjør målingen ren — hver kompilering er en observerbar, uavhengig planlagt OS-enhet — men de betyr også at gjestekjernen, ikke Overleaf, fordeler CPU og minne mellom kompileringer. Hver skaleringslov i denne artikkelen er derfor en egenskap ved Linux-planleggeren anvendt på entrådede prosesser, og det er derfor den er så regelmessig.

clsi. Ingen av dem dominerer — kompileringskostnaden for et prosjekt bestemmes av TeX Live-treet på 32 GiB som hver samtidige kompilering leser via den delte sidebufferen.

git-bridge. Toolkits standardoppsett (b), som er det vi måler, har en multiplikator på én, så en konstant dimensjonert for ett medlem av en flåte blir taket for hele installasjonen.

clsi-cache. Et prosjekt tilordnes ved , dvs. hashrommet deles inn i like mange like store sektorer som det finnes shards. Dette er modulo-hashing, ikke ringbasert konsistent hashing: å utvide flåten fra tre shards til fire partisjonerer hele rommet på nytt og omtilordner så å si hvert eneste prosjekt (a, b). Det er nettopp derfor implementasjonen trenger en eksplisitt rampe for omsharding på nett, som flytter en lineært voksende andel av prosjektene fra currentShards til desiredShards over et tidsvindu, i stedet for den -flyttingen en konsistent hashing-ring ville gitt. Når en shards kretsbryter er utløst, økes saltet og sharden fjernes fra kandidatlisten, slik at oppslaget prøver videre i stedet for å feile (c).
2.3 De to feilmodusene
Hver konfigurasjon vi målte, feiler på nøyaktig én av to måter, og forskjellen er synlig i responsstatusen i stedet for å måtte utledes:- Minneutmattelse — selve Overleaf-stakken slutter å svare, og forespørselen returnerer HTTP 502. Tilgjengelig gjesteminne på nivået som feiler, er typisk under 500 MiB.
- Tidsavbrudd for kompilering — CLSI avslutter kompileringen ved tidsavbruddet per bruker og rapporterer statusen
timedout. Tilgjengelig minne på nivået som feiler, er ofte flere gigabyte.
3. Metodikk
3.1 Testmiljø og klokkekontroll
Alle gjester kjører under QEMU/KVM på én enkelt Intel Core i9-14900K-vert med 62 GiB RAM og NVMe-lagring. Gjesten er Ubuntu 24.04 med Docker 29.7 og Overleaf Toolkit som distribuerer Ayakaleaf Pro v6.2.2 med sandkassekompilering mottexlive-full:2025.1.
En vanlig stasjonær CPU er en dårlig stedfortreder for en server med mindre klokken er kontrollert. KVM har ingen mekanisme for å angi en virtuell klokke: en vCPU er en vertstråd og kjører med den frekvensen vertskjernen kjører med. Vi begrenser derfor verten direkte ved å deaktivere turbo og låse scaling_max_freq til 3,0 GHz på hver kjerne, og vi fester gjestens vCPU-er til fysiske P-kjerner med taskset. Forskjellen er viktig på en CPU med hybride kjerner: E-kjernene i denne prosessoren har en basisklokke på 2,4 GHz og kan ikke nå 3,0 GHz når turbo er deaktivert, så en kjøring som havner på dem, måler i det stille en tregere maskin. Under full last verifiserer vi nøyaktig 3000 MHz på alle seksten festede tråder. Et vaktskript kontrollerer denne invarianten før hver benchmark og nekter å starte ellers; det fanget én stille tilbakestilling av governoren i løpet av studien.
3.2 Et andre testmiljø: én stor runner
QEMU-matrisen isolerer én variabel om gangen, men den stopper ved seksten festede tråder. For å undersøke om de samme lovene fortsatt holder en størrelsesorden høyere, gjentok vi samtidighetssveipet på én enkelt stor server: én AMD EPYC 7773X (Milan-X, 64 kjerner / 128 tråder, 768 MiB L3) med 995 GiB RAM, som kjørte det samme Ayakaleaf Pro v6.2.2-imaget mot den sammetexlive-full:2025.1. I motsetning til QEMU-gjestene er denne maskinen ikke klokkelåst: den er en server i produksjonsklassen, og vi måler den som en slik.
To driftsmessige forholdsregler var nødvendige og er verdt å nevne, fordi eksperimentet uten dem måler testoppsettet i stedet for serveren. For det første ble hver container begrenset til en systemd-slice med MemoryMax=940 GiB, slik at et løpsk sveip bruker opp en cgroup i stedet for verten. For det andre opprettes sandkassekompileringer av vertsdaemonen, og hver av dem skriver til sitt eget copy-on-write-lag — målt til 116 MiB per container selv om basisimaget på 20,6 GiB deles — så Docker-dataroten ble flyttet til en dedikert NVMe-enhet. Et sveip ved skriver omtrent 119 GiB med midlertidige lag, noe som ikke får plass på et standard rotfilsystem.
3.3 Arbeidsbelastning
Dokumentet er en ekte masteroppgave på 63 sider (SJTU-mal) kompilert med XeLaTeX vialatexmk, med TikZ-figurer, bibliografibehandling med biblatex og innebygde PDF-ressurser — altså en realistisk og ikke syntetisk belastning. Én enkelt kompilering på en ubelastet gjest tar 8,6–9,8 s på tvers av alle konfigurasjoner, og vi bruker dette som frittflygende grunnlinje .
3.4 Lastgenerering
Vi oppretter 512 ekte brukerkontoer og gir hver sin egen kopi av prosjektet, slik at samtidige kompileringer konkurrerer nøyaktig slik uavhengige brukere ville gjort, i stedet for å dele en prosjektlås. Forespørsler sendes fra verten mot gjestens videresendte port, slik at lastgenereringen ikke bruker gjestens CPU. Samtidigheten er simultan, ikke forskjøvet. Hver sesjon etableres først — pålogging, CSRF-token, valg av kompilator — og først deretter sover hver tråd til et felles tidspunkt, beregnet én gang og delt, før den sender sinPOST /project/:id/compile. Forskjellen er ikke pedantisk. En forskjøvet rampe måler gjennomstrømning under en stabil kø; et simultant utbrudd måler hva som skjer når en hel forelesningssal med studenter trykker på den samme knappen etter den samme fristkunngjøringen, som er det tilfellet driftsansvarlige faktisk frykter. De to skiller seg med mer enn en konstant faktor, fordi det andre fyller kompileringskøen raskere enn daemonen kan tømme den.
Fire praktiske hindringer måtte fjernes før dette utbruddet kunne leveres korrekt. Hver av dem er verdt å dokumentere, fordi hver av dem i det stille gjør eksperimentet om til en måling av testoppsettet i stedet for serveren.
3.4.1 To hastighetsbegrensere, ikke én
Overleaf struper pålogginger per kildeadresse — 20 forsøk per minutt — og all trafikken vår kommer fra én enkelt vert. Å gi hver simulert bruker en egenX-Forwarded-For-adresse fjerner den grensen, men støter umiddelbart på en annen, grovere grense: et budsjett per subnett på omtrent 200 per minutt. Å spre brukere over en sammenhengende blokk feiler derfor ved konto nummer 201. Vi utleder i stedet den syntetiske adressen fra brukerindeksen, slik at påfølgende brukere havner i ulike /24-er,
noe som holder begge begrenserne slakke for hele populasjonen på 1024.
3.4.2 Den injiserte headeren forkastes som standard
Det er ikke nok å sette headeren. Express respekterer bareX-Forwarded-For fra motparter den er bedt om å stole på, og Overleafs trustedProxyIps har standardverdien loopback. Fordi lastgeneratoren når applikasjonen via containerens bro i stedet for over loopback-grensesnittet, blir headeren tolket og deretter kastet, og alle simulerte brukere faller tilbake til én adresse. Symptomet er en bølge av HTTP 429 nøyaktig ved den tjuende påloggingen, noe som lett kan feiltolkes som serveroverbelastning. Gateway-nettverket må legges eksplisitt til i tillitskjeden; i den klyngebaserte distribusjonen i §4.3 må også pod- og tjeneste-CIDR-ene legges til.
3.4.3 En lastbalanserer overskriver headeren den ble bedt om å bevare
Når instansen står bak en proxy, legger den vanligeoption forwardfor den reelle klientadressen til i kjeden, noe som er riktig atferd i produksjon og nettopp feil her: den syntetiske adressen fortrenges av lastgeneratorens egen. Direktivet må kvalifiseres som option forwardfor if-none, slik at proxyen bare legger til en verdi når klienten ikke har oppgitt noen.
3.4.4 Klienten går tom for fildeskriptorer før serveren går tom for kapasitet
Ved holder generatoren mer enn tusen samtidige sockets, og den myke standardgrensen på 1024 deskriptorer nås under oppsett av sesjoner i stedet for under målingen. Feilen er stille: tre sesjoner blir ikke etablert og kjøringen rapporterer 1021 i stedet for 1024, mens en samplingstråd som kaller et skall for å telle containere, dør medEMFILE og i det stille avkorter telemetrien. Den myke grensen må økes på generatoren — den harde grensen på verten vår var allerede 1048576 — og kjøringen gjentas. Vi rapporterer begge kjøringene i §4.3: den korrigerte fullfører 1024 av 1024 med en median innenfor 1,2 s av den avkortede, og derfor behandler vi den første som brukbar, men ikke autoritativ.
3.5 Måleprotokoll
Flere metodiske valg viste seg nødvendige for reproduserbarhet.3.5.1 Oppvarming
På en nystartet gjest er sidebufferen tom, og de første kompileringene måler I/O ved kaldstart i stedet for kapasitet i stabil tilstand: den samme konfigurasjonen med 2 vCPU / 2 GiB gir 36,5 s kald og 9,8 s varm, en faktor på 3,7. Hver konfigurasjon utfører derfor to forkastede oppvarmingskompileringer etter oppstart.3.5.2 Godkjenningskriterium
Et samtidighetsnivå godkjennes bare hvis hver kompilering lykkes og nivået består en gjentakelse. Dette er strengere enn en terskel for suksessrate, og det har betydning: ved 4 vCPU / 16 GiB besto nivået 32 én gang med 80,2 s median og fikk deretter tidsavbrudd på alle 32 kompileringer ved gjentakelse, så vi rapporterer 31.3.5.3 Søk
Nivåer lokaliseres ved eksponentiell innramming fra et modellforutsagt startpunkt, etterfulgt av eksakt heltallsbiseksjon. Siden kriteriet er alt-eller-ingenting, avgjøres et nivå av sin første feil, så vi forlater de gjenværende pågående forespørslene så snart én feiler — unntatt på små nivåer, der de forlatte kompileringene belaster en liten gjest så hardt at den aldri kommer seg.3.5.4 Isolasjon mellom nivåer
Kompileringscontainere tømmes, og webapplikasjonen polles til den svarer igjen, før neste nivå starter. Uten dette registrerer et nivå som følger etter et krasj, en falsk feil med null sesjoner.3.5.5 Vertshygiene
Urelaterte virtuelle maskiner på verten ble slått av: med 24 GiB vertsminne bundet opp andre steder rapporterte den samme gjestekonfigurasjonen et lastgjennomsnitt på 11,7 i stedet for 3,2 ved identisk samtidighet. Minnepress på verten forplanter seg inn i gjesten og gjør målingen ugyldig.4. Resultater
4.1 Kapasitetsmatrisen
Tabell 1 og figur 4 viser det målte taket for hver konfigurasjon. Å lese den langs en rad er den første overraskelsen. Ved 4 GiB når gjestene med 2, 4 og 8 vCPU-er alle nøyaktig 9 — å firedoble antall kjerner endrer ingenting. Ved 16 GiB når de 54, 45 og 57: å gå fra 4 til 16 kjerner gir 6 %, og gjesten med 8 kjerner er faktisk dårligere enn den med 4 kjerner (§5.2). Først ved 48 GiB skiller antall kjerner konfigurasjonene tydelig: 143, 268 og 331.
Tabell 1. Maksimalt antall samtidige kompileringer som fullføres vellykket, målt med et tidsavbrudd for kompilering på 300 s og med samtidighetstaket i CLSI fjernet. Fet skrift markerer en CPU-bundet konfigurasjon (kompileringer får tidsavbrudd selv om det er minne til overs); resten er minnebundne (stakken dør med HTTP 502). Raden for 2 GiB inneholder korrigeringen som diskuteres i §5.2.
4.2 Samtidighet er tidsdeling
Figur 5 sveiper hvert samtidighetsnivå på en fast gjest med 8 vCPU / 16 GiB. To regimer er skilt av et skarpt kne ved nøyaktig én kompilering per kjerne. Under det er gjennomsnittlig kompileringstid flat — den går fra 8,7 s ved til 9,1 s ved , en endring på 5 %. Over det vokser tiden strengt proporsjonalt med : ved måler vi 18,5 s og 27,1 s, dvs. et forhold på mot ideelt .

4.3 Vertikal skalering til 1024 samtidige kompileringer
Tabell 2 og figur 7 rapporterer sveipet på den store runneren. Hvert nivå er en kald kompilering: før hvert nivå tømmer vi kompileringskatalogen og CLSI-bufferen for hvert deltakende prosjekt viaDELETE /project/:id/output, slik at intet nivå drar nytte av arbeid utført av nivået under. Grunnlinjen for én kompilering på denne maskinen er 28,8 s, som er kaldtallet og ikke bør sammenlignes med grunnlinjen på 8,6–9,8 s i stabil tilstand som ble brukt tidligere; den kalde grunnlinjen på QEMU-gjestene er 28,3 s, så per tråd ligger de to maskinene innenfor to prosent av hverandre for denne arbeidsbelastningen.
Tabell 2. Samtidighetssveip på én EPYC 7773X (64 kjerner / 128 tråder, 995 GiB). Alle nivåer kalde; grunnlinje 28,8 s. Maks. containere er det største antallet sandkasser som var i live samtidig.

4.3.1 Maskinen feiler aldri
Hvert nivå fullføres med 100 %, inkludert — åtte ganger antall tråder. Vi fant ikke kapasitetstaket for denne maskinen; tålmodigheten vår tok slutt før maskinens margin gjorde det. Dette er den første konfigurasjonen i studien der den begrensende faktoren ikke er minne: ved topper kompilerings-cgroupen på 184 GiB, en femtedel av taket på 940 GiB, mens CPU-en står på 100 % utnyttelse med et lastgjennomsnitt på 166.4.3.2 Degraderingen er sublineær fordi opptaket er hastighetsbegrenset
Naiv tidsdeling forutsier at trådene koster latensen. Den målte kostnaden er relativt til én enkelt kompilering, men bare relativt til — for en åttedobling av tilbudt last. Årsaken er synlig i figur 7(b) og i den siste kolonnen i tabell 2: selv om 1024 forespørsler sendes samtidig, overstiger antall sandkasser som faktisk er i live, aldri 205. Daemonen kan ikke opprette containere så raskt som klientene ber om, så forespørslene står i kø ved opptak i stedet for å konkurrere inne i CPU-en. Køen er det som redder halen her, og det skjer ved et tilfelle.4.3.3 Kneet ligger ved 512, ikke ved feilpunktet
Mellom og øker -latensen med for en dobling av lasten; hver tidligere dobling kostet mellom og . Kapasitet oppgitt som «den største som ikke feiler», ville rapportere 1024 og være ubrukelig for en driftsansvarlig: på det punktet er ventetiden i halen nesten åtte minutter.4.4 Kompileringstiden er omvendt proporsjonal med klokken
Siden arbeidsbelastningen er CPU-bundet, bør kostnaden skalere som . Vi tester dette direkte ved å sveipe vertsklokken over hele maskinens område, 1,0–5,5 GHz i ti trinn, på en ellers uendret gjest (figur 8). Tiden for én kompilering går fra 26,5 s til 4,8 s: en 5,5× klokke gir en 5,5× hastighetsøkning uten avtagende avkastning noe sted i området. Produktet er konstant innenfor 2 % over alle ti klokkefrekvensene. Normalisering med kjerneandelen samler alle tretti målingene — tre samtidighetsnivåer ved ti klokkefrekvenser — på én enkelt konstant: med en restspredning på 5,1 % over et område der selve klokken varierer med 5,5×. Fraværet av krumning er i seg selv resultatet: hadde arbeidsbelastningen vært begrenset av minnebåndbredde eller I/O, ville ha flatet ut ved høy klokke etter hvert som CPU-en løp fra den andre ressursen.
5. Analyse
5.1 To vegger, tilpasset hver for seg
Hver konfigurasjon klassifiseres etter feilsignaturen sin (§2.3), og minneveggen og CPU-taket tilpasses deretter bare på konfigurasjonene som faktisk treffer dem: med i gibibyte og i vCPU-er. Eksponenten for minneveggen er konsekvent superlineær, : den marginale minnekostnaden for én ekstra samtidig kompilering synker når det totale minnet vokser, fra omtrent 312 MiB per kompilering på en gjest med 3 GiB til rundt 194 MiB på en med 32 GiB. Mekanismen er den delte sidebufferen over TeX Live-treet som er beskrevet i §2.1: samtidige kompileringer leser overlappende font- og makrofiler, så en større buffer fordeles på flere av dem. Dette er grunnen til at den naive regelen «én gigabyte per fem brukere» underestimerer store maskiner og overestimerer små.
5.2 Der flere kjerner gjør ting verre
Likning (2) er et minimum av to ledd og derfor monoton i , men det er ikke målingene. Vi observerer to inversjoner der flere kjerner reduserte kapasiteten: ved 16 GiB (54 mot 45) og ved 32 GiB (145 mot 135). Begge forekommer i det minnebundne regimet, og mekanismen er den samme i begge: med flere kjerner går samtidige kompileringer i takt og når sin maksimale residente størrelse i samme øyeblikk, mens planleggeren med færre kjerner fletter dem og toppene forskyves. På en gjest der minnemarginen allerede er knapp, er det forskyvningen som holder den i live. En kapasitetsmodell bygget på gjennomsnittlig ressursbruk kan ikke uttrykke dette; det er en egenskap ved at toppene sammenfaller. En tredje tilsynelatende inversjon, ved 2 GiB, ser vi nå bort fra. Søket registrerer kapasitet 2 ved 2 vCPU, men 1 ved 4 og 8 vCPU, noe som ser ut som den samme effekten. En ny gjennomgang av de rå sveipene viser noe enklere: ved 2 GiB lyktes nivået ved første forsøk for alle tre antall kjerner og feilet deretter bekreftelseskjøringen for to av de tre. Nivået er ikke en kapasitet, men et myntkast, og oppføringen for 2 vCPU er kastet som tilfeldigvis landet riktig. Vi rapporterer derfor den reproduserbare verdien, 1, for alle tre antall kjerner og trekker ingen konklusjon fra forskjellen. Vi dokumenterer korrigeringen her i stedet for å endre tabellen i det stille, fordi den forkastede avlesningen er av den typen som ville ha støttet en interessant påstand.6. Funn i implementasjonen
6.1 Et hardkodet samtidighetstak
På tilstrekkelig store gjester stoppet kapasiteten ved nøyaktig 65 samtidige kompileringer uavhengig av forespurt samtidighet: ved målte vi vellykkede og umiddelbareunavailable-svar, med antall containere låst på 65, flere gigabyte minne ubrukt og median kompileringstid stabil på 77 s — langt under ethvert tidsavbrudd.
Årsaken er en konstant i CLSI:
success=65, unavailable=15 ved , i stedet success=80.
6.2 En virkningsløs minnegrense for containere
En inspeksjon av en aktiv kompileringscontainer viser ingen ressursisolasjon overhodet:HostConfig, der Docker-API-et forventer det, så det forkastes — noe den observerte Memory=0 bekrefter. Begge feilene finnes i commiten som introduserte filen (9a519f0d3d, mars 2018), og de overlevde konverteringen fra CoffeeScript, en reformatering av hele repositoriet og en migrering fra CJS til ESM, der ingen av dem gjennomgikk semantikken. Det er verdt å merke seg at MAX_OUTPUT = 1024 * 1024 // 1MB i den samme commiten er korrekt, noe som tyder på en glipp snarere enn en misforståelse.
Konsekvensen er synlig i målingene våre med lite minne. Fordi kompileringer er ubegrensede, viser minneutmattelse seg ikke ved at Docker avslutter én problematisk container; den tar ned hele gjesten. På konfigurasjonen med 2 vCPU / 2 GiB observerte vi at overvåkings-SSH-sesjonen var blokkert i 300 s, et lastgjennomsnitt på 68 på to kjerner, og at gjesten til slutt startet seg selv på nytt. En fungerende grense per container ville degradere langt mer kontrollert: den for store kompileringen ville feile, og tjenesten ville overleve.
Den ene grensen som faktisk virker, er RLIMIT_CPU, satt til sekunder. Den begrenser CPU-tid, ikke veggklokketid, og én enkelt kompilering bruker bare rundt 9 s CPU, så den slår aldri inn uansett samtidighet; den beskytter mot patologisk inndata som en løpsk makro. Den er imidlertid et nyttig orakel: å observere Soft:305 bekrefter at en innstilling for tidsavbrudd på 300 s faktisk har forplantet seg til containeren.
6.3 Tidsavbruddet for kompilering er den dominerende justeringsparameteren
Feltet per brukerfeatures.compileTimeout har standardverdien 180 s. For enhver CPU-bundet konfigurasjon er dette ikke en sikkerhetsmargin, men en kapasitetsinnstilling, fordi en maskin som fortsatt regner korrekt, erklæres som feilet. Å øke den til 300 s — én enkelt MongoDB-oppdatering — endrer den målte kapasiteten med opptil en faktor på 4,2 (tabell 3). Taket er 600 s, håndhevet av RequestParser.MAX_TIMEOUT, og over dette avkortes verdien i det stille.
Tabell 3. Effekten av tidsavbruddet for kompilering på målt kapasitet.
De to siste radene er den kontraintuitive halvdelen av resultatet og grunnen til at vi målte hver konfigurasjon på nytt under ett tidsavbrudd. For minne-bundne konfigurasjoner reduserer et lengre tidsavbrudd kapasiteten, fordi hver kompilering holder på sitt residente sett lenger og flere av dem overlapper. Et kapasitetstall er derfor meningsløst uten å oppgi tidsavbruddet det ble målt under, og de to kan ikke blandes i én tabell.
7. Relatert arbeid
7.1 Leverandørens veiledning
Overleafs egen maskinvaredokumentasjon slår fast de kvalitative fakta vi kvantifiserer her: at LaTeX er entrådet, at ytelsen per kjerne derfor styrer kompileringstiden, og at «flere kjerner vil bare hjelpe hvis du prøver å kompilere flere dokumenter enn du har ledige CPU-kjerner» [1]. Deretter gir den den lineære dimensjoneringsregelen — en base på 2 kjerner/3 GiB pluss én kjerne og én gigabyte per fem til ti samtidige brukere — som motiverte denne studien. Vårt bidrag er å gjøre disse utsagnene om til målte lover (likning (1) og (2)), og å vise hvor den lineære regelen bryter sammen: den har ikke noe ledd for den delte sidebufferen som gjør minneveggen superlineær, og ikke noe ledd for de to programvareparameterne som dominerer resultatet.7.2 Kapasitetsstudier av bygg og CI
Måling av byggesystemer under samtidighet er godt etablert utenfor LaTeX-sammenhengen. LightSys rapporterer at konvensjonelle CI-systemer som kompilerer inne i Docker-containere, får dårligere I/O når ankomstraten for pull requests øker, med en flaskehals som oppstår rundt elleve samtidige forespørsler [17]; TAOS-CI observerer at kompilering dominerer veggklokketiden i CI og utgjør 60–67 % av den totale varigheten av pipelinen i store prosjekter [18]. Systemet vårt skiller seg på ett punkt som viser seg å være avgjørende: en LaTeX-kompilering er interaktiv. En CI-jobb som tar dobbelt så lang tid, er en ulempe; en kompilering som tar dobbelt så lang tid, observeres direkte av en bruker som venter på et forhåndsvisningspanel, og derfor behandler vi tidsavbruddet ikke som en feilterskel, men som en kapasitetsparameter.7.3 Overhead for containere
Nyere arbeid dekomponerer oppstartslatensen for Docker-containere på tvers av lagringsnivåer [19] og karakteriserer containerytelse i edge-miljøer [20]. I vår sammenheng fordeles oppstarten av containere per kompilering: den er en liten konstant sammenlignet med en kompilering på 9 s, og den frittflygende tiden som vi tilpasser, absorberer den. Containeregenskapen som faktisk betyr noe, er fraværet av ressursgrenser (§6.2), som gjør et minneoverløp i én kompilering om til en feil for hele verten.7.4 LaTeX som upålitelig inndata
Sandkassekompilering finnes fordi TeX er et programmeringsspråk og dokumenter er upålitelig inndata [21, 22]. Dette designvalget er det som gjør denne studien mulig — hver kompilering er en isolert container med observerbar ressursatferd — og også det som gjør den manglende minnegrensen betydningsfull, siden isolasjon tas for gitt av driftsansvarlige som tar den i bruk.7.5 Kompilatoren som studieobjekt
TeX i seg selv er godt dokumentert som språk [16], men atferden som byggemål har først nylig fått oppmerksomhet. Tan og Rigger [8] kompilerer et stort korpus av arXiv-kilder på tvers av motorer og distribusjonsversjoner og finner at motorvalget ikke er utskiftbart: bare en brøkdel av en prosent av dokumentene gir byte-identisk utdata under XeTeX og pdfTeX. Det resultatet har direkte betydning for metodikken vår. Kapasitet er en egenskap ved et dokument og en motor, så en benchmark som ikke fastsetter begge, er ikke reproduserbar; vi fastsetter derfor ett dokument, én motor og én distribusjon (texlive-full:2025.1) gjennom hele studien, og vi oppgir motoren i hver figurtekst. Det begrenser også hvor generelle tallene våre er, på en måte som er verdt å si rett ut: de karakteriserer XeLaTeX på dette dokumentet, ikke TeX i abstrakt forstand.
Arbeid med byggesystemer for LaTeX er i stor grad drevet av praktikere. LaTeX3-prosjektets l3build [13] standardiserer regresjonstesting og pakking, og uavhengige benchmarker sammenligner wrapper-verktøy — en undersøkelse av 26 byggesystemer finner at en forhåndskompilert preambel gir omtrent 20 % gevinst sammenlignet med en vanlig kjøring og 40 % sammenlignet med latexmk [14]. Disse optimaliserer én enkelt kompilering. De er ortogonale til, og kan kombineres med, det vi måler: en preambelbuffer forkorter , og hvert kapasitetstall i denne artikkelen skalerer med .
7.6 Samtidighetskontroll i editoren, ikke i kompilatoren
Samarbeidsdelen av Overleaf hviler på en veletablert forskningstradisjon. Operasjonell transformasjon stammer fra Ellis og Gibbs [9] og ble gjort praktisk for klienter med høy latens av Jupiter-systemet [10], hvis design er gjenkjennelig idocument-updater: en server som ordner operasjoner, og en buffer per dokument som klientene synkroniserer mot. Konfliktfrie replikerte datatyper [11] løser det samme problemet uten en sentral sekvensierer. Dette skillet er det som i det hele tatt får topologien i §4.3 til å fungere: fordi bufferen for ventende oppdateringer ligger i delt Redis i stedet for i en instans’ minne, ser en kompilering som rutes til en hvilken som helst replika, de siste tastetrykkene, og kompileringstilhørighet kan velges ut fra bufferlokalitet i stedet for korrekthet.
7.7 Kapasitetsmodeller
Amdahls lov [24] begrenser hastighetsøkningen fra parallellitet, og Littles lov [23] relaterer belegg til ankomstrate og tjenestetid; begge brukes ovenfor. Gunthers universelle skalerbarhetslov [12] utvider den første med et retrograd ledd for koherensforsinkelse og forutsier at gjennomstrømningen når en topp og deretter synker. Vi merker oss at systemet vårt ikke viser dette retrograde regimet opp til : gjennomstrømningen metter seg og latensen vokser, men ingenting kollapser. Årsaken er strukturell snarere enn flaks — kompileringer deler ingen tilstand som må holdes koherent, så leddet loven legger til, er nær null, og opptaksplatået i §4.3 begrenser konkurransen før den kan få betydning.8. Anbefalinger for driftsansvarlige
1
Rett de to programvareparameterne før du kjøper maskinvare
Begge er gratis, og begge er verdt mer enn noen enkelt maskinvareoppgradering vi målte. Øk
features.compileTimeout til en verdi brukerne dine faktisk vil tolerere — maksimum CLSI godtar er 600 s — og hvis du forventer å overstige 65 samtidige kompileringer, enten hev compileConcurrencyLimit i et avledet image eller skaler ut. Å gjøre ingen av delene betyr å betale for kjerner som programvaren nekter å bruke.2
Dimensjoner én maskin etter kneet, ikke etter taket
Sveipet på den store runneren (§4.3) skiller to tall som rutinemessig blandes sammen. Taket — den største samtidigheten som fortsatt returnerer hver PDF — er minst 1024 på en server med 64 kjerner, og vi nådde det aldri. Kneet — punktet der halelatensen slutter å vokse forsiktig og begynner å doble seg — ligger ved 512, og det siste komfortable driftspunktet under det er 256. Mellom og går -ventetiden fra to minutter til nesten seks; mellom 512 og 1024 når den åtte. En driftsansvarlig som dimensjonerer etter taket, leverer et system som teknisk sett fungerer, men som ingen vil bruke.For denne maskinen og dette dokumentet er det anbefalte driftspunktet derfor 256 samtidige kompileringer, som er antall fysiske kjerner og antall tråder, og som holder nær 120 s. Vi foreslår å sette
compileConcurrencyLimit til den verdien i stedet for å la den være høy: å slippe inn 1024 kompileringer samtidig får alle til å vente åtte minutter, mens det å slippe inn 256 og sette resten i kø betjener de fleste brukerne på to. Kø gir dårligere opplevelse for de som kommer sent; konkurranse gir dårligere opplevelse for alle.3
Behandle disse som tall for verste tilfelle
Hvert nivå i tabell 2 er en kald kompilering som avfyres samtidig. Ingen av betingelsene gjelder i produksjon: en varm kompilering av det samme dokumentet tar 8,6 s mot 28,3 s kald, en faktor på , og ekte brukere trykker ikke på knappen i samme sekund. En populasjon i stabil tilstand som rekompilerer hvert annet minutt med en typisk buffertreffrate, vil derfor kunne betjene betydelig flere skribenter enn samtidighetstallet alene antyder — i størrelsesorden tusen eller flere aktive forfattere ved driftspunktet 256. Samtidighetstallet er en grense for det øyeblikkelige utbruddet, ikke et antall plasser.
4
Bestem latensbudsjettet først, og les deretter av størrelsen
Likning (1) kan inverteres direkte. For en målventetid ved klokke på kjerner er samtidigheten som passer, med for dette dokumentet. Et budsjett på 60 s på 8 kjerner ved 3 GHz gir ; et budsjett på 120 s dobler det. Å publisere budsjettet sammen med kapasiteten er den eneste ærlige måten å oppgi noen av dem på.
5
Kjøp minne først, deretter kjerner, og sjekk hvilken vegg du står ved
Under 32 GiB målte vi nesten ingen gevinst av flere kjerner. Diagnosen er billig: hvis feil viser seg som HTTP 502 mens gjesten mangler minne, legg til minne; hvis de viser seg som
timedout med minne til overs, legg til kjerner eller øk tidsavbruddet. Driftsansvarlige kan lese dette av den samme feilsignaturen som vi brukte til å klassifisere konfigurasjoner.6
Prioriter klokke for opplevelse, kjerner for populasjon
Fordi holder uten krumning (2 % over 1,0–5,5 GHz), gjør en raskere klokke hver kompilering raskere for hver bruker. Flere kjerner gjør ingen enkeltkompilering raskere; de slipper bare inn flere samtidige. Distribusjoner der klagen er «kompileringer er trege», bør kjøpe klokke; distribusjoner der klagen er «kompileringer feiler ved frister», bør kjøpe minne og kjerner.
7
Skaler ut i stedet for opp forbi taket
Utover 65 samtidige kompileringer er den støttede veien horisontal skalering (figur 2c og 10, utdypet i §9): flere applikasjonsinstanser bak en lastbalanserer med informasjonskapselbasert sesjonstilhørighet, som deler sentral MongoDB, Redis og S3-kompatibel lagring, med
git-bridge som én enkelt instans. Dette multipliserer taket per instans med antall instanser, som er nøyaktig hvordan SaaS-distribusjonen når sin egen kapasitet.8
Ikke stol på isolasjon per kompilering
Inntil minnegrensen i Docker-runneren er rettet (§6.2), kan ett enkelt patologisk dokument bruke opp verten i stedet for å bli drept alene. Driftsansvarlige som trenger den garantien, bør innføre den selv i stedet for å vente på den. Mekanismen vi brukte på den store verten, er en systemd-slice med et hardt tak, som Docker-daemonen deretter pekes mot, slik at hver container den oppretter, regnes inn under den:Én detalj her koster en hel ettermiddag hvis den overses. En slice med navnet
docker-capped.slice ligger ikke ved siden av docker.slice; den ligger inne i den, fordi bindestreken er hierarkiskilletegnet og ikke en del av navnet. Et tak som ikke ser ut til å ha noen effekt, er vanligvis brukt ett nivå unna der containerne faktisk ligger. Verifiser ved å lese toppverdien fra memory.max_usage_in_bytes etter en kjøring i stedet for å stole på konfigurasjonsfilen — på verten vår oversteg kompilerings-cgroupen aldri en femtedel av taket sitt, selv ved 1024 samtidige kompileringer, noe som i seg selv er beviset på at det var daemonen og ikke minnet som var den begrensende faktoren.
git-bridge, som holder repositorier på lokal disk uten noen replikeringsvei og må kjøre som én enkelt instans ved siden av én utpekt replika.
9. En referansedistribusjon på flere maskiner
Alt ovenfor måler én maskin. Denne delen beskriver den distribuerte formen detaljert nok til at den kan bygges, og — fordi spørsmålet en driftsansvarlig faktisk står overfor, ikke er hvordan, men om — angir først punktet der den blir verdt bryet.9.1 Når den distribuerte formen er berettiget
Én maskin er billigere å drifte på alle måter som betyr noe: ett feildomene, ingen delt tilstand å holde konsistent, ingen ruting å gjøre feil. Dataene våre setter tre terskler for når man bør forlate den.9.1.1 Under 65 samtidige kompileringer: ikke gjør det
Taket per instans er en programvarekonstant, ikke en maskinvarekonstant (§6.1). Inntil den tilbudte lasten nærmer seg den, gir en ekstra maskin flere feilmoduser og ingen gevinst. Verten med 64 kjerner betjente 256 samtidige kompileringer med full suksess først etter atcompileConcurrencyLimit ble hevet; en driftsansvarlig som ennå ikke har endret den ene verdien, er ikke begrenset av maskinvare og bør ikke handle maskinvare.
9.1.2 Mellom 65 og omtrent 500: skaler opp først
Vertikal skalering forble lineær over hele området vårt og gikk aldri inn i et retrograd regime. Én stor vert nådde 1024 samtidige kalde kompileringer med 100 % suksess (§4.3); kneet i latens dukket opp ved 512, ikke før. Innenfor dette båndet er én større maskin strengt tatt enklere enn flere mindre, og ifølge §4.4 forbedrer en raskere maskin opplevelsen for hver bruker i stedet for bare å slippe inn flere av dem.9.1.3 Gå distribuert for tilgjengelighet, ikke gjennomstrømning
Den ærlige grunnen til å kjøre mer enn én applikasjonsreplika under taket er at én maskin er én strømforsyning, én kjerne, ett oppgraderingsvindu. Det er en legitim grunn, og det er den vi selv ville oppgitt; det er rett og slett ikke et kapasitetsargument, og å blande de to sammen fører til at driftsansvarlige kjøper replikaer når de trengte minne.9.2 Nivåer og dimensjonering
Figur 10 viser topologien. Den har fire nivåer, og de skalerer på ulike størrelser — som er hele poenget med å skille dem.9.2.1 Kant
Én lastbalanserer, eller to for tilgjengelighet. Den terminerer TLS og gjør ikke noe kostbart; den skalerer med antall tilkoblinger, ikke antall kompileringer, og en liten instans er nok for belastningene som er studert her. Det er konfigurasjonen, ikke størrelsen, som betyr noe (§9.3).9.2.2 Applikasjonsreplikaer
Disse bærer kompileringslasten og er det eneste nivået som skalerer med samtidighet. Dimensjoner hver av dem etter reglene i §8 — minne før kjerner, deretter klokke — og sett så antall replikaer slik at det dekker maksimal samtidighet delt på taket per replika. Replikaer har ingen varig tilstand: den lokale disken deres inneholder midlertidige kompileringsfiler og en utdatabuffer, som begge kan gjenskapes. Det er dette som gjør det trygt å legge dem til og fjerne dem fritt, og det er verdt å verifisere i stedet for å anta, fordi én enkelt feilkonfigurertfilestore-sti i det stille gjør nivået om til et nivå med tilstand.
9.2.3 Tilstand
Redis, MongoDB og et S3-kompatibelt objektlager, på separate verter. Redis er den bærende komponenten og den minst åpenbare: den inneholder sesjonslageret og den aktive dokumentbufferen, som er det som gjør at en kompilering som rutes til en hvilken som helst replika, ser tastetrykk som ble skrevet mot en annen replika. En driftsansvarlig som behandler Redis som en buffer og dimensjonerer den for utkastelse, vil få kompileringer av utdaterte dokumenter som er ekstremt vanskelige å diagnostisere, fordi ingenting feiler — utdataene er bare feil. MongoDB skalerer med antall prosjekter i stedet for kompileringsrate. Objektlageret er valgfritt ved én replika og obligatorisk utover det.9.2.4 Enkeltinstansen
git-bridge holder repositorier på lokal disk, vedlikeholder en lokal indeks og har ingen replikeringsvei. Den må kjøre som nøyaktig én instans, festet ved siden av én utpekt replika, og det er komponenten som gjør at distribusjonen ikke er helt tilstandsløs. Planlegg verten deretter: det er disken dens som må sikkerhetskopieres.
Tabell 4. Referansenivåer. Bare applikasjonsnivået skalerer med samtidighet; dimensjoneringen av det er temaet for §8.
9.3 Ruting er den delen det er lett å gjøre feil
Tre klasser av forespørsler må nå tre ulike steder, og standardkonfigurasjonen med én enkelt regel oppfyller høyst to av dem. Kompileringstrafikk under/project/ bør fordeles med konsistent hashing på prosjektidentifikatoren, slik at et prosjekts kompileringsbuffer blir værende hos én replika. Vi bruker HAProxys balance hash path,field(3,/) med hash-type consistent og hash-balance-factor 150. Valget har betydning ved utskalering: med informasjonskapselbasert tilhørighet forblir eksisterende sesjoner festet til sin opprinnelige replika på ubestemt tid, og en nylig lagt til replika mottar bare nye brukere, så maskinen en driftsansvarlig nettopp har betalt for, tar ikke noe av lasten som motiverte kjøpet. Konsistent hashing omfordelte 35 % av prosjektene ved utskalering i konfigurasjonen vår, mot 0 % for informasjonskapsler.
Sesjonstrafikk er annerledes. Når WebSocket-oppgraderingen feiler og socket.io faller tilbake til XHR-polling, må påfølgende poll i én sesjon nå én replika, og det finnes ingen prosjektidentifikator i stien å hashe. Denne trafikken trenger en egen backend med informasjonskapselbasert tilhørighet. Vi utformet denne oppdelingen, men tok den ikke i bruk; vi markerer den som et hull i stedet for å påstå den.
Til slutt må /git/ nå replikaen som git-bridge kjører ved siden av. Den rutes til den replikaen i stedet for direkte til git-bridge, fordi broen autentiserer tilbakekallene sine mot applikasjonens OAuth-endepunkter og løser blob-URL-er via den; å gå utenom replikaen ødelegger autentiseringen i stedet for å forbedre noe.
9.4 Innskalering krever en dreneringsbuffer
Å fjerne en replika er ikke symmetrisk med å legge til en: en pågående kompilering går tapt, og brukeren ser en feil vedkommende ikke forårsaket. Den brukbare rekkefølgen er å stoppe ny trafikk først, vente, og først deretter avslutte. Vi implementerte dette som en pre-stop-hook som holder poden i et konfigurerbart intervall mens balansereren markerer backenden som drenerende — kort nok til å testes på minutter, og i produksjon langt nok til at en sesjon når sin naturlige slutt, timer snarere enn sekunder. Intervallet er justeringsparameteren som avgjør om elastisiteten er usynlig eller irriterende. En ytterligere begrensning fant vi ved måling snarere enn ved design: autoskalering på CPU fungerer ikke for denne arbeidsbelastningen. Applikasjonspodens egen utnyttelse lå på 22 m core mot en nodetotal på 3997 m core, fordi kompileringsarbeidet skjer i søskencontainere som poden ikke regner med. Ethvert signal som brukes til å skalere dette nivået, må telle kjørende kompileringscontainere, ikke pod-CPU.10. Implikasjoner utover Overleaf
Ingenting i §4.2 eller §4.3 er spesifikt for Overleafs kode. De målte lovene følger av tre egenskaper som enhver driftet LaTeX-tjeneste deler: arbeidsenheten er en entrådet prosess, den er isolert i en container, og arbeidssettet er et stort skrivebeskyttet tre som sidebufferen må holde. Tre konsekvenser kan overføres direkte til alle som bygger en slik tjeneste.10.1 Sørg for minne, deretter kjerner
Matrisens sterkeste resultat er negativt: under 16 GiB er antall kjerner nesten irrelevant, og først ved 48 GiB skiller konfigurasjonene med 4, 8 og 16 vCPU-er seg i det hele tatt (143, 268, 331). En driftsansvarlig som leser den konvensjonelle regelen som «legg til én kjerne per fem brukere», kjøper feil ressurs. Mekanismen er den delte sidebufferen over distribusjonstreet, og den er en egenskap ved TeX Lives størrelse snarere enn ved noe bestemt grensesnitt.10.2 Opptaksraten er en ressurs, og den glemmes vanligvis
Ved hadde serveren vår aldri mer enn 205 aktive sandkasser (figur 7b), selv om alle forespørslene kom samtidig. Oppretting av containere, ikke kompilering, var den begrensende faktoren — i samsvar med målestudier som tilskriver oppstartskostnaden for containere overhead i kjøretidsmiljøet snarere enn imagestørrelse [19, 20]. En tjeneste som bare dimensjonerer CPU og minne, vil oppdage at utbruddsatferden styres av en størrelse den aldri målte. Den praktiske formen av dette er anbefalingen i §8: begrens opptaket bevisst, fordi en kø du velger, er bedre enn en kø du oppdager.10.3 En sandkasse som lever lenger enn kompileringen, ugyldiggjør modellen
Hvert kapasitetstall her forutsetter at containeren opprettes, utfører én kompilering og avslutter — en levetid på titalls sekunder og en arbeidssyklus nær én bare mens den kjører. To nyere designmønstre bryter denne forutsetningen, og de bryter den på samme måte. Det første er den vedvarende sandkassen per bruker. Å tildele hver bruker et fast privat miljø gjør en statistisk multiplekset pool om til et sett med reservasjoner: en tjeneste som kunne betjene 256 samtidige kompileringer fra 64 kjerner ved tidsdeling, kan bare betjene 16 brukere hvis hver får fire dedikerte kjerner, en størrelsesorden færre for samme maskinvare. Dataene våre kvantifiserer kostnaden ved det valget i stedet for å argumentere mot det — reservasjoner kjøper forutsigbarhet, og vekslingskursen er omtrent ved driftspunktet vi anbefaler. Det andre, og nyere, er AI-agenten som deler sandkassen med kompilatoren. På skriveplattformer med agentstøtte kan den samme containeren som kjører XeLaTeX, også være vert for en langvarig kodeagent, slik at den er opptatt kontinuerlig i stedet for i utbrudd. Praktikere rapporterer nøyaktig det symptomet modellen forutsier for slike distribusjoner — vedvarende treghet ved beskjedne brukertall [15]. Samspillet er verdt å beskrive presist, fordi det ikke bare er «mer last». Tre av funnene våre forsterker hverandre. Belegget slutter å komme i utbrudd, så tidsdelingsloven i §4.2 gjelder for hele populasjonen samtidig i stedet for bare for andelen som kompilerer for øyeblikket. Sidebufferen, som er det som gir den superlineære minneavkastningen i §4.1, deles nå med agentens eget arbeidssett og slutter å være varm for TeX. Og den manglende minnegrensen for containere i §6.2 blir langt farligere, fordi en container som aldri avslutter, aldri gir tilbake minnet sitt. Vi målte ingen slik plattform og fremsetter ingen påstander om noe spesifikt produkt. Det vi kan si, er hva tallene våre innebærer for designet: en arkitektur som gir hver bruker en langlivet sandkasse med flere kjerner, bør dimensjoneres som et reservasjonssystem, ikke etter samtidighetstallene som rapporteres her, og kapasiteten den kan forvente, ligger nærmere antall kjerner delt på kjerner per bruker enn noe i tabell 1.11. Trusler mot validiteten
11.1 Ett enkelt dokument
Alle målinger bruker ett XeLaTeX-dokument på 63 sider. Absolutte kapasiteter vil være annerledes for andre dokumenter; skaleringslovene, som er forholdstall, bør ikke være det. Et dokument med et vesentlig større residentsett ville flytte minneveggen uten å endre dens superlineære karakter.11.2 Virtualisert vert
Gjestene kjører under KVM på én fysisk maskin, så absolutte tall inkluderer virtualiseringsoverhead, og gjestene deler vertens sidebuffer og NVMe-enhet. Vi reduserte den største forstyrrende faktoren ved å slå av urelaterte gjester etter å ha observert at minnepress på verten blåser opp lastgjennomsnittet i gjesten med mer enn ved identisk samtidighet.11.3 Samtidig ankomst
Hver kompilering sendes i ett og samme øyeblikk, noe som er verste tilfelle. Ekte brukere ankommer som en stokastisk prosess, så en distribusjon dimensjonert etter tallene våre har margin snarere enn underskudd — men toppen ved slutten av en innleveringsfrist ligger nærmere modellen vår enn en Poisson-modell.11.4 Grensekonfigurasjoner
Ved 2 GiB er systemet så nær kollaps at gjentatte kjøringer av samme konfigurasjon kan skille seg med én kompilering. Vi rapporterer den konservative verdien og trekker ingen konklusjoner fra forskjeller på i dette regimet.12. Tilgjengelighet
Systemet som er testet, distribusjonsverktøyene og upstream-prosjektet det er avledet fra, er alle offentlige:- Ayakaleaf Pro — https://github.com/ayaka-notes/ayakaleaf-pro
- Distribusjonsverktøy (toolkit) — https://github.com/ayaka-notes/toolkit
- Dokumentasjon — https://ayakaleaf-pro.ayaka.space
- Upstream Overleaf — https://github.com/overleaf/overleaf
- TeX Live-kompileringsimages —
ghcr.io/ayaka-notes/texlive-full:2025.1
9a519f0d3d, 5d472e9b38), finnes i Overleaf-historikken.

