نظام مرن لاختبار وجمع مقاييس البرنامج باستخدام مجموعة اختبار LLVM كمثال

مقدمة


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

جناح اختبار LLVM


للوهلة الأولى في مستودع مجموعة الاختبار ، يبدو أن هذه مجرد مجموعة من المعايير في C / C ++ ، ولكن هذا ليس صحيحًا تمامًا. بالإضافة إلى شفرة المصدر للبرامج التي سيتم إجراء قياسات الأداء عليها ، تتضمن مجموعة الاختبار بنية أساسية مرنة لبناء المقاييس وتشغيلها وجمعها. بشكل افتراضي ، تجمع المقاييس التالية: وقت التجميع ، ووقت التنفيذ ، ووقت الارتباط ، وحجم الرمز (في الأقسام).

تعتبر مجموعة الاختبار مفيدة بشكل طبيعي لاختبار ومقارنة المترجمات ، ولكن يمكن استخدامها أيضًا لبعض المهام البحثية الأخرى حيث تكون هناك حاجة إلى بعض قواعد كود C / C ++. أعتقد أن أولئك الذين قاموا ذات مرة بمحاولات القيام بشيء ما في مجال تحليل البيانات ، على ما أعتقد ، واجهوا مشكلة نقص وتجزئة البيانات المصدر. مجموعة اختبار ، على الرغم من أنها لا تتكون من عدد كبير من التطبيقات ، ولكن لديها آلية موحدة لجمع البيانات. إن إضافة تطبيقاتك الخاصة إلى المجموعة ، وجمع المقاييس اللازمة لمهمتك الخاصة أمر بسيط للغاية. لذلك ، في رأيي ، تعتبر مجموعة الاختبار (بالإضافة إلى المهام الرئيسية للاختبار والقياس) خيارًا جيدًا لمشروع أساسي ، يمكنك على أساسه بناء مجموعة البيانات الخاصة بك للمهام حيث تحتاج إلى تحليل بعض ميزات رمز البرنامج أو بعض خصائص البرامج.

هيكل جناح اختبار LLVM


test-suite |----CMakeLists.txt //  CMake ,   ,  | //   .. | |---- cmake | |---- .modules //        , | //   API    | |---- litsupport //  Python,      test-suite, | //    lit (  LLVM) | |---- tools //   :    | //     (    | // ),    .. | | //     | |---- SingleSource //   ,       | // .        . | |---- MultiSource //   ,      | //  .        | //  . | |---- MicroBenchmarks // ,   google-benchmark.   | //  ,    ,  | //       | |---- External //    ,     test-suite,  | // ,     (  ) | // -    

الهيكل بسيط ومباشر.

مبدأ العمل


كما ترون ، CMake وصيغة اختبار مضاءة خاصة مسؤولة عن كل العمل على وصف التجميع ، وإطلاق وجمع المقاييس.

إذا اعتبرنا ذلك بطريقة مجردة للغاية ، فمن الواضح أن عملية القياس باستخدام هذا النظام تبدو بسيطة ويمكن التنبؤ بها للغاية:


كيف يبدو هذا بمزيد من التفصيل؟ في هذه المقالة ، أود أن أتطرق إلى الدور الذي يلعبه CMake في النظام بأكمله وما هو الملف الوحيد الذي يجب عليك كتابته إذا كنت تريد إضافة شيء إلى هذا النظام.

1. تطبيقات اختبار البناء.

كنظام بناء ، أصبح المعيار الفعلي لبرامج C / C ++ CMake. يقوم CMake بتكوين المشروع وإنشاء ملفات إنشاء ، ونينجا ، وما إلى ذلك وفقًا لتفضيلات المستخدم. للبناء المباشر.
ومع ذلك ، لا يقوم CMake في مجموعة الاختبار بإنشاء قواعد حول كيفية إنشاء التطبيقات فحسب ، بل يقوم أيضًا بتكوين الاختبارات نفسها.

بعد بدء CMake ، ستتم كتابة ملفات أخرى (بالملحق .test) إلى دليل الإنشاء مع وصف لكيفية تنفيذ التطبيق والتحقق من صحته.

مثال لملف الاختبار القياسي

 RUN: cd <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football ; <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football/football VERIFY: cd <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football ; <some_path_to_build_directory>/tools/fpcmp %o football.reference_output 

قد يحتوي الملف بالملحق .test على الأقسام التالية:

  • الاستعداد - يصف أي إجراءات يجب القيام بها قبل تشغيل التطبيق ، تشبه إلى حد كبير الطريقة السابقة الموجودة في أطر اختبار الوحدات المختلفة ؛
  • RUN - يصف كيفية تشغيل التطبيق ؛
  • التحقق - يصف كيفية التحقق من التشغيل الصحيح للتطبيق ؛
  • METRIC - يصف المقاييس التي يجب جمعها بشكل إضافي في المعيار.

قد يتم حذف أي من هذه الأقسام.

ولكن نظرًا لأن هذا الملف يتم إنشاؤه تلقائيًا ، فإنه في ملف CMake للمعيار الذي يصف: كيفية الحصول على ملفات الكائنات ، وكيفية تجميعها في تطبيق ، ثم ما يجب القيام به مع هذا التطبيق.

لفهم أفضل للسلوك الافتراضي وكيفية وصف ذلك ، ضع في اعتبارك مثالاً لبعض CMakeLists.txt

 list(APPEND CFLAGS -DBREAK_HANDLER -DUNICODE-pthread) #      (         ..     CMak,       ) list(APPEND LDFLAGS -lstdc++ -pthread) #       

يمكن تعيين الأعلام اعتمادًا على النظام الأساسي ، يتم تضمين ملف DetectArch architecture في وحدات اختبار cmake في مجموعة الاختبار ، والتي تحدد النظام الأساسي الهدف الذي يتم تشغيل المعايير عليه ، بحيث يمكنك ببساطة استخدام البيانات التي تم جمعها بالفعل. تتوفر أيضًا بيانات أخرى: نظام التشغيل ، ترتيب البايت ، إلخ.

 if(TARGET_OS STREQUAL "Linux") list(APPEND CPPFLAGS -DC_LINUX) endif() if(NOT ARCH STREQUAL "ARM") if(ENDIAN STREQUAL "little") list(APPEND CPPFLAGS -DFPU_WORDS_BIGENDIAN=0) endif() if(ENDIAN STREQUAL "big") list(APPEND CPPFLAGS -DFPU_WORDS_BIGENDIAN=1) endif() endif() 

من حيث المبدأ ، لا ينبغي أن يكون هذا الجزء جديدًا بالنسبة للأشخاص الذين رأوا أو كتبوا ملف CMake بسيطًا مرة واحدة على الأقل. بطبيعة الحال ، يمكنك استخدام المكتبات ، وقم ببنائها بنفسك ، بشكل عام ، استخدم أي وسيلة توفرها CMake لوصف عملية إنشاء التطبيق الخاص بك.

ثم تحتاج إلى التأكد من إنشاء ملف الاختبار. ما الأدوات التي توفرها واجهة tets-suite لهذا؟

هناك 2 وحدات ماكرو أساسية llvm_multisource و llvm_singlesource ، وهي كافية لمعظم الحالات التافهة.

  • يتم استخدام llvm_multisource إذا كان التطبيق يتكون من عدة ملفات. إذا لم تقم بتمرير ملفات التعليمات البرمجية المصدر كمعلمات عند استدعاء هذا الماكرو في CMake ، فسيتم استخدام جميع ملفات التعليمات البرمجية المصدر الموجودة في الدليل الحالي كأساس للبناء. في الواقع ، تجري حاليًا تغييرات في واجهة هذا الماكرو في مجموعة الاختبار ، والطريقة الموضحة لنقل ملفات المصدر كمعلمات ماكرو هي الإصدار الحالي الموجود في الفرع الرئيسي. في السابق ، كان هناك نظام آخر: كان يجب كتابة الملفات ذات شفرة المصدر إلى متغير المصدر (كما كان في الإصدار 7.0) ، ولم يقبل الماكرو أي معلمات. لكن المنطق الأساسي للتنفيذ ظل كما هو.
  • يعتبر llvm_singlesource أن كل ملف .c / .cpp هو مقياس منفصل لكل منها ويجمع ملفًا تنفيذيًا منفصلاً.

بشكل افتراضي ، يقوم كل من وحدات الماكرو الموضحة أعلاه لتشغيل تطبيق مدمج بإنشاء أمر يقوم ببساطة باستدعاء هذا التطبيق. ويتم التحقق من الصحة بسبب المقارنة مع الإخراج المتوقع الموجود في الملف بالملحق .reference_output (أيضًا مع اللواحق المحتملة .reference_output.little-endian ، .reference_output.big-endian).

إذا كان هذا يناسبك ، فهو رائع ، خط إضافي واحد (استدعاء llvm_multisource أو llvm_singlesource) يكفيك لبدء التطبيق والحصول على المقاييس التالية: حجم الشفرة (في الأقسام) ، ووقت الترجمة ، ووقت الرابط ، ووقت التنفيذ.

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

توجد وحدات ماكرو في واجهة برمجة التطبيقات لوصف الإجراءات في كل مرحلة.

لا يوجد شيء كثير لتكتبه عن الماكرو llvm_test_prepare للمرحلة الإعدادية ، حيث يتم ببساطة تمرير الأوامر التي تحتاج إلى تنفيذها كمعلمة.

ما الذي قد يكون مطلوبًا في قسم الإطلاق؟ الحالة الأكثر توقعًا هي أن التطبيق يقبل بعض الحجج وملفات الإدخال. لهذا ، هناك ماكرو llvm_test_run ، الذي يقبل فقط وسيطات بدء تشغيل التطبيق (بدون اسم الملف القابل للتنفيذ) كمعلمات.

 llvm_test_run(--fixed 400 --cpu 1 --num 200000 --seed 1158818515 run.hmm) 

لتغيير الإجراءات في مرحلة التحقق ، يتم استخدام الماكرو llvm_test_verify الذي يقبل أي أوامر كمعلمات. بالطبع ، للتحقق من صحة ، من الأفضل استخدام الأدوات المضمنة في مجلد الأدوات. أنها توفر فرصًا جيدة لمقارنة المخرجات المتولدة مع الناتج المتوقع (هناك معالجة منفصلة لمقارنة الأرقام الحقيقية ببعض الأخطاء ، وما إلى ذلك). ولكن يمكنك في مكان ما والتحقق من اكتمال التطبيق بنجاح ، إلخ.

 llvm_test_verify("cat %o | grep -q 'exit 0'") # %o -   placeholder   ,   lit.          lit,    ,    .    lit (  ,   LLVM)      (   <a href="https://llvm.org/docs/CommandGuide/lit.html"> </a>) 

ولكن ماذا لو كانت هناك حاجة لجمع بعض المقاييس الإضافية؟ يوجد ماكرو llvm_test_metric لهذا .

 llvm_test_metric(METRIC < > <,   >) 

على سبيل المثال ، بالنسبة إلى dhrystone ، يمكن الحصول على مقياس خاص به.

 llvm_test_metric(METRIC dhry_score grep 'Dhrystones per Second' %o | awk '{print $4}') 

بالطبع ، إذا كنت بحاجة إلى جمع مقاييس إضافية لجميع الاختبارات ، فإن هذه الطريقة غير ملائمة إلى حد ما. إما أنك تحتاج إلى إضافة استدعاء llvm_test_metric إلى وحدات الماكرو ذات المستوى الأعلى التي توفرها الواجهة ، أو يمكنك استخدام TEST_SUITE_RUN_UNDER (متغير CMake) ونص برمجي خاص لجمع المقاييس. يعتبر متغير TEST_SUITE_RUN_UNDER مفيدًا جدًا ، ويمكن استخدامه ، على سبيل المثال ، للتشغيل على أجهزة محاكاة ، وما إلى ذلك. في الواقع ، يتم كتابة أمر فيه سيقبل التطبيق مع حججه كمدخل.

نتيجة لذلك ، نحصل على بعض CMakeLists.txt من النموذج

 #       llvm_test_run(--fixed 400 --cpu 1 --num 200000 --seed 1158818515 run.hmm) llvm_test_verify("cat %o | grep -q 'exit 0'") llvm_test_metric(METRIC score grep 'Score' %o | awk '{print $4}') llvm_multisource() # llvm_multisource(my_application)    

لا يتطلب التكامل جهودًا إضافية ، إذا تم إنشاء التطبيق بالفعل باستخدام CMake ، فعندئذ في ملف CMakeList.txt في مجموعة الاختبار ، يمكنك تضمين CMake الحالي للتجميع وإضافة بعض مكالمات الماكرو البسيطة.

2. تشغيل الاختبارات

نتيجة لعملها ، أنشأت CMake ملف اختبار خاص وفقًا للوصف المحدد. لكن كيف يتم تنفيذ هذا الملف؟

يستخدم lit دائمًا بعض ملفات التكوين lit.cfg ، والتي ، وفقًا لذلك ، موجودة في مجموعة الاختبار. في ملف التكوين هذا ، يشار إلى إعدادات مختلفة لتشغيل الاختبارات ، بما في ذلك تنسيق الاختبارات القابلة للتنفيذ. يستخدم Test-suite التنسيق الخاص به ، والذي يقع في مجلد litsupport.

 config.test_format = litsupport.test.TestSuiteTest() 

يوصف هذا التنسيق بأنه فئة اختبار موروثة من الاختبار المضاء القياسي وتجاوز الطريقة الرئيسية لواجهة التنفيذ. من المكونات المهمة أيضًا في litsupport فئة مع وصف لخطة تنفيذ اختبار TestPlan ، والتي تخزن جميع الأوامر التي يجب تنفيذها في مراحل مختلفة ومعرفة ترتيب المراحل. لتوفير المرونة اللازمة ، تم إدخال وحدات أيضًا في الهندسة المعمارية التي يجب أن توفر طريقة mutatePlan ، والتي يمكن من خلالها تغيير خطة الاختبار ، فقط تقديم وصف لمجموعة المقاييس اللازمة ، وإضافة أوامر إضافية لقياس الوقت لبدء تشغيل التطبيق ، إلخ. بسبب هذا الحل ، تتوسع الهندسة المعمارية بشكل جيد.



فيما يلي مثال على عملية اختبار مجموعة الاختبار (باستثناء التفاصيل في شكل فئات TestContext ، وتكوينات مضاءة متنوعة والاختبارات نفسها ، وما إلى ذلك).



يتسبب Lit في تنفيذ نوع الاختبار المحدد في ملف التكوين. يوزع TestSuiteTest ملف اختبار CMake الذي تم إنشاؤه ، ويتلقى وصفًا للمراحل الرئيسية. بعد ذلك ، يتم استدعاء جميع الوحدات الموجودة لتغيير خطة الاختبار الحالية ، ويتم إجراء الإطلاق. ثم يتم تنفيذ خطة الاختبار المستلمة: ويتم تنفيذها بترتيب مرحلة الإعداد والإطلاق والتحقق. إذا لزم الأمر ، يمكن إجراء التنميط (يضاف بواسطة إحدى الوحدات ، إذا تم تعيين متغير أثناء التكوين الذي يشير إلى الحاجة إلى التنميط). الخطوة التالية هي جمع المقاييس ، ويتم تجميع وظائف التجميع التي تمت إضافتها بواسطة الوحدات القياسية في حقل metric_collectors في TestPlan ، ثم يتم جمع المقاييس الإضافية التي وصفها المستخدم في CMake.

3. تشغيل جناح الاختبار

هناك طريقتان لتشغيل مجموعة الاختبار:

  • يدوي ، أي الاستدعاء المتسلسل للأوامر.
     cmake -DCMAKE_CXX_COMPILER:FILEPATH=clang++ -DCMAKE_C_COMPILER:FILEPATH=clang test-suite #  make #   llvm-lit . -o <output> #   
  • باستخدام LNT (نظام آخر من نظام LLVM البيئي الذي يسمح لك بتشغيل المعايير ، وحفظ النتائج في قاعدة البيانات ، وتحليل النتائج في واجهة الويب). تقوم LNT ، ضمن فريق التشغيل التجريبي الخاص بها ، بتنفيذ نفس الخطوات كما في الفقرة السابقة.
     lnt runtest test-suite --sandbox SANDBOX --cc clang --cxx clang++ --test-suite test-suite 

يتم عرض نتيجة كل اختبار كـ

 PASS: test-suite :: MultiSource/Benchmarks/Prolangs-C/football/football.test (m of n) ********** TEST 'test-suite :: MultiSource/Benchmarks/Prolangs-C/football/football.test' RESULTS ********** compile_time: 1.1120 exec_time: 0.0014 hash: "38254c7947642d1adb9d2f1200dbddf7" link_time: 0.0240 size: 59784 size..bss: 99800 … size..text: 37778 ********** 

يمكن مقارنة نتائج عمليات الإطلاق المختلفة بدون LNT (على الرغم من أن هذا الإطار يوفر فرصًا كبيرة لتحليل المعلومات باستخدام أدوات مختلفة ، ولكنه يحتاج إلى مراجعة منفصلة) ، باستخدام البرنامج النصي المتضمن في مجموعة الاختبار

 test-suite/utils/compare.py results_a.json results_b.json 

مثال لمقارنة حجم الرمز لمعيار واحد ونفس المعيار من إطلاقين: مع علامتي -O3 و -Os

 test-suite/utils/compare.py -m size SANDBOX1/build/O3.json SANDBOX/build/Os.json Tests: 1 Metric: size Program O3 Os diff test-suite...langs-C/football/football.test 59784 47496 -20.6% 

الخلاصة


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

Source: https://habr.com/ru/post/ar428421/


All Articles