الروبوت:كم من النفقات العامة التي تم إنشاؤها بواسطة تشغيل فارغة الأسلوب ؟

StackOverflow https://stackoverflow.com/questions/4345845

سؤال

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

public class Debug {
    public static void debug( String module, String message) {
        if( Release.DEBUG )
            Log.d(module, message);
    }
}

بعد قراءة سؤال آخر, لقد تعلمت أن محتويات البيان إذا لم يتم تصنيفه إذا كان الإفراج المستمر.التصحيح هو زائف.

ما أريد أن أعرفه هو كيف الكثير من النفقات العامة التي تم إنشاؤها بواسطة تشغيل هذا فارغ ؟ (مرة واحدة إذا كان الشرط هو إزالة لا يوجد رمز اليسار في الأسلوب) هو الذهاب الى يكون لها أي تأثير على طلبي ؟ ومن الواضح أن أداء قضية كبيرة عند الكتابة على الهواتف النقالة =P

شكرا

جاري

هل كانت مفيدة؟

المحلول

القياسات التي أجريت على Nexus S مع Android 2.3.2:

10^6 iterations of 1000 calls to an empty static void function: 21s  <==> 21ns/call
10^6 iterations of 1000 calls to an empty non-static void function: 65s  <==> 65ns/call

10^6 iterations of 500 calls to an empty static void function: 3.5s  <==> 7ns/call
10^6 iterations of 500 calls to an empty non-static void function: 28s  <==> 56ns/call

10^6 iterations of 100 calls to an empty static void function: 2.4s  <==> 24ns/call
10^6 iterations of 100 calls to an empty non-static void function: 2.9s  <==> 29ns/call

مراقبة:

10^6 iterations of an empty loop: 41ms <==> 41ns/iteration
10^7 iterations of an empty loop: 560ms <==> 56ns/iteration
10^9 iterations of an empty loop: 9300ms <==> 9.3ns/iteration

لقد كررت القياسات عدة مرات. لم يتم العثور على انحرافات كبيرة. يمكنك أن ترى أن تكلفة كل نسبة يمكن أن تختلف اختلافًا كبيرًا حسب عبء العمل (ربما بسبب تجميع JIT) ، ولكن يمكن استخلاص 3 استنتاجات:

  1. تمتص Dalvik/Java في تحسين الكود الميت

  2. يمكن تحسين مكالمات الوظائف الثابتة بشكل أفضل من غير القديم (الوظائف غير الافتراضية افتراضية وتحتاج إلى البحث في جدول افتراضي)

  3. التكلفة على Nexus S ليست أكبر من 70ns/call (هذا ~ 70 دورة وحدة المعالجة المركزية) وهي قابلة للمقارنة مع تكلفة واحدة فارغة لتكرار الحلقة (أي زيادة واحدة وفحص شرط واحد على متغير محلي)

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

void empty(String string){
}

استدعاء مع الحجج مثل

empty("Hello " + 42 + " this is a string " + count );

10^4 تكرارات 100 مكالمة تستغرق 10s. هذا هو 10us/call ، أي ~ 1000 مرة أبطأ من مجرد مكالمة فارغة. كما أنه ينتج كمية ضخمة من نشاط GC. الطريقة الوحيدة لتجنب ذلك هي ضمّن الوظيفة يدويًا ، أي استخدام >> إذا << بيان بدلاً من استدعاء وظيفة التصحيح. إنها قبيحة ولكن الطريقة الوحيدة لجعلها تعمل.

نصائح أخرى

ما لم تسمي هذا من داخل حلقة متداخلة بعمق ، فلن أقلق بشأنه.

يقوم برنامج التحويل البرمجي الجيد بإزالة الطريقة الفارغة بأكملها ، مما يؤدي إلى عدم وجود علاقات عامة على الإطلاق. لست متأكدًا مما إذا كان برنامج التحويل البرمجي Dalvik يقوم بذلك بالفعل ، لكنني أظن أنه من المحتمل ، على الأقل منذ وصول المترجم في الوقت المناسب مع Froyo.

أنظر أيضا: التوسع المضمّن

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

Debug.debug(mymodule, "My error message" + myerrorcode);

والتي ستظل تحدث حتى من خلال الرسالة. لسوء الحظ ، تحتاج حقًا إلى "if (relext.debug)" حول المكالمات إلى هذه الوظيفة بدلاً من داخل الوظيفة نفسها إذا كان هدفك هو الأداء ، وسوف ترى ذلك في الكثير من رمز Android.

هذا هو السؤال المهم و أنا أحب @misiu_mp تحليل, حتى ظننت أنني سوف تحديثه مع 2016 اختبار على جهاز Nexus 7 بنظام أندرويد 6.0.1.هنا هو اختبار التعليمات البرمجية:

public void runSpeedTest() {
    long startTime;
    long[] times = new long[100000];
    long[] staticTimes = new long[100000];
    for (int i = 0; i < times.length; i++) {
        startTime = System.nanoTime();
        for (int j = 0; j < 1000; j++) {
            emptyMethod();
        }
        times[i] = (System.nanoTime() - startTime) / 1000;
        startTime = System.nanoTime();
        for (int j = 0; j < 1000; j++) {
            emptyStaticMethod();
        }
        staticTimes[i] = (System.nanoTime() - startTime) / 1000;
    }
    int timesSum = 0;
    for (int i = 0; i < times.length; i++) { timesSum += times[i]; Log.d("status", "time," + times[i]); sleep(); }
    int timesStaticSum = 0;
    for (int i = 0; i < times.length; i++) { timesStaticSum += staticTimes[i]; Log.d("status", "statictime," + staticTimes[i]); sleep(); }
    sleep();
    Log.d("status", "final speed = " + (timesSum / times.length));
    Log.d("status", "final static speed = " + (timesStaticSum / times.length));
}

private void sleep() {
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private void emptyMethod() { }
private static void emptyStaticMethod() { }

على sleep() إضافة إلى منع تفيض Log.d العازلة.

لقد لعبت حوالي مع ذلك مرات عديدة كانت النتائج متسقة جدا مع @misiu_mp:

10^5 iterations of 1000 calls to an empty static void function: 29ns/call
10^5 iterations of 1000 calls to an empty non-static void function: 34ns/call

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

النظر في الرسم البياني مرات يكشف عن شيء مثير للاهتمام ، ومع ذلك.غالبية الدعوة ، سواء كانت ثابتة أو لا يستغرق ما بين 30-40ns ، ويبحث عن كثب في البيانات هم تقريبا كل 30ns بالضبط.

enter image description here

تشغيل نفس رمز فارغة حلقات (معلقا طريقة المكالمات) تنتج بمتوسط سرعة 8ns, ومع ذلك ، فإن حوالي 3/4 من قياس الزمن 0ns في حين تبقى هي بالضبط 30ns.

أنا غير متأكد من كيفية حساب هذه البيانات ، ولكن لست متأكدا من أن @misiu_mp استنتاجات ما زالت.الفرق بين فارغة ثابت وغير ثابت طرق لا يكاد يذكر ، وكثرة من القياسات بالضبط 30ns.أن يقال, يبدو أنه لا يزال هناك بعض غير الصفر تكلفة تشغيل فارغة الأساليب.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top