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

هذه هي المطالبات التي سمعتها من مطورينا. الشيء الأكثر إثارة للاهتمام هو أن هذا تبين أنه حقيقي ، مما أدى إلى تحقيق مطول. سيكون الأمر يتعلق بخوادم 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 مللي ثانية
I كان يجب أن ينتبه مبكرًا)


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

When you disable virtualization of the TSC, reading the TSC from within the virtual machine returns the physical machine’s TSC value, and writing the TSC from within the virtual machine has no effect. Migrating the virtual machine to another host, resuming it from suspended state, or reverting to a snapshot causes the TSC to jump discontinuously. Some guest operating systems fail to boot, or exhibit other timekeeping problems, when TSC virtualization is disabled. In the past, this feature has sometimes been recommended to improve performance of applications that read the TSC frequently, but performance of the virtual TSC has been improved substantially in current products. The feature has also been recommended for use when performing measurements that require a precise source of real time in the virtual machine.

باختصار ، أضف المعلمة

monitor_control.virtual_rdtsc = FALSE

استنتاج


ربما لديك سؤال: ماذا عن استدعاء SQL GetTimePrecise كثيرًا؟

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

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

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

All Articles