Ja, min gamle bærbare computer er flere gange stærkere end din produktionsserver

Det er præcis de klager, jeg har hørt fra vores udviklere. Det mest interessante er, at dette viste sig at være sandt, hvilket gav anledning til en lang undersøgelse. Vi vil tale om SQL-servere, der kører på VMware.

Ja, min gamle bærbare computer er flere gange stærkere end din produktionsserver

Faktisk er det nemt at sikre, at produktionsserveren er håbløst bag den bærbare computer. Udfør (ikke på tempdb og ikke på en database med Delayed Durability aktiveret) koden:

set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin 
  insert into _t select 'What a slowpoke!'
  delete from _t
  set @n=@n-1
  end
GO
drop table _t

På mit skrivebord tager det 5 sekunder, og på produktionsserveren tager det 28 sekunder. Fordi SQL skal vente på den fysiske slutning af transaktionslogposten, og vi laver meget korte transaktioner her. Groft sagt kørte vi en stor, kraftfuld lastbil ud i bytrafikken, og så på, hvordan den blev overhalet af pizzabude på scootere – gennemløbet er ikke vigtigt her, kun latens er vigtig. Og ingen netværkslagring, uanset hvor mange nuller der er i prisen, kan slå den lokale SSD med hensyn til latenstid.

(i kommentarerne viste det sig, at jeg løj - jeg havde forsinket holdbarhed begge steder. Uden forsinket holdbarhed viser det sig:
Desktop - 39 sekunder, 15K tr/sek., 0.065ms /io tur/retur
PROD - 360 sekunder, 1600 tr/sek., 0.6ms
Jeg burde have bemærket, at det var for hurtigt)

Men i dette tilfælde har vi at gøre med trivielle nuller af Riemann zeta-funktionen med et trivielt eksempel. I det eksempel, som udviklerne bragte mig, var det anderledes. Jeg var overbevist om, at de havde ret, og begyndte at fjerne alle deres detaljer relateret til forretningslogik fra eksemplet. På et tidspunkt indså jeg, at jeg helt kunne smide deres kode og skrive min egen - hvilket demonstrerer det samme problem - i produktionen kører den 3-4 gange langsommere:

create function dbo.isPrime (@n bigint)
returns int
as
  begin
  if @n = 1 return 0
  if @n = 2 return 1
  if @n = 3 return 1
  if @n % 2 = 0 return 0
  declare @sq int
  set @sq = sqrt(@n)+1 -- check odds up to sqrt
  declare @dv int = 1
  while @dv < @sq 
    begin
	set @dv=@dv+2
	if @n % @dv = 0 return 0
	end
  return 1
  end
GO
declare @dt datetime set @dt=getdate()
select dbo.isPrime(1000000000000037)
select datediff(ms,@dt,getdate()) as ms
GO

Hvis alt er i orden, vil det tage 6-7-8 sekunder at kontrollere et nummers primaalitet. Dette skete på en række servere. Men på nogle tog kontrollen 25-40 sekunder. Interessant nok var der ingen servere, hvor eksekveringen ville tage f.eks. 14 sekunder – koden virkede enten meget hurtigt eller meget langsomt, det vil sige, at problemet var, lad os sige, sort/hvid.

Hvad jeg har gjort? Brugte VMware-metrics. Alt var fint der - der var en overflod af ressourcer, klar tid = 0, der var nok af alt, under testen på både hurtige og langsomme servere CPU = 100 på én vCPU. Jeg tog en test for at beregne tallet Pi - testen viste de samme resultater på enhver server. Duften af ​​sort magi blev stærkere og stærkere.

Da jeg kom til DEV-farmen, begyndte jeg at lege med serverne. Det viste sig, at vMotion fra vært til vært kan "kurere" en server, men den kan også gøre en "hurtig" server til en "langsom". Det ser ud til, at dette er det - nogle værter har et problem... men... nej. En eller anden virtuel maskine var langsom på vært, siger A, men arbejdede hurtigt på vært B. Og en anden virtuel maskine, tværtimod, arbejdede hurtigt på A og satte farten ned på B! Både "hurtige" og "langsomme" maskiner snurrede ofte på værten!

Fra det øjeblik var der en tydelig lugt af svovl i luften. Trods alt kunne problemet ikke tilskrives den virtuelle maskine (f.eks. Windows-patches) - det blev trods alt til "hurtigt" med vMotion. Men problemet kunne heller ikke tilskrives værten - den kunne trods alt have både "hurtige" og "langsomme" maskiner. Dette var heller ikke relateret til belastningen - det lykkedes mig at få en "langsom" maskine på værten, hvor der overhovedet ikke var noget udover det.

Ud af desperation lancerede jeg Process Explorer fra Sysinternals og kiggede på SQL-stakken. På langsomme maskiner fangede linjen mig straks:

ntoskrnl.exe!KeSynchronizeExecution+0x5bf6
ntoskrnl.exe!KeWaitForMultipleObjects+0x109d
ntoskrnl.exe!KeWaitForMultipleObjects+0xb3f
ntoskrnl.exe!KeWaitForSingleObject+0x377
ntoskrnl.exe!KeQuerySystemTimePrecise+0x881 < — !!!
ntoskrnl.exe!ObDereferenceObjectDeferDelete+0x28a
ntoskrnl.exe!KeSynchronizeExecution+0x2de2
sqllang.dll!CDiagThreadSafe::PxlvlReplace+0x1a20
... sprunget over
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

Det her var allerede noget. Programmet blev skrevet:

    class Program
    {
        [DllImport("kernel32.dll")]
        static extern void GetSystemTimePreciseAsFileTime(out FILE_TIME lpSystemTimeAsFileTime);

        [StructLayout(LayoutKind.Sequential)]
        struct FILE_TIME
        {
            public int ftTimeLow;
            public int ftTimeHigh;
        }

        static void Main(string[] args)
        {
            for (int i = 0; i < 16; i++)
            {
                int counter = 0;

                var stopwatch = Stopwatch.StartNew();

                while (stopwatch.ElapsedMilliseconds < 1000)
                {
                    GetSystemTimePreciseAsFileTime(out var fileTime);
                    counter++;
                }

                if (i > 0)
                {
                    Console.WriteLine("{0}", counter);
                }
            }
        }
    }

Dette program demonstrerede en endnu mere udtalt afmatning - på "hurtige" maskiner viser det 16-18 millioner cyklusser i sekundet, mens det på langsomme maskiner viser halvanden million eller endda 700 tusind. Det vil sige, at forskellen er 10-20 gange (!!!). Dette var allerede en lille sejr: Under alle omstændigheder var der ingen trussel om at sidde fast mellem Microsoft og VMware-support, så de ville vende pile mod hinanden.

Så stoppede fremskridtet - ferier, vigtige sager, viralt hysteri og en kraftig stigning i arbejdsbyrden. Jeg nævnte ofte det magiske problem for mine kolleger, men til tider så det ud til, at de ikke engang altid troede på mig - udtalelsen om, at VMware sænker koden 10-20 gange, var for monstrøs.

Jeg prøvede selv at grave frem, hvad der bremsede mig. Til tider forekom det mig, at jeg havde fundet en løsning - at tænde og slukke for Hot plugs, ændre mængden af ​​hukommelse eller antallet af processorer gjorde ofte maskinen til en "hurtig" en. Men ikke for evigt. Men det, der viste sig at være rigtigt, er, at det er nok at gå ud og banke på rattet – altså skifte enhver virtuel maskine parameter

Endelig fandt mine amerikanske kolleger pludselig årsagen.

Ja, min gamle bærbare computer er flere gange stærkere end din produktionsserver

Værterne var forskellige i frekvens!

  • Som regel er dette ikke en big deal. Men: når man flytter fra en 'native' vært til en vært med en 'anden' frekvens, skal VMware justere GetTimePrecise-resultatet.
  • Som regel er dette ikke et problem, medmindre der er et program, der anmoder om den nøjagtige tid millioner af gange i sekundet, som SQL-server.
  • Men dette er ikke skræmmende, da SQL-server ikke altid gør dette (se konklusion)

Men der er tilfælde, hvor denne rake rammer hårdt. Og alligevel, ja, ved at trykke på hjulet (ved at ændre noget i VM-indstillingerne) tvang jeg VMware til at 'genberegne' konfigurationen, og frekvensen af ​​den aktuelle vært blev maskinens 'native' frekvens.

beslutning

www.vmware.com/files/pdf/techpaper/Timekeeping-In-VirtualMachines.pdf

Når du deaktiverer virtualisering af TSC'en, returnerer læsning af TSC'en fra den virtuelle maskine den fysiske maskines TSC-værdi, og at skrive TSC'en inde fra den virtuelle maskine har ingen effekt. Migrering af den virtuelle maskine til en anden vært, genoptagelse af den fra suspenderet tilstand eller tilbagevenden til et snapshot får TSC'en til at hoppe diskontinuerligt. Nogle gæsteoperativsystemer kan ikke starte eller udviser andre tidtagningsproblemer, når TSC-virtualisering er deaktiveret. Tidligere er denne funktion nogle gange blevet anbefalet for at forbedre ydeevnen af ​​programmer, der læser TSC ofte, men ydeevnen af ​​den virtuelle TSC er blevet væsentligt forbedret i nuværende produkter. Funktionen er også blevet anbefalet til brug ved udførelse af målinger, der kræver en præcis kilde til realtid i den virtuelle maskine.

Kort sagt skal du tilføje parameteren

monitor_control.virtual_rdtsc = FALSK

Konklusion

Du har sikkert et spørgsmål: hvorfor kalder SQL GetTimePrecise så ofte?

Jeg har ikke SQL-serverens kildekode, men logikken siger dette. SQL er nærmest et styresystem med cooperativ concurrency, hvor hver tråd skal "give efter" fra tid til anden. Hvor er det bedste sted at gøre dette? Hvor der er en naturlig ventetid - lås eller IO. Okay, men hvad nu hvis vi spinder beregningsløkker? Så er det åbenlyse og næsten eneste sted i tolken (dette er egentlig ikke en tolk), efter at have udført den næste sætning.

Generelt bruges SQL server ikke til ren computing nailing, og dette er ikke et problem. Men sløjfer, der fungerer med alle mulige midlertidige tabeller (som straks cachelagres) gør koden til en sekvens af meget hurtigt udførte udsagn.

Forresten, hvis du pakker funktionen ind i NATIVELY COMPILED, så holder den op med at spørge efter tid, og dens hastighed øges med 10 gange Hvad med cooperativ multitasking? Men for indbygget kompileret kode var vi nødt til at udføre PREEMPTIVE MULTITASKING i SQL.

Kilde: www.habr.com

Tilføj en kommentar