نعم ، جهاز الكمبيوتر المحمول القديم أقوى بعدة مرات من خادم الإنتاج لديك.

هذه هي بالضبط الشكاوى التي سمعتها من مطورينا. والشيء الأكثر إثارة للاهتمام هو أنه تبين أن هذا صحيح، مما أدى إلى تحقيق طويل. سنتحدث عن خوادم SQL التي تعمل على برنامج VMware.

نعم ، جهاز الكمبيوتر المحمول القديم أقوى بعدة مرات من خادم الإنتاج لديك.

في الواقع، من السهل التأكد من أن خادم الإنتاج يتخلف بشكل يائس عن الكمبيوتر المحمول. قم بتنفيذ الكود (ليس على tempdb وليس على قاعدة بيانات مع تمكين Delayed Durability):

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 مللي ثانية/IO ذهابًا وإيابًا
PROD - 360 ثانية، 1600 دورة/ثانية، 0.6 مللي ثانية
كان يجب أن ألاحظ أنه كان سريعًا جدًا)

ومع ذلك، فإننا في هذه الحالة نتعامل مع أصفار تافهة لدالة زيتا لريمان بمثال تافه. في المثال الذي قدمه لي المطورون، كان الأمر مختلفًا. لقد كنت مقتنعا بأنهم كانوا على حق، وبدأت في إزالة جميع تفاصيلهم المتعلقة بمنطق الأعمال من المثال. في مرحلة ما، أدركت أنه يمكنني التخلص تمامًا من التعليمات البرمجية الخاصة بهم وكتابة الكود الخاص بي - وهو ما يوضح نفس المشكلة - في الإنتاج يعمل بشكل أبطأ بمقدار 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 على وحدة معالجة مركزية افتراضية واحدة. لقد أجريت اختبارًا لحساب الرقم Pi - أظهر الاختبار نفس النتائج على أي خادم. أصبحت رائحة السحر الأسود أقوى وأقوى.

بمجرد وصولي إلى مزرعة DEV، بدأت اللعب بالخوادم. اتضح أن vMotion من مضيف إلى مضيف يمكنه "معالجة" الخادم، ولكن يمكنه أيضًا تحويل الخادم "السريع" إلى خادم "بطيء". يبدو أن هذا هو الحال - يواجه بعض المضيفين مشكلة... لكن... لا. كانت بعض الأجهزة الافتراضية بطيئة على المضيف، على سبيل المثال A، ولكنها عملت بسرعة على المضيف B. وعلى العكس من ذلك، عملت آلة افتراضية أخرى بسرعة على المضيف A وتباطأت على المضيف B! غالبًا ما كانت الآلات "السريعة" و"البطيئة" تدور على المضيف!

ومنذ تلك اللحظة، كانت هناك رائحة مميزة من الكبريت في الهواء. بعد كل شيء، لا يمكن أن تعزى المشكلة إلى الجهاز الظاهري (تصحيحات Windows، على سبيل المثال) - بعد كل شيء، تحولت إلى "سريع" مع 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 مرة (!!!). لقد كان هذا بالفعل انتصارًا صغيرًا: على أي حال، لم يكن هناك أي تهديد بالوقوع بين دعم Microsoft و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 = خطأ

اختتام

ربما يكون لديك سؤال: لماذا يتصل SQL بـ GetTimePrecise كثيرًا؟

ليس لدي الكود المصدري لخادم SQL، لكن المنطق يقول ذلك. يعد SQL بمثابة نظام تشغيل تقريبًا مع التزامن التعاوني، حيث يجب على كل مؤشر ترابط "الاستسلام" من وقت لآخر. أين هو أفضل مكان للقيام بذلك؟ حيث يوجد انتظار طبيعي - قفل أو IO. حسنًا، لكن ماذا لو كنا ندير حلقات حسابية؟ ثم المكان الواضح والوحيد تقريبًا هو المترجم (هذا ليس مترجمًا حقيقيًا)، بعد تنفيذ العبارة التالية.

بشكل عام، لا يتم استخدام خادم SQL لتسمية الحوسبة فقط، وهذه ليست مشكلة. لكن الحلقات التي تعمل مع جميع أنواع الجداول المؤقتة (التي يتم تخزينها مؤقتًا على الفور) تحول الكود إلى سلسلة من البيانات التي يتم تنفيذها بسرعة كبيرة.

بالمناسبة، إذا قمت بتغليف الوظيفة في NATIVELY COMPILED، فإنها تتوقف عن المطالبة بالوقت، وتزداد سرعتها بمقدار 10 مرات. وماذا عن المهام المتعددة التعاونية؟ ولكن بالنسبة للكود الذي تم تجميعه محليًا، كان علينا القيام بمهام متعددة وقائية في SQL.

المصدر: www.habr.com

إضافة تعليق