سؤال

يفعل خارج المعلمات في ج# هل لديك أي آثار على الأداء يجب أن أعرف عنها؟(مثل الاستثناءات)

أعني، هل من الجيد أن يكون لديك طريقة مع out المعلمة في حلقة سيتم تشغيلها بضعة ملايين مرة في الثانية؟

أعلم أنه قبيح ولكني أستخدمه بنفس الطريقة Int32.TryParse يستخدمها - يعود أ bool لمعرفة ما إذا كان بعض التحقق من الصحة ناجحًا ولديه out المعلمة التي تحتوي على بعض البيانات الإضافية إذا كانت ناجحة.

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

المحلول

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

يحرر:بقية هذا جانبا، على نحو فعال.إنها ذات صلة فقط بأنواع القيمة الكبيرة، والتي عادةً ما يجب تجنبها على أي حال :)

أنا لا أتفق مع تأكيد كونراد حول "يتم التعامل مع قيم الإرجاع لجميع الأنواع> 32 بت بشكل مشابه أو مطابق للوسيطات الخارجية على مستوى الجهاز على أي حال".إليك تطبيق اختبار صغير:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

struct BigStruct
{
    public Guid guid1, guid2, guid3, guid4;
    public decimal dec1, dec2, dec3, dec4;
}

class Test
{
    const int Iterations = 100000000;

    static void Main()
    {
        decimal total = 0m;
        // JIT first
        ReturnValue();
        BigStruct tmp;
        OutParameter(out tmp);

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            BigStruct bs = ReturnValue();
            total += bs.dec1;
        }
        sw.Stop();
        Console.WriteLine("Using return value: {0}",
                          sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            BigStruct bs;
            OutParameter(out bs);
            total += bs.dec1;
        }
        Console.WriteLine("Using out parameter: {0}",
                          sw.ElapsedMilliseconds);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static BigStruct ReturnValue()
    {
        return new BigStruct();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void OutParameter(out BigStruct x)
    {
        x = new BigStruct();
    }
}

نتائج:

Using return value: 11316
Using out parameter: 7461

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

لا تتردد في انتقاد التطبيق المعياري رغم ذلك - ربما فاتني شيء ما!

نصائح أخرى

ليست مشكلة في الأداء، ولكنها شيء ظهر سابقًا - لا يمكنك استخدامها مع التباين في C# 4.0.

شخصيا، أنا أميل إلى استخدام out معلمات مبلغ لا بأس به في بلدي خاص الكود (أيداخل الفصل الدراسي، مع وجود طريقة تُرجع قيمًا متعددة دون استخدام نوع منفصل) - لكنني أميل إلى تجنبها على واجهة برمجة التطبيقات العامة، باستثناء bool Try{Something}(out result) نمط.

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

في الواقع، يتم التعامل مع قيم الإرجاع لجميع الأنواع> 32 بت بطريقة مماثلة out الحجج على مستوى الآلة على أي حال.

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

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

السبب الرئيسي لتجنب المعلمات هو سهولة قراءة التعليمات البرمجية، بدلاً من الأداء.

بالنسبة لأنواع القيم، لا يوجد فرق حقيقي على أي حال (فهي تنسخ دائمًا) وبالنسبة للأنواع المرجعية، فهي في الأساس مماثلة للتمرير بالمرجع.

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

يتم تمرير المعلمات خارج المرجع.لذلك تم تمرير مؤشر فقط على المكدس.

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

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

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