Jes, mia malnova tekkomputilo estas plurfoje pli potenca ol via produktservilo

Ĉi tiuj estas ĝuste la plendoj, kiujn mi aŭdis de niaj programistoj. La plej interesa afero estas, ke tio rezultis vera, kaŭzante longan esploron. Ni parolos pri SQL-serviloj, kiuj funkcias per VMware.

Jes, mia malnova tekkomputilo estas plurfoje pli potenca ol via produktservilo

Efektive, estas facile certigi, ke la produktservilo estas senespere malantaŭ la tekkomputilo. Efektivigu (ne sur tempdb kaj ne sur datumbazo kun Malfrua Fortikeco ebligita) la kodon:

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

Sur mia labortablo ĝi daŭras 5 sekundojn, kaj sur la produktadservilo necesas 28 sekundoj. Ĉar SQL devas atendi la fizikan finon de la transakcia protokolo-eniro, kaj ni faras tre mallongajn transakciojn ĉi tie. Malglate parolante, ni veturis grandan, potencan kamionon en urban trafikon, kaj rigardis kiel ĝi estis impete preterpasita de picliveristoj sur skoteroj - trafluo ne estas grava ĉi tie, nur latenteco estas grava. Kaj neniu retstokado, negrave kiom da nuloj estas en ĝia prezo, povas venki la lokan SSD laŭ latenteco.

(en la komentoj montriĝis, ke mi mensogis - mi prokrastis fortikecon ambaŭloke. Sen prokrastita fortikeco rezultas:
Labortablo - 39 sekundoj, 15K tr/sec, 0.065ms/io rondveturo
PROD - 360 sekundoj, 1600 tr/sec, 0.6ms
Mi devus rimarki, ke ĝi estis tro rapida)

Tamen, en ĉi tiu kazo ni traktas bagatelaj nuloj de la Rimana zeta funkcio kun bagatela ekzemplo. En la ekzemplo, kiun la programistoj alportis al mi, ĝi estis malsama. Mi estis konvinkita, ke ili pravas, kaj komencis forigi el la ekzemplo ĉiujn iliajn specifaĵojn rilate al komerca logiko. Iam mi rimarkis, ke mi povus tute forĵeti ilian kodon kaj skribi mian propran - kio montras la saman problemon - en produktado ĝi funkcias 3-4 fojojn pli malrapide:

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

Se ĉio estas en ordo, tiam kontroli la unuarancon de nombro daŭros 6-7-8 sekundojn. Ĉi tio okazis sur kelkaj serviloj. Sed ĉe iuj, la kontrolo daŭris 25-40 sekundojn. Interese, ke ne ekzistis serviloj, kie ekzekuto daŭros, ekzemple, 14 sekundojn - la kodo funkciis aŭ tre rapide aŭ tre malrapide, tio estas, la problemo estis, ni diru, nigra kaj blanka.

Kion mi faris? Uzis VMware-metrikojn. Ĉio estis bone tie - estis abundo da rimedoj, Preta tempo = 0, estis sufiĉe da ĉio, dum la testo ĉe ambaŭ rapidaj kaj malrapidaj serviloj CPU = 100 sur unu vCPU. Mi faris teston por kalkuli la nombron Pi - la testo montris la samajn rezultojn sur iu ajn servilo. La odoro de nigra magio fariĝis pli kaj pli forta.

Post kiam mi alvenis al la DEV-bieno, mi komencis ludi kun la serviloj. Evidentiĝis, ke vMotion de gastiganto al gastiganto povas "kuraci" servilon, sed ĝi ankaŭ povas igi "rapidan" servilon en "malrapidan". Ŝajnas, ke ĉi tio estas - kelkaj gastigantoj havas problemon... sed... ne. Iu virtuala maŝino estis malrapida ĉe gastiganto, ekzemple A, sed rapide funkciis ĉe gastiganto B. Kaj alia virtuala maŝino, kontraŭe, funkciis rapide ĉe A kaj malrapidiĝis ĉe B! Ambaŭ "rapidaj" kaj "malrapidaj" maŝinoj ofte turniĝis sur la gastiganto!

De tiu momento, estis klara odoro de sulfuro en la aero. Post ĉio, la problemo ne povus esti atribuita al la virtuala maŝino (fenestroj, ekzemple) - finfine ĝi fariĝis "rapida" kun vMotion. Sed la problemo ankaŭ ne povus esti atribuita al la gastiganto - finfine ĝi povus havi ambaŭ "rapidajn" kaj "malrapidajn" maŝinojn. Ankaŭ ĉi tio ne rilatis al la ŝarĝo - mi sukcesis akiri "malrapidan" maŝinon sur la gastiganto, kie estis nenio krom ĝi.

Pro malespero, mi lanĉis Process Explorer de Sysinternals kaj rigardis la SQL-stakon. Sur malrapidaj maŝinoj la linio tuj kaptis mian atenton:

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
... transsaltis
sqldk.dll!SystemThread::MakeMiniSOSTthread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

Ĉi tio jam estis io. La programo estis skribita:

    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);
                }
            }
        }
    }

Ĉi tiu programo montris eĉ pli prononcitan malrapidiĝon - ĉe "rapidaj" maŝinoj ĝi montras 16-18 milionojn da cikloj sekundo, dum sur malrapidaj maŝinoj ĝi montras unu kaj duonon, aŭ eĉ 700 mil. Tio estas, la diferenco estas 10-20 fojojn (!!!). Ĉi tio jam estis malgranda venko: ĉiukaze ne estis minaco blokiĝi inter Microsoft kaj VMware-subteno por ke ili turnu sagojn unu sur la alian.

Tiam progreso ĉesis - ferioj, gravaj aferoj, virusa histerio kaj akra pliiĝo de laborkvanto. Mi ofte menciis la magian problemon al miaj kolegoj, sed foje ŝajnis, ke ili eĉ ne ĉiam kredis min - la deklaro, ke VMware malrapidigas la kodon 10-20 fojojn, estis tro monstra.

Mi provis elfosi min tion, kio malrapidigis min. Foje ŝajnis al mi, ke mi trovis solvon - ŝalti kaj malŝalti Varmajn ŝtopilon, ŝanĝi la kvanton da memoro aŭ la nombron da procesoroj ofte igis la maŝinon "rapida". Sed ne eterne. Sed kio montriĝis vera estas, ke sufiĉas eliri kaj frapi la radon - tio estas, ŝanĝi. iu ajn virtuala maŝino parametro

Fine, miaj usonaj kolegoj subite trovis la radikan kaŭzon.

Jes, mia malnova tekkomputilo estas plurfoje pli potenca ol via produktservilo

La gastigantoj malsamis en ofteco!

  • Kiel regulo, ĉi tio ne estas granda afero. Sed: kiam oni transiras de "denaska" gastiganto al gastiganto kun "malsama" ofteco, VMware devas ĝustigi la rezulton de GetTimePrecise.
  • Kiel regulo, ĉi tio ne estas problemo, krom se ekzistas aplikaĵo, kiu petas la ĝustan tempon milionojn da fojoj sekundo, kiel SQL-servilo.
  • Sed ĉi tio ne estas timiga, ĉar SQL-servilo ne ĉiam faras tion (vidu Konkludon)

Sed estas kazoj kiam ĉi tiu rastilo forte batas. Kaj tamen, jes, frapante sur la rado (ŝanĝante ion en la VM-agordoj) mi devigis VMware 'rekalkuli' la agordon, kaj la ofteco de la nuna gastiganto fariĝis la 'denaska' ofteco de la maŝino.

decido

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

Kiam vi malŝaltas virtualigon de la TSC, legado de la TSC de ene de la virtuala maŝino resendas la TSC-valoron de la fizika maŝino, kaj skribi la TSC de ene de la virtuala maŝino ne havas efikon. Migri la virtualan maŝinon al alia gastiganto, rekomenci ĝin de suspendita stato, aŭ reveni al momentfoto igas la TSC salti malkontinue. Iuj gastoperaciumoj malsukcesas lanĉi, aŭ elmontras aliajn tempokonservajn problemojn, kiam TSC-virtualigo estas malŝaltita. En la pasinteco, ĉi tiu funkcio foje estis rekomendita por plibonigi agadon de aplikoj, kiuj legas la TSC ofte, sed efikeco de la virtuala TSC estis plibonigita sufiĉe en nunaj produktoj. La funkcio ankaŭ estis rekomendita por uzo dum elfarado de mezuradoj kiuj postulas precizan fonton de reala tempo en la virtuala maŝino.

Mallonge, vi devas aldoni la parametron

monitor_control.virtual_rdtsc = FALSA

konkludo

Vi verŝajne havas demandon: kial SQL tiel ofte nomas GetTimePrecise?

Mi ne havas la fontkodon de SQL-servilo, sed la logiko diras tion. SQL estas preskaŭ operaciumo kun kunlabora samtempeco, kie ĉiu fadeno devas "cedi" de tempo al tempo. Kie estas la plej bona loko por fari ĉi tion? Kie estas natura atendo - seruro aŭ IO. Bone, sed kio se ni turnas komputajn buklojn? Tiam la evidenta kaj preskaŭ nura loko estas en la interpretisto (ĉi tio ne vere estas interpretisto), post ekzekuto de la sekva aserto.

Ĝenerale, SQL-servilo ne estas uzata por pura komputika najlado kaj ĉi tio ne estas problemo. Sed bukloj kiuj funkcias kun ĉiaj provizoraj tabeloj (kiuj tuj estas kaŝmemorigitaj) turnas la kodon en sekvencon de tre rapide efektivigitaj deklaroj.

Cetere, se vi envolvas la funkcion en NATIVE KOMPILITA, tiam ĝi ĉesas peti tempon, kaj ĝia rapido pliiĝas je 10 fojojn.Kion pri kunlabora plurtasking? Sed por denaske kompilita kodo ni devis fari PREEMPTIVE MULTITASKING en SQL.

fonto: www.habr.com

Aldoni komenton