سؤال

أنا مبرمج Java المختصة إلى حد ما الجديد جدا في جيم. أحاول تحسين روتين يحتوي على أربع أوضاع عملية.

أنا حلقة على جميع البكسل في صورة وحسب قيمة بكسل جديدة اعتمادا على "الوضع" المرت.

فيما يتعلق سؤالي في النفقات العامة لبيان التبديل داخل اثنين متداخل للحلقات. سأكون مهتما بأي روابط للوثائق المتعلقة بالكفاءة النسبية لبيانات C الأساسية والعمليات المنطقية الرياضيات والعمليات المنطقية.

الرمز سوف يذهب كما يلي؛

for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             switch (mode)                  /* select the type of calculation */
             {
                case 0:
                weight = dCentre / maxDistanceEdge;
                case 1: 
                    weight = (float)x/width;             
                    break;
                case 2: 
                    weight = (float)y/height;
                    break;
                case 3: 
                    weight = dBottomLeft / maxDistanceCorner;
                    break;
                case 4: 
                    weight = dTopRight / maxDistanceCorner;
                    break;
                default: 
                weight = 1;
                break;
            }
             // Calculate the new pixel value given the weight
             ...
            }             

    }

هل تتوقع أن ترى الكثير من النفقات العامة إذا كان هذا أكثر من 5000 × 5000 بكسل؟ لقد حاولت القيام ببعض الاختبارات ولكن نتائجي في كل مكان حيث أن النظام (الجهاز المحمول) لديه كل أنواع الأشياء التي تعمل في الخلفية التي قد تنحدر النتائج.

الخيار الآخر هو أن يكون لديك طريقة منفصلة لكل وضع، ولكل منها أربع حلقاتها. من الواضح أن هذا من شأنه أن يعرض رمزا زائدة ولكن الكفاءة هو اسم اللعبة هنا.

شكرا مقدما!

غاف

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

المحلول

تجميع البيانات التبديلية لتجميع جدول قفزة للقيم المتتالية وإلى مجموعة من العبارات IF-ELEST للقيم المتنزع. على أي حال، لا تريد عبارة تبديل في حلقة الداخلية لمعالجة الصور إذا كنت تهتم بالأداء. تريد على النحو التالي بدلا من ذلك.

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

switch (mode)                  /* select the type of calculation */
{
case 0:
    weight = dCentre / maxDistanceEdge;
    for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;
case 1:
    for (x = 0; x < width; x++) {
        weight = (float)x/width;
        for (y = 0; y < height; y++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;
case 2:
    // note - the loops have been swapped to get the weight calc out of the inner loop
    for (y = 0; y < height; y++) {
        weight = (float)y/height;
        for (x = 0; x < width; x++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;
case 3:
    weight = dBottomLeft / maxDistanceCorner;
    for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;
case 4:
    weight = dTopRight / maxDistanceCorner;
    for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;
default:
    weight = 1;
    for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             // Calculate the new pixel value given the weight
             ...
        }
    }
    break;

// etc..
}

نصائح أخرى

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

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

بالمقارنة مع الرياضيات التي تقوم بها في الحلقة، من المحتمل أن تكون النفقات العامة للمفتاح ضئيلة. بعد القول أنه، الطريقة الوحيدة للتأكد من أن تخلق إصدارات مختلفة للمناهبين المختلفة، والوقت لهم.

التبديل / الحالة سريع للغاية مقارنة مع ما يعادله إذا / غير ذلك: يتم تنفيذها عادة كجدول قفزة. ومع ذلك لا يزال لديه تكلفة.

أثناء تحسين الأشياء:

1) حاول حلقة فوق الخطوط، وليس على الأعمدة (رمز التبديل X و Y "ل" الحلقات)، قد يكون حل أسرع بشكل لا يصدق من الآخر، بسبب إدارة ذاكرة التخزين المؤقت.

2) استبدال جميع الانقسامات عن طريق تضرب العكس (المحسوبة مسبقا) تعطيك مكسبا كبيرا، وربما فقدان دقيقا مقبولا.

من أجل الكفاءة أنت تتحرك أفضل switch خارج الحلقة.

كنت أستخدم مؤشرات الوظائف مثل هذا:

double fun0(void) { return dCentre/maxDistanceEdge; }
double fun1(void) { return (float)x/width; }
/* and so on ... */

double (*fun)(void);

switch (mode)                  /* select the type of calculation */
{
    case 0: fun = fun0;
            break;
    case 1: fun = fun1;
            break;
    case 2: fun = fun2;
            break;
    case 3: fun = fun3;
            break;
    case 4: fun = fun3;
            break;
    default : fun = fun_default;
            break;
}

for (x = 0; x < width; x++) {
        for (y = 0; y < height; y++) {
             weight = fun();
             // Calculate the new pixel value given the weight
             ...
        }
}

يضيف وظيفة استدعاء الدالة ولكن لا ينبغي أن يكون كبيرا جدا حيث تجاوز عدم وجود علامات على الوظيفة. أعتقد أنها مفاجئة جيدة بين الأداء والقراءة.

تعديل: إذا كنت تستخدم دول مجلس التعاون الخليجي، للتخلص من مكالمة الوظيفة، يمكنك استخدامها goto و التسميات كقيم: ابحث عن الملصق المناسب داخل التبديل ثم القفز إليه في كل مرة. أعتقد أنه يجب أن ينقذ عدد قليل من الدورات.

يجب أن تنتج المفاتيح أي نفقات هامة، يتم تجميعها في نوع من مجموعة من المؤشرات في النهاية المنخفضة، ثم إنها حالة بفعالية:

JMP {baseaddress} + switchascasenum

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

ومع ذلك، فإن أفضل طريقة لمعرفة الملف الشخصي هو وانظر.

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

آسف لتصدي هذا الموضوع، ولكن يبدو لي أن المفتاح بعيد عن المشكلة.

المشكلة الحقيقية مع الكفاءة في هذه الحالة هي الانقسامات. يبدو لي أن جميع القواسم من عمليات التقسيم هي ثوابت (العرض، الارتفاع، ماكس ...) ولن تتغير هذه طوال دورة الصورة. إذا كان تخميني صحيحا، فهذه متغيرات بسيطة يمكن أن تتغير بناء على الصورة التي تم تحميلها بحيث يمكن استخدام أي صورة بحجمها في وقت التشغيل، وهذا يسمح الآن بتحميل أي حجم صورة، ولكن هذا يعني أيضا أن المحول البرمجي لا يمكن تحسينها في عملية الضرب الأكثر بساطة والتي يمكن أن تفعلها إذا تم الإعلان عنها "const". سيكون اقتراحي هو حساب حفلات هذه الثوابت وضربها مسبقا. بقدر ما أستطيع أن أتذكر، تستغرق عملية الضرب حوالي 10 دورات على مدار الساعة، حيث تستغرق الانقسام حوالي 70. هذه هي زيادة قدرها 60 دورة لكل بكسل، ومع ما سبق المذكورة أعلاه 5000 × 5000، وهذا زاد سرعة تقدير قدره 1.5 ثانية على 1 جيجا هرتز وحدة المعالجة المركزية.

يعتمد على الرقاقة والمترجم وتفاصيل التعليمات البرمجية، و ... ولكن غالبا ما يتم تنفيذ هذا كجدول قفزة، يجب أن يكون سريعا جدا.

راجع للشغل - فهم هذا النوع من الأشياء هي حجة جيدة جدا لقضاء أسبوعين في تعلم بعض الجمعية في مرحلة ما في حياتك المهنية ...

ربما يكون استخدام مفتاح التبديل أفضل لكل من وقت السرعة والبرمجيات. أنت تجعل رمز أقل زائدة، وربما لن تتطلب إطارا جديدا كومة.

مفاتيح فعالة للغاية حتى يتمكنوا من استخدامها للغرابة والمربكة السحر الأسود.

لكن الكفاءة هي اسم اللعبة هنا.

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

أيضا، بدلا من القيام بتعليمات المتفرعة في كل مرة، يمكنك استدعاء مؤشر دالة من مجموعة من مؤشرات الوظائف، حيث يعمل الفهرس كمعرف وضعك.

بحيث ينتهي بك مع المكالمات مثل:

computeWeight[mode](pixel);

بمعدل 5000 × 5000 بكسل، يمكن أيضا تقليل وظيفة استدعاء الوظيفة عن طريق استدعاء الوظيفة لمجموعة من البكسل، بدلا من البكسلات الفردية.

يمكنك أيضا استخدام حلقة غير مثقوبة والمعلمة التي تمر بالرجوع / المؤشر، من أجل تحسين هذا الأمر.

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

لذلك في حالة حدوث الحالة 4 في كثير من الأحيان الحالة 1، يجب أن تكون فوقها:

switch (mode) {
    case 4:
        // ..
        break;
    case 1:
        // ..
        break;
}

سيء للغاية كنت لا تستخدم C ++، لأنه، يمكن استبدال عبارة التبديل مع تعدد الأشكال.

هتافات !

هناك الكثير من الاقتراحات الإبداعية في هذا الموضوع بطرق عدم الاضطرار إلى كتابة 5 وظائف منفصلة.

ما لم تقرأ "وضع" من ملف أو من إدخال مكتوب يمكن تحديد طريقة الحساب في وقت الترجمة. كقاعدة عامة لا ترغب في نقل العمليات الحسابية من وقت الترجمة إلى وقت التشغيل.

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

أيضا عند الحصول على الأخطاء في الرمز المحيط، فلن تضطر إلى البحث إذا تم تعيين ENUM على القيمة الخطأ أم لا.

فيما يتعلق بالحلقات الداخلية ... 0-> var أفضل القيام به var-> 0 as var-- يثير علامة الصفر (6502 يوما). هذا النهج يعني أيضا تحميل "العرض" في X ويمكن نسيانه، كما ينطبق الشيء نفسه على "الارتفاع". عادة ما تكون البكسلات في الذاكرة عادة ما تكون متبقية> على اليمين، أعلى -> أسفل بالتأكيد X كحل حلقة الداخلية.

for (y = height; y--;) {
    for (x = width; x--;) {
         weight = fun();
         // Calculate the new pixel value given the weight
         ...
    }
}

أيضا ... والأهمية للغاية هو عبارات التبديل الخاصة بك فقط لديك 2 حالات تستخدم x أو y. الباقي هو الثوابت.

 switch (mode)                  /* select the type of calculation */
 {
     case 0:
        weight = dCentre / maxDistanceEdge;
        break;
     //case 1: 
     //  weight = (float)x/width;             
     // break;
     //case 2: 
     //     weight = (float)y/height;
     //     break;
     case 3: 
          weight = dBottomLeft / maxDistanceCorner;
          break;
      case 4: 
           weight = dTopRight / maxDistanceCorner;
           break;
      default: 
           weight = 1;
           break;
 }

لذلك أساسا ما لم يتم حساب وضع 1 أو 2 الوزن قبل الحلقة.

... Y loop code here

    if (mode == 2) { weight = (float)y/height; } // calc only once per Y loop

    ... X loop here

        if (mode == 1) { weight = (float)x/width; } // after this all cases have filled weight
        calc_pixel_using_weight(weight);

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

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