بله، لپ تاپ قدیمی من چندین برابر قدرتمندتر از سرور تولید شما است

این دقیقاً شکایاتی است که من از توسعه دهندگان خود شنیدم. جالب ترین چیز این است که این درست است و باعث تحقیقات طولانی می شود. ما در مورد سرورهای SQL که روی VMware اجرا می شوند صحبت خواهیم کرد.

بله، لپ تاپ قدیمی من چندین برابر قدرتمندتر از سرور تولید شما است

در واقع، اطمینان از اینکه سرور تولید به طور ناامیدکننده ای پشت لپ تاپ است، آسان است. کد را اجرا کنید (نه در tempdb و نه در پایگاه داده با دوام تاخیری فعال)

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

در دسکتاپ من 5 ثانیه و در سرور تولید 28 ثانیه طول می کشد. زیرا SQL باید منتظر پایان فیزیکی ورود گزارش تراکنش باشد و ما در اینجا تراکنش های بسیار کوتاهی را انجام می دهیم. به طور کلی، ما یک کامیون بزرگ و قدرتمند را وارد ترافیک شهری کردیم و دیدیم که افراد تحویل دهنده پیتزا در اسکوترها از آن سبقت می‌گیرند - توان عملیاتی در اینجا مهم نیست، فقط تأخیر مهم است. و هیچ فضای ذخیره سازی شبکه ای، مهم نیست که قیمت آن چند صفر باشد، نمی تواند SSD محلی را از نظر تأخیر شکست دهد.

(در نظرات معلوم شد که دروغ گفتم - دوام را در هر دو جا به تعویق انداختم. بدون تاخیر در ماندگاری معلوم می شود:
رومیزی - 39 ثانیه، 15 هزار تریلیون در ثانیه، 0.065 میلی‌ثانیه /یو رفت و برگشت
PROD - 360 ثانیه، 1600 tr/sec، 0.6ms
باید توجه می کردم که خیلی سریع بود)

با این حال، در این مورد با یک مثال بی اهمیت با صفرهای جزئی تابع زتای ریمان سروکار داریم. در مثالی که توسعه دهندگان برای من آوردند، قضیه متفاوت بود. من متقاعد شدم که آنها درست می گویند، و شروع کردم به حذف تمام مشخصات آنها در رابطه با منطق تجاری از مثال. در نقطه ای متوجه شدم که می توانم کد آنها را کاملا دور بریزم و کد خودم را بنویسم - که همین مشکل را نشان می دهد - در تولید 3-4 برابر کندتر اجرا می شود:

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

اگر همه چیز خوب باشد، بررسی اولیه بودن یک عدد 6-7-8 ثانیه طول می کشد. این اتفاق روی تعدادی از سرورها افتاد. اما در برخی، بررسی 25-40 ثانیه طول کشید. جالب اینجاست که هیچ سروری وجود نداشت که اجرای آن مثلاً 14 ثانیه طول بکشد - کد یا خیلی سریع یا خیلی کند کار می کرد، یعنی مشکل، مثلاً سیاه و سفید بود.

من چه کرده ام؟ از معیارهای VMware استفاده شده است. همه چیز در آنجا خوب بود - منابع فراوانی وجود داشت، زمان آماده = 0، همه چیز به اندازه کافی وجود داشت، در طول آزمایش روی سرورهای سریع و آهسته CPU = 100 در یک vCPU. من آزمایشی برای محاسبه عدد Pi انجام دادم - آزمایش نتایج یکسانی را در هر سروری نشان داد. بوی جادوی سیاه بیشتر و قوی تر شد.

وقتی به مزرعه DEV رسیدم، شروع به بازی با سرورها کردم. مشخص شد که vMotion از میزبانی به میزبان دیگر می‌تواند یک سرور را «درمان» کند، اما همچنین می‌تواند یک سرور «سریع» را به یک سرور «آهسته» تبدیل کند. به نظر می رسد این است - برخی از هاست ها مشکل دارند ... اما ... نه. بعضی از ماشین های مجازی روی هاست A کند بود، اما روی هاست B سریع کار می کرد و ماشین مجازی دیگر، برعکس، روی A سریع کار می کرد و روی B کند شد! هر دو دستگاه "سریع" و "آهسته" اغلب روی میزبان می چرخیدند!

از همان لحظه بوی گوگرد در هوا به مشام می رسید. از این گذشته ، مشکل را نمی توان به ماشین مجازی نسبت داد (به عنوان مثال وصله های ویندوز) - از این گذشته ، با vMotion به "سریع" تبدیل شد. اما این مشکل همچنین نمی تواند به میزبان نسبت داده شود - از این گذشته ، می تواند ماشین های "سریع" و "آهسته" داشته باشد. همچنین ، این به بار ارتباطی نداشت - من موفق شدم یک دستگاه "آهسته" را روی میزبان دریافت کنم که در آن هیچ چیز دیگری وجود نداشت.

از سر ناامیدی، Process Explorer را از Sysinternals راه اندازی کردم و به پشته SQL نگاه کردم. در ماشین های آهسته، خط بلافاصله نظرم را جلب کرد:

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

این قبلا چیزی بود برنامه نوشته شده بود:

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

این برنامه کاهش سرعت بیشتری را نشان داد - در ماشین های "سریع" 16-18 میلیون چرخه در ثانیه را نشان می دهد، در حالی که در ماشین های آهسته یک و نیم میلیون یا حتی 700 هزار را نشان می دهد. یعنی تفاوت 10-20 برابر (!!!) است. این قبلاً یک پیروزی کوچک بود: در هر صورت، هیچ تهدیدی برای گیر کردن بین پشتیبانی مایکروسافت و VMware وجود نداشت تا آنها به یکدیگر تیر بزنند.

سپس پیشرفت متوقف شد - تعطیلات، مسائل مهم، هیستری ویروسی و افزایش شدید حجم کار. من اغلب مشکل جادویی را برای همکارانم ذکر می‌کردم، اما گاهی به نظر می‌رسید که آنها حتی همیشه من را باور نمی‌کردند - این بیانیه که VMware کد را 10 تا 20 برابر کند می‌کند خیلی وحشتناک بود.

سعی کردم از خودم کشف کنم که چه چیزی باعث کاهش سرعتم شده بود. گاهی اوقات به نظرم می رسید که راه حلی پیدا کرده ام - روشن و خاموش کردن پریزهای داغ، تغییر مقدار حافظه یا تعداد پردازنده ها اغلب دستگاه را به یک دستگاه "سریع" تبدیل می کند. اما نه برای همیشه. اما چیزی که معلوم شد این است که کافی است بیرون بروید و روی چرخ بکوبید - یعنی تغییر دهید любой پارامتر ماشین مجازی

سرانجام، همکاران آمریکایی من ناگهان علت اصلی را پیدا کردند.

بله، لپ تاپ قدیمی من چندین برابر قدرتمندتر از سرور تولید شما است

میزبان ها در فرکانس متفاوت بودند!

  • به عنوان یک قاعده، این یک معامله بزرگ نیست. اما: هنگام انتقال از یک میزبان «بومی» به میزبانی با فرکانس «متفاوت»، VMware باید نتیجه GetTimePrecise را تنظیم کند.
  • به عنوان یک قاعده، این مشکلی نیست، مگر اینکه برنامه ای وجود داشته باشد که میلیون ها بار در ثانیه زمان دقیق را درخواست کند، مانند سرور SQL.
  • اما این ترسناک نیست، زیرا سرور SQL همیشه این کار را انجام نمی دهد (به نتیجه مراجعه کنید)

اما مواردی وجود دارد که این چنگک به شدت ضربه می زند. و با این حال، بله، با ضربه زدن روی چرخ (با تغییر چیزی در تنظیمات VM) VMware را مجبور کردم تا پیکربندی را دوباره محاسبه کند، و فرکانس میزبان فعلی به فرکانس «بومی» دستگاه تبدیل شد.

تصمیم

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

وقتی مجازی‌سازی TSC را غیرفعال می‌کنید، خواندن TSC از داخل ماشین مجازی، مقدار TSC ماشین فیزیکی را برمی‌گرداند و نوشتن TSC از داخل ماشین مجازی هیچ تاثیری ندارد. انتقال ماشین مجازی به میزبان دیگر، از سرگیری آن از حالت تعلیق، یا بازگشت به یک عکس فوری باعث می شود TSC به طور ناپیوسته پرش کند. وقتی مجازی‌سازی TSC غیرفعال است، برخی از سیستم‌عامل‌های مهمان بوت نمی‌شوند یا مشکلات زمان‌سنجی دیگری را نشان می‌دهند. در گذشته، گاهی اوقات این ویژگی برای بهبود عملکرد برنامه هایی که به طور مکرر TSC را می خوانند، توصیه می شد، اما عملکرد TSC مجازی در محصولات فعلی به طور قابل توجهی بهبود یافته است. این ویژگی همچنین برای استفاده در هنگام انجام اندازه گیری هایی که نیاز به منبع دقیق زمان واقعی در ماشین مجازی دارند، توصیه شده است.

به طور خلاصه، شما باید پارامتر را اضافه کنید

monitor_control.virtual_rdtsc = FALSE

نتیجه

احتمالاً یک سوال دارید: چرا SQL اغلب GetTimePrecise را فراخوانی می کند؟

من کد منبع SQL سرور را ندارم، اما منطق این را می گوید. SQL تقریباً یک سیستم عامل با همزمانی مشارکتی است، که در آن هر رشته باید هر از گاهی "تسلیم شود". بهترین مکان برای این کار کجاست؟ جایی که انتظار طبیعی وجود دارد - قفل یا IO. خوب، اما اگر حلقه‌های محاسباتی را بچرخانیم چه؟ سپس مکان آشکار و تقریباً تنها در مفسر است (این واقعاً یک مفسر نیست)، پس از اجرای دستور بعدی.

به طور کلی سرور SQL برای محاسبات محاسباتی خالص استفاده نمی شود و این مشکلی ندارد. اما حلقه‌هایی که با انواع جدول‌های موقت کار می‌کنند (که بلافاصله در حافظه پنهان ذخیره می‌شوند) کد را به دنباله‌ای از دستورات بسیار سریع اجرا می‌کنند.

ضمناً، اگر تابع را در NATIVELY COMPILED بپیچید، دیگر درخواست زمان را متوقف می کند و سرعت آن 10 برابر افزایش می یابد. اما برای کدهای کامپایل شده بومی، ما مجبور بودیم چندوظیفه ای پیشگیرانه را در SQL انجام دهیم.

منبع: www.habr.com

اضافه کردن نظر