تباطؤ ويندوز الجزء 3: إنهاء العملية
يشارك المؤلف في تحسين أداء Chrome في Google - تقريبًا. العابرةفي صيف عام 2017 ، واجهت مشكلة في أداء Windows. كانت عملية إنهاء العملية بطيئة ومتسلسلة وحظرت قائمة انتظار إدخال النظام ، مما أدى إلى توقف العديد من مؤشر الماوس أثناء تجميع Chrome. السبب الرئيسي هو أنه في نهاية العمليات ، أمضى Windows الكثير من الوقت في البحث عن كائنات GDI ، مع الاستمرار في الضغط على القسم الحاسم من user32 للنظام العالمي. تحدثت عن هذا في المقال
"معالج 24 النواة ، لكنني لا أستطيع تحريك المؤشر .
"قامت Microsoft بإصلاح الخلل ، وعدت إلى عملي ، ولكن بعد ذلك اتضح أن الخطأ قد عاد. كانت هناك شكاوى حول التشغيل البطيء لاختبارات LLVM ، مع توقف الإدخال المتكرر.
ولكن في الواقع ، فإن علة لم يعودوا. كان السبب هو التغيير في كودنا.
إصدار 2017

تحتوي كل عملية Windows على العديد من واصفات كائن GDI القياسية. بالنسبة للعمليات التي لا تفعل شيئًا مع الرسومات ، تكون هذه الواصفات عادةً خالية. في نهاية العملية ، يستدعي Windows بعض الوظائف لهذه الواصفات ، حتى لو كانت فارغة. لا يهم - الميزات عملت بسرعة - حتى إصدار Windows 10 Anniversary Edition ، حيث
جعلت بعض التغييرات الأمنية هذه الميزات بطيئة . أثناء العملية ، كانوا يحملون القفل نفسه الذي تم استخدامه لأحداث الإدخال. عندما يتم إنهاء عدد كبير من العمليات في نفس الوقت ، تقوم كل واحدة بإجراء عدة مكالمات إلى الوظيفة البطيئة التي تحمل هذا القفل الخطير ، مما يؤدي في النهاية إلى حظر إدخال المستخدم وتجمد المؤشر.
لم يكن تصحيح Microsoft هو استدعاء هذه الوظائف للعمليات بدون كائنات GDI. لا أعرف التفاصيل ، لكنني أعتقد أن تصحيح Microsoft كان مثل هذا:
+ if (IsGUIProcess())
+ NtGdiCloseProcess();
– NtGdiCloseProcess();
وهذا هو ، فقط تخطي تنظيف GDI إذا كانت العملية ليست عملية GUI / GDI.
نظرًا لعدم استخدام المجمعين والعمليات الأخرى التي ننشئها وننهيها بسرعة كائنات GDI ، فقد تبين أن هذا التصحيح يكفي لإصلاح تجميد واجهة المستخدم.
2018 قضية
اتضح أنه من السهل جدًا تخصيص العمليات فعليًا لبعض كائنات GDI القياسية. إذا حمّلت العملية gdi32.dll ، فستتلقى تلقائيًا كائنات GDI (DC ، والأسطح ، والمناطق ، والفرش ، والخطوط ، وما إلى ذلك) ، سواء كنت في حاجة إليها أم لا (لاحظ أنه لا يتم عرض كائنات GDI القياسية هذه في "إدارة المهام" بين كائنات GDI للعملية).
ولكن هذا لا ينبغي أن يكون مشكلة. أقصد ، لماذا يقوم المترجم بتحميل gdi32.dll؟ حسنًا ، اتضح أنه إذا قمت بتحميل user32.dll أو shell32.dll أو ole32.dll أو العديد من DLLs الأخرى ، فسوف تحصل تلقائيًا على إضافة gdi32.dll (مع كائنات GDI القياسية المذكورة أعلاه). ومن السهل جدًا تنزيل إحدى هذه المكتبات عن طريق الخطأ.
يختبر LLVM عند تحميل كل عملية تسمى
CommandLineToArgvW (shell32.dll) ، وأحيانًا تسمى
SHGetKnownFolderPath (أيضًا shell32.dll) كانت هذه الاستدعاءات كافية لسحب gdi32.dll وإنشاء كائنات GDI القياسية المخيفة هذه. نظرًا لأن مجموعة اختبار LLVM تولد الكثير من العمليات ، فإنها في نهاية المطاف تتسلسل عند الانتهاء من العمليات ، مما يتسبب في تأخير كبير وتجميد المدخلات ، أسوأ بكثير من تلك التي كانت في عام 2017.
لكن هذه المرة عرفنا بالمشكلة الرئيسية للحظر ، لذلك عرفنا على الفور ما يجب القيام به.
بادئ ذي
بدء ، لقد تخلصنا من استدعاء
CommandLineToArgvW ، مع
تحليل سطر الأوامر يدويًا . بعد ذلك ، نادراً ما تسمى مجموعة اختبار LLVM أي وظيفة من أي DLL مشكلة. لكننا عرفنا مقدمًا أن هذا لن يؤثر على الأداء بأي طريقة. السبب هو أنه حتى المكالمة
الشرطية المتبقية كانت كافية لسحب shell32.dll دائمًا ، والتي بدورها سحبت gdi32.dll ، مما ينشئ كائنات GDI قياسية.
كان الإصلاح الثاني
التحميل المتأخر لـ shell32.dll . التحميل المؤجل يعني أن المكتبة يتم تحميلها عند الطلب - عند استدعاء الوظيفة - بدلاً من التحميل عند بدء العملية. هذا يعني أن shell32.dll و gdi32.dll سيتم تحميلهما نادرًا ، وليس دائمًا.
بعد ذلك ، بدأت مجموعة اختبار LLVM في الركض أسرع
خمس مرات - في دقيقة واحدة بدلاً من خمس. ولا يوجد المزيد من الماوس يتجمد على أجهزة التطوير ، بحيث يمكن للموظفين العمل بشكل طبيعي أثناء تنفيذ الاختبارات. هذا تسارع مجنون لمثل هذا التغيير المتواضع ، وكان صاحب التصحيحات ممتنًا للغاية للتحقيق الذي أجريته حتى أنه رشحني
لمكافأة الشركات .
في بعض الأحيان يكون لأصغر التغييرات أكبر النتائج. تحتاج فقط إلى
معرفة مكان الاتصال "صفر" .
مسار التنفيذ غير مقبول

تجدر الإشارة إلى أننا اهتمنا بالكود الذي
لم يتم تنفيذه - وكان هذا تغييرًا رئيسيًا. إذا كان لديك أداة سطر أوامر لا تصل إلى gdi32.dll ، فإن إضافة تعليمة برمجية مع استدعاء دالة
شرطية سوف تبطئ العملية عدة مرات إذا تم تحميل gdi32.dll. في المثال أدناه ، لا
يتم استدعاء
CommandLineToArgvW أبدًا ، ولكن حتى التواجد البسيط في الكود (بدون تأخير المكالمة) يؤثر سلبًا على الأداء:
int main(int argc, char* argv[]) { if (argc < 0) { CommandLineToArgvW(nullptr, nullptr); // shell32.dll, pulls in gdi32.dll } }
لذا ، نعم ، قد تكون إزالة استدعاء دالة ، حتى لو لم يتم تنفيذ التعليمات البرمجية ، كافية لتحسين الأداء بشكل كبير في بعض الحالات.
علم الأمراض الاستنساخ

عندما حققت في الخطأ الأولي ، كتبت برنامجًا (
ProcessCreateTests ) أنشأ 1000 عملية ثم قتلهم جميعًا بشكل متوازٍ. استنساخ هذا التجميد ، وعندما أصلحت مايكروسوفت الخطأ ، استخدمت برنامج اختبار للتحقق من التصحيح: انظر
الفيديو . بعد تجسد الخطأ ، قمت بتغيير برنامجي
بإضافة الخيار -user32 ، الذي يقوم بتحميل user32.dll لكل من آلاف عمليات الاختبار. كما هو متوقع ، يزداد وقت الانتهاء من جميع عمليات الاختبار بشكل كبير مع هذا الخيار ، ومن السهل اكتشاف تجميد مؤشر الماوس. يزداد وقت إنشاء العملية أيضًا مع الخيار -user32 ، لكن لا توجد أي تعليق للمؤشر أثناء إنشاء العملية. يمكنك استخدام هذا البرنامج ومعرفة مدى فظاعة المشكلة. في ما يلي بعض النتائج النموذجية لجهاز الكمبيوتر المحمول ذي النواة الأربعة / ثمانية خيوط بعد أسبوع من الجهوزية. يزيد خيار -user32 من الوقت لكل شيء ، لكن قفل
UserCrit على العمليات ينتهي بشكل كبير:
> ProcessCreatetests.exe
Process creation took 2.448 s (2.448 ms per process).
Lock blocked for 0.008 s total, maximum was 0.001 s.
Process destruction took 0.801 s (0.801 ms per process).
Lock blocked for 0.004 s total, maximum was 0.001 s.
> ProcessCreatetests.exe -user32
Testing with 1000 descendant processes with user32.dll loaded.
Process creation took 3.154 s (3.154 ms per process).
Lock blocked for 0.032 s total, maximum was 0.007 s.
Process destruction took 2.240 s (2.240 ms per process).
Lock blocked for 1.991 s total, maximum was 0.864 s.
حفر أعمق للمتعة فقط
فكرت في بعض أساليب ETW التي يمكن استخدامها لدراسة المشكلة بمزيد من التفاصيل ، وبدأت بالفعل في كتابتها. لكنني واجهت مثل هذا السلوك الذي لا يمكن تفسيره ، والتي قررت تكريس مقال منفصل. يكفي القول أنه في هذه الحالة ، يتصرف Windows بطريقة أكثر غرابة.
مقالات أخرى في السلسلة:
الأدب