Evalml: تستخدم اختبارات الوحدة ما يصل إلى 20 جيجابايت من الذاكرة على الدائرة

تم إنشاؤها على ١٤ نوفمبر ٢٠٢٠  ·  11تعليقات  ·  مصدر: alteryx/evalml

مشكلة
أثناء تصحيح أخطاء الاختبار المتقطع في PR # 1410 ، قمت أنا و

هذا أكثر بكثير مما كنت أتوقعه ... السؤال هو لماذا؟

ملاحظات
لقد دخلنا في مربع دائري يعمل على main وقمنا بتشغيل ما يلي باستخدام memory-profiler :

mprof run  --include-children pytest evalml/ -n 8 --doctest-modules --cov=evalml --junitxml=test-reports/junit.xml --doctest-continue-on-failure -v

الذي أنشأ المؤامرة التالية ، مرئية بـ mprof plot :
mprof_4.png

قمت بتشغيل هذا مرتين وحصلت على مؤامرة مماثلة ، لذلك يبدو أن النتائج متسقة عبر الأشواط.

هذا قريب بشكل خطير من الحد الأقصى للذاكرة المسموح بها على حجم عامل الدائرة الذي نستخدمه. لهذا السبب بدأنا النظر في هذا - في # 1410 ، رأينا أن استخدام الذاكرة ارتفع بمقدار 5 جيجابايت لسبب ما.

bug performance testing

ال 11 كومينتر

قم بإجراء هذا محليًا مع عامل واحد فقط ويبدو أن الاختبارات تستخدم فقط 2 جيجابايت من الذاكرة مقارنة بـ 10 جيجابايت عندما أستخدم 8 عمال. لذا يبدو أن هذا يمكن أن يكون مزيجًا من الدائرة والمعالجة المتعددة؟

mprof run  --include-children pytest evalml/ --doctest-modules --cov=evalml --junitxml=test-reports/junit.xml --doctest-continue-on-failure -v

image

mprof run  --include-children pytest evalml/ -n 8 --doctest-modules --cov=evalml --junitxml=test-reports/junit.xml --doctest-continue-on-failure -v

image

مناقشة من مواجهة فيrpeck مع @ angela97linfreddyaboultonchristopherbunnParthivNaresh

فرضية
تمسك أداة الاختبار الخاصة بنا بأشياء لا ينبغي أن تكون ، وهذا يسبب غالبية المشكلة. ولكن أيضًا ، من الممكن أن يكون هناك عدد قليل من التسريبات في automl نفسها.

الخطوات التالية

  • تعقب الاختبارات التي تسبب أكبر زيادة في الذاكرة
  • اختر أحد هذه الاختبارات ، وشغّله 20 مرة مع تتبع الكومة ( استخدم tracemalloc ؟) ، انظر أين يوجد التسرب
  • الهدف: هل تسرب في automl أو في أداة اختبار / اختبار؟

rpeckdsherrychristopherbunn ولقد بحثت في هذا الأمر وهنا هو ملخص ما نعرفه حتى الآن:

  1. تأتي الكثير من الارتفاعات التي نراها في قطع الأراضي memory-profile من الواردات بدلاً من اختبارات الوحدة. استيراد AutoMLSearch هو 120 ميغا بايت تقريبًا ، على سبيل المثال. عندما قمنا بتعيين -n 8 في أمر pytest ، يتم مضاعفة بصمة الذاكرة من الواردات في 8 ، نظرًا لأن كل عملية فرعية يجب أن يتم استيراد كل شيء.

  2. ومع ذلك ، هناك بعض اختبارات الوحدة التي لها بصمة ذاكرة كبيرة. على سبيل المثال ، يقوم اختبار automl test_max_batches_works بتشغيل 20 دفعة مع التجميع. على الرغم من أننا نسخر من الملاءمة والنتيجة ، يظهر منشئ الذاكرة أن جميع المكالمات إلى _automl_algorithm.add_result(....) تصل إلى 27 ميغابايت من الذاكرة! يجب أن نمر باختباراتنا لمعرفة مقدار الذاكرة التي يستخدمونها ومعرفة ما إذا كانت هناك طرق لتقليلها دون المساس بجودة الاختبار.

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

في الوقت الحالي ، نرى ما إذا كان تقليل التوازي في pytest من 8 إلى 4 في دائرة ، أو استخدام عمال مخصصين ، سيؤدي إلى إلغاء حظر # 1410.

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

لا تتردد في إضافة أي شيء فاتني!

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

كان rpeck يبحث في قياس التغيير في الذاكرة قبل مقابل كل اختبار للوحدة. سيخبرنا هذا عن الاختبارات التي شهدت أكبر زيادة ، مما يعني أنه يمكننا التركيز على تحديد سمات تلك الاختبارات ومعرفة مصدر أكبر التخصيصات. آمل أن نتمكن من مواصلة هذا العمل.

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

سأكون فضوليًا لمعرفة ما إذا كان استخدام الذاكرة العالية ناتجًا عن إضافة Woodwork واستخدام DataTables. ربما يجب تتبع استخدام الذاكرة من وحدة اختبارات الإصدار إلى الإصدار؟

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

شيء واحد لاحظناه هو أن استيراد woodwork يتطلب حوالي 60 ميغابايت بشكل أساسي بسبب sklearn والباندا. لست متأكدًا مما يمكن فعله حيال ذلك ولكنك أردت لفت انتباهك إلى هذا. يسعدني تقديم شيء ما في ww repo!

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     3     37.8 MiB     37.8 MiB           1   <strong i="9">@profile</strong>
     4                                         def ww_imports():
     5     47.3 MiB      9.5 MiB           1       import numpy
     6     65.8 MiB     18.5 MiB           1       import pandas
     7     66.4 MiB      0.6 MiB           1       import click
     8     93.2 MiB     26.8 MiB           1       import sklearn
     9     96.3 MiB      3.2 MiB           1       import pyarrow

freddyaboulton ربما يقوم ww باستيراد أجزاء أكبر من هذه المكتبات أكثر من اللازم؟

سيكون من الرائع أن يكون لديك أداة تعمل على تحسين الواردات من خلال إيجاد الإغلاق المتعدِّد لجميع أجزاء المكتبة المستخدمة بالفعل ...

freddyaboulton ربما يقوم ww باستيراد أجزاء أكبر من هذه المكتبات أكثر من اللازم؟

سيكون من الرائع أن يكون لديك أداة تعمل على تحسين الواردات من خلال إيجاد الإغلاق المتعدِّد لجميع أجزاء المكتبة المستخدمة بالفعل ...

هذا هو استيراد sklearn الوحيد في Woodwork: from sklearn.metrics.cluster import normalized_mutual_info_score . لست متأكدًا مما إذا كان هناك أي شيء يمكننا القيام به لتقليص ذلك.

بالنسبة إلى الباندا ، فإننا عادةً ما نستورد المكتبة بأكملها ، ولكن هذا جزء كبير من كود Woodwork ، ولست متأكدًا من أنه يمكننا تقليص ذلك بسهولة أيضًا ، ولكن يمكننا النظر فيها أكثر إذا لزم الأمر.

شكرا على الشرح @ thehomebrewnerd ! نعم ، يبدو أن استيراد وحدة فرعية سيؤدي إلى استيراد الوحدة الرئيسية تلقائيًا . تزيد وارداتنا الكبيرة من مشكلة الذاكرة لدينا ، لكنها بالتأكيد في أسفل عمود الطوطم لعناصر العمل التي نحتاج إلى النظر فيها في جانب التقييم.

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

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

لقد لاحظنا أنه يمكننا إزالة 1.5 جيجا بايت من اختبارات automl فقط (نصفها تقريبًا!) عن طريق تعيين n_jobs=1 يدويًا لجميع المقدرات المستخدمة بواسطة automl (المؤامرات أدناه). لقد تحققنا من أن قيمة n_jobs هي عامل فقط في عدد قليل من اختبارات automl التي لا تسخر من fit و score . وبناءً على ذلك توصلنا إلى الخطة الحالية:

  1. لكل مكون يقبل n_jobs كمعامل (أي المقدرات القائمة على sklearn) ، تأكد من أن لدينا اختبار وحدة واحد يحدد n_jobs=-1 ، للتحقق من أنه يعمل بشكل صحيح لهذا المكون.
  2. بالنسبة لجميع اختبارات الوحدة الأخرى التي لا تسخر من fit ، قم بتعيين n_jobs=1 لجميع المكونات لتجنب مشاكل الذاكرة والترابط
  3. تأكد من أننا في مظهر الزجاج نستخدم n_jobs=-1 ، وهو ما أعتقد أننا نقوم به حاليًا لأن القيمة الافتراضية لـ n_jobs للمقدرات ذات الصلة هي -1

نأمل بمجرد الانتهاء من ذلك ، سنرى بعض التحسينات اللطيفة على البصمة الإجمالية للذاكرة لاختبارات الوحدة!

automl_tests_jobs-1
automl_tests_njobs_1

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات