سؤال

أنا فقط أقوم بمراجعة الفصل 4 من C# في العمق الذي يتعامل مع أنواع لا لبس فيه ، وأضيف قسمًا حول استخدام "AS" المشغل ، والذي يتيح لك الكتابة:

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... // Use x.Value in here
}

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

هذا يبدو أنه ليس هو الحال ، ولكن. لقد قمت بتضمين تطبيق اختبار عينة أدناه ، والذي يلخص بشكل أساسي جميع الأعداد الصحيحة داخل صفيف الكائن - لكن الصفيف يحتوي على الكثير من المراجع الفارغة ومراجع السلسلة وكذلك الأعداد الصحيحة المعبأة. يقيس المعيار الرمز الذي يجب أن تستخدمه في C# 1 ، والرمز باستخدام مشغل "AS" ، وفقط للركل حل LINQ. بالنسبة إلى دهشتي ، يكون رمز C# 1 أسرع 20 مرة في هذه الحالة - وحتى رمز LINQ (الذي كنت أتوقع أن يكون أبطأ ، بالنظر إلى التكرار المعنيين) يتفوق على رمز "AS".

هو تنفيذ .NET لـ isinst لأنواع قابلة للبطولة بطيئة حقًا؟ هل هو إضافي unbox.any التي تسبب المشكلة؟ هل هناك تفسير آخر لهذا؟ في الوقت الحالي ، يبدو أنني سأضطر إلى تضمين تحذير من استخدام هذا في المواقف الحساسة للأداء ...

نتائج:

الممثلون: 10000000: 121
AS: 10000000: 2211
LINQ: 10000000: 2143

شفرة:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i+1] = "";
            values[i+2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAs(values);
        FindSumWithLinq(values);
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int) o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }
}
هل كانت مفيدة؟

المحلول

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

ال هو يعد اختبار المشغل سهلًا ، فقط تحقق مما إذا كان الكائن فارغًا وهو من النوع المتوقع ، يستغرق سوى عدد قليل من تعليمات رمز الجهاز. من السهل أيضًا أن يكون فريق CAST ، يعرف موقع JIT موقع بت القيمة في الكائن ويستخدمها مباشرة. لا يحدث أي نسخ أو تحويل ، كل رمز الجهاز مضمّن ويأخذ ولكن حوالي عشرة تعليمات. يجب أن يكون هذا فعالًا حقًا في .NET 1.0 عندما كانت الملاكمة شائعة.

صب إلى int؟ يأخذ الكثير من العمل. تمثيل القيمة للبكتيريا المعبأة غير متوافق مع تخطيط الذاكرة لـ Nullable<int>. هناك حاجة إلى تحويل والرمز صعب بسبب أنواع التعداد المحاصر المحتملة. يقوم برنامج التحويل البرمجي JIT بإنشاء مكالمة إلى وظيفة Helper CLR تسمى JIT_UNBOX_NULLABLE لإنجاز المهمة. هذه وظيفة غرض عام لأي نوع من القيمة ، والكثير من التعليمات البرمجية هناك للتحقق من الأنواع. ويتم نسخ القيمة. من الصعب تقدير التكلفة نظرًا لأن هذا الرمز يتم إغلاقه داخل mscorwks.dll ، ولكن من المحتمل أن تكون مئات تعليمات رمز الجهاز.

تستخدم طريقة تمديد LINQ OFTYPE () أيضًا هو المشغل والممثلين. ومع ذلك ، فإن هذا هو المصبوب لنوع عام. يقوم برنامج التحويل البرمجي JIT بإنشاء مكالمة إلى دالة المساعد ، JIT_UNBOX () يمكنها أداء فريق الممثلين على نوع القيمة التعسفية. ليس لدي تفسير رائع لماذا بطيئة مثل الممثلين Nullable<int>, ، بالنظر إلى أن العمل الأقل يجب أن يكون ضروريًا. أظن أن ngen.exe قد يسبب مشكلة هنا.

نصائح أخرى

يبدو لي أن isinst هو مجرد بطيئة حقا على الأنواع الباطلة. في الطريقة FindSumWithCast لقد تغيرت

if (o is int)

ل

if (o is int?)

الذي يبطئ بشكل كبير التنفيذ. الاختلاف الوحيد في IL الذي يمكنني رؤيته هو ذلك

isinst     [mscorlib]System.Int32

يتم تغييرها إلى

isinst     valuetype [mscorlib]System.Nullable`1<int32>

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

أولا ، ج# as سوف ينبعث المشغل isinst تعليمات IL (وكذلك الأمر is المشغل أو العامل). (تعليمات أخرى مثيرة للاهتمام castclass, ، تم وضعه عند القيام بممثل مباشر ويعرف المترجم أن فحص وقت التشغيل لا يمكن أن يكون مُلحقًا.)

هنا هو ما isinst يفعل (ECMA 335 Partition III ، 4.6):

صيغة: isinst Typetok

Typetok هو رمز بيانات التعريف (أ typeref, typedef أو typespec) ، مما يشير إلى الفئة المطلوبة.

لو Typetok هو نوع قيمة غير قابل للفرق أو نوع معلمة عامة يتم تفسيره على أنه "محاصر" Typetok.

لو Typetok هو نوع لاغية ، Nullable<T>, ، يتم تفسيره على أنه "محاصر" T

الاكثر اهمية:

إذا كان النوع الفعلي (وليس نوع تتبع المدقق) من OBJ هو expier-assignable-to نوع typetok ثم isinst ينجح و OBJ (مثل نتيجة) يتم إرجاعه دون تغيير بينما يتتبع التحقق نوعه ك Typetok. على عكس الإكراه (§1.6) والتحويلات (§3.27) ، isinst لا تغير أبدًا النوع الفعلي للكائن ويحافظ على هوية الكائن (انظر القسم I).

لذلك ، قاتل الأداء ليس isinst في هذه الحالة ، ولكن الإضافية unbox.any. لم يكن هذا واضحًا من إجابة هانز ، حيث نظر إلى رمز Jited فقط. بشكل عام ، سوف ينبع من برنامج التحويل البرمجي C unbox.any بعد isinst T? (ولكن سوف يحذفها في حال كنت تفعل isinst T, ، متى T هو نوع مرجعي).

لماذا تفعل ذلك؟ isinst T? لم يكن له أي تأثير واضح ، أي أنك تعود T?. بدلاً من ذلك ، تضمن كل هذه التعليمات أن لديك "boxed T" التي يمكن أن تكون غير مربحة ل T?. للحصول على فعلي T?, ، ما زلنا بحاجة إلى إلغاء مربعنا "boxed T" ل T?, ، ولهذا السبب ينبعث المترجم unbox.any بعد isinst. إذا كنت تفكر في ذلك ، فهذا أمر منطقي لأن "تنسيق المربع" لـ T? ما هو الا "boxed T" وصنع castclass و isinst أداء unbox سيكون غير متناسق.

نسخة احتياطية من اكتشاف هانز مع بعض المعلومات من اساسي, ، من هنا تبدأ:

(ECMA 335 Partition III ، 4.33): unbox.any

عند تطبيقه على النموذج المعبأ من نوع القيمة ، unbox.any تستخلص التعليمات القيمة الواردة داخل OBJ (من النوع O). (يعادل unbox تليها ldobj.) عند تطبيقه على نوع مرجعي ، unbox.any التعليم له نفس تأثير castclass Typetok.

(ECMA 335 Partition III ، 4.32): unbox

عادة، unbox ببساطة يحسب عنوان نوع القيمة الموجود بالفعل داخل الكائن المحاصر. هذا النهج غير ممكن عند إلغاء توصيل أنواع القيمة الموروثة. لان Nullable<T> يتم تحويل القيم إلى Boxed Ts أثناء تشغيل الصندوق ، غالبًا ما يجب على التنفيذ تصنيع جديد Nullable<T> على الكومة وحساب العنوان إلى الكائن المخصص حديثًا.

ومن المثير للاهتمام ، لقد مررت بتعليقات حول دعم المشغل عبر dynamic كونه أمرًا أبطأ Nullable<T> (مشابه ل هذا الاختبار المبكر) - أظن لأسباب مماثلة للغاية.

فلدي الحب Nullable<T>. متعة أخرى هي أنه على الرغم من بقع JIT (وتزيل) null بالنسبة للهياكل غير القابلة للفرق ، فإنه يحتلها Nullable<T>:

using System;
using System.Diagnostics;
static class Program {
    static void Main() { 
        // JIT
        TestUnrestricted<int>(1,5);
        TestUnrestricted<string>("abc",5);
        TestUnrestricted<int?>(1,5);
        TestNullable<int>(1, 5);

        const int LOOP = 100000000;
        Console.WriteLine(TestUnrestricted<int>(1, LOOP));
        Console.WriteLine(TestUnrestricted<string>("abc", LOOP));
        Console.WriteLine(TestUnrestricted<int?>(1, LOOP));
        Console.WriteLine(TestNullable<int>(1, LOOP));

    }
    static long TestUnrestricted<T>(T x, int loop) {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
    static long TestNullable<T>(T? x, int loop) where T : struct {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
}

هذه هي نتيجة FindSumwithasandhas أعلاه: alt text

هذه هي نتيجة FindSumwithcast: alt text

الموجودات:

  • استخدام as, ، يختبر أولاً إذا كان الكائن مثيلًا لـ int32 ؛ تحت الغطاء يستخدم isinst Int32 (وهو مشابه للرمز المكتوب يدويًا: إذا (O هو int)). واستخدام as, ، كما أنه غير شرطية الكائن دون قيد أو شرط. وهو قاتل أداء حقيقي للاتصال بممتلكات (لا تزال وظيفة تحت الغطاء) ، IL_0027

  • باستخدام الممثلين ، يمكنك الاختبار أولاً إذا كان الكائن int if (o is int); ؛ تحت الغطاء هذا يستخدم isinst Int32. إذا كان مثيلًا لـ Int ، فيمكنك إلغاء صناديق القيمة بأمان ، IL_002D

ببساطة ، هذا هو الرمز الزائفة لاستخدام as مقاربة:

int? x;

(x.HasValue, x.Value) = (o isinst Int32, o unbox Int32)

if (x.HasValue)
    sum += x.Value;    

وهذا هو الرمز الزائف لاستخدام نهج الممثلين:

if (o isinst Int32)
    sum += (o unbox Int32)

لذلك الممثلين ((int)a[i], ، حسنًا ، يبدو أن بناء الجملة مثل الممثلين ، لكنه في الواقع إلغاء التزامن ، ويشترك المصطلحون وملعون الصناديق في نفس الجملة ، في المرة القادمة ، سأكون متغلبًا مع المصطلحات الصحيحة) أسرع حقًا ، فأنت بحاجة فقط إلى إلغاء قيمة A Boxing عندما يكون الكائن بالتأكيد int. لا يمكن قول نفس الشيء لاستخدام as مقاربة.

التنميط كذلك:

using System;
using System.Diagnostics;

class Program
{
    const int Size = 30000000;

    static void Main(string[] args)
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "";
            values[i + 2] = 1;
        }

        FindSumWithIsThenCast(values);

        FindSumWithAsThenHasThenValue(values);
        FindSumWithAsThenHasThenCast(values);

        FindSumWithManualAs(values);
        FindSumWithAsThenManualHasThenValue(values);



        Console.ReadLine();
    }

    static void FindSumWithIsThenCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int)o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenValue(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (x.HasValue)
            {
                sum += (int)o;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Has then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithManualAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            bool hasValue = o is int;
            int x = hasValue ? (int)o : 0;

            if (hasValue)
            {
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Manual As: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenManualHasThenValue(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (o is int)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Manual Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

}

انتاج:

Is then Cast: 10000000 : 303
As then Has then Value: 10000000 : 3524
As then Has then Cast: 10000000 : 3272
Manual As: 10000000 : 395
As then Manual Has then Value: 10000000 : 3282

ماذا يمكننا أن نستنتج من هذه الأرقام؟

  • أولاً ، هو نهج IS-then-cast أسرع بكثير من مثل مقاربة. 303 مقابل 3524
  • ثانياً ،. القيمة أبطأ بشكل هامشي من الصب. 3524 مقابل 3272
  • ثالثًا ،. hasvalue أبطأ بشكل هامشي من استخدام اليدوي (أي استخدام هو). 3524 مقابل 3282
  • رابعًا ، إجراء مقارنة من التفاح إلى التفاح (أي تعيين القيمة المحاكاة وتحويل القيمة المحاكاة معًا) بينهما) بين محاكاة و حقيقي كما النهج ، يمكننا أن نرى محاكاة لا يزال أسرع بكثير من حقيقي كما. 395 مقابل 3524
  • أخيرًا ، استنادًا إلى الخلاصة الأولى والرابعة ، هناك خطأ ما في مثلالتنفيذ ^_ ^

من أجل الحفاظ على هذه الإجابة على تحديث ، تجدر الإشارة إلى أن معظم النقاش على هذه الصفحة أصبح الآن متخلفًا الآن ج# 7.1 و .NET 4.7 الذي يدعم بناء جملة نحيف الذي ينتج أيضًا أفضل رمز IL.

مثال OP الأصلي ...

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    // ...use x.Value in here
}

يصبح ببساطة ...

if (o is int x)
{
    // ...use x in here
}

لقد وجدت أن استخدامًا شائعًا للناء على بناء الجملة الجديد هو عندما تكتب .NET نوع القيمة (بمعنى آخر struct في ج#) التي تنفذ IEquatable<MyStruct> (كما ينبغي معظم). بعد تنفيذها بقوة Equals(MyStruct other) الطريقة ، يمكنك الآن إعادة توجيه الأعمق Equals(Object obj) تجاوز (ورثت من Object) على النحو التالي:

public override bool Equals(Object obj) => obj is MyStruct o && Equals(o);

 


زائدة: ال Release يبني انا يتم تقديم رمز لوظيفة المثال الأولين الموضحة أعلاه في هذه الإجابة (على التوالي) هنا. على الرغم من أن رمز IL الخاص ببناء بناء الجملة الجديد أصغر في الواقع ، إلا أنه يفوز في الغالب عن طريق إجراء مكالمات صفرية (مقابل اثنين) وتجنب unbox العملية تماما عندما يكون ذلك ممكنا.

// static void test1(Object o, ref int y)
// {
//     int? x = o as int?;
//     if (x.HasValue)
//         y = x.Value;
// }

[0] valuetype [mscorlib]Nullable`1<int32> x
        ldarg.0
        isinst [mscorlib]Nullable`1<int32>
        unbox.any [mscorlib]Nullable`1<int32>
        stloc.0
        ldloca.s x
        call instance bool [mscorlib]Nullable`1<int32>::get_HasValue()
        brfalse.s L_001e
        ldarg.1
        ldloca.s x
        call instance !0 [mscorlib]Nullable`1<int32>::get_Value()
        stind.i4
L_001e: ret

// static void test2(Object o, ref int y)
// {
//     if (o is int x)
//         y = x;
// }

[0] int32 x,
[1] object obj2
        ldarg.0
        stloc.1
        ldloc.1
        isinst int32
        ldnull
        cgt.un
        dup
        brtrue.s L_0011
        ldc.i4.0
        br.s L_0017
L_0011: ldloc.1
        unbox.any int32
L_0017: stloc.0
        brfalse.s L_001d
        ldarg.1
        ldloc.0
        stind.i4
L_001d: ret

لمزيد من الاختبار الذي يثبت ملاحظتي حول أداء الجديد ج#7 بناء الجملة تتجاوز الخيارات المتاحة مسبقًا ، انظر هنا (على وجه الخصوص ، مثال "د").

ليس لدي وقت لتجربته ، لكن قد ترغب في الحصول على:

foreach (object o in values)
        {
            int? x = o as int?;

مثل

int? x;
foreach (object o in values)
        {
            x = o as int?;

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

جربت نوع التحقق من النوع الدقيق

typeof(int) == item.GetType(), ، والذي يؤدي بأسرع ما item is int الإصدار ، ودائمًا ما يعيد الرقم (التركيز: حتى لو كتبت أ Nullable<int> إلى المصفوفة ، ستحتاج إلى استخدامها typeof(int)). تحتاج أيضًا إلى إضافي null != item تحقق هنا.

لكن

typeof(int?) == item.GetType() يبقى سريعًا (على عكس item is int?) ، ولكن دائما يعود خطأ.

البناء من النوع في عيني أسرع طريقة ل بالضبط اكتب التحقق ، لأنه يستخدم RunTimeTypeHandle. نظرًا لأن الأنواع الدقيقة في هذه الحالة لا تتطابق مع Nullable ، فإن تخميني هو ، is/as يجب أن تقوم بالسماح الإضافي هنا لضمان أنه في الواقع مثيل لنوع لاغية.

وبصراحة: ماذا تفعل is Nullable<xxx> plus HasValue يشتري لك؟ لا شيئ. يمكنك دائمًا الانتقال مباشرة إلى نوع (القيمة) الأساسية (في هذه الحالة). يمكنك إما الحصول على القيمة أو "لا ، وليس مثيل من النوع الذي كنت تطلبه". حتى لو كتبت (int?)null إلى المصفوفة ، سيعود فحص النوع FALSE.

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "";
            values[i + 2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAsAndHas(values);
        FindSumWithAsAndIs(values);


        FindSumWithIsThenAs(values);
        FindSumWithIsThenConvert(values);

        FindSumWithLinq(values);



        Console.ReadLine();
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int)o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsAndHas(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As and Has: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }


    static void FindSumWithAsAndIs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (o is int)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As and Is: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }







    static void FindSumWithIsThenAs(object[] values)
    {
        // Apple-to-apple comparison with Cast routine above.
        // Using the similar steps in Cast routine above,
        // the AS here cannot be slower than Linq.



        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {

            if (o is int)
            {
                int? x = o as int?;
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then As: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithIsThenConvert(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {            
            if (o is int)
            {
                int x = Convert.ToInt32(o);
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then Convert: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }



    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }
}

المخرجات:

Cast: 10000000 : 456
As and Has: 10000000 : 2103
As and Is: 10000000 : 2029
Is then As: 10000000 : 1376
Is then Convert: 10000000 : 566
LINQ: 10000000 : 1811

تحرير: 2010-06-19

ملاحظة: تم إجراء الاختبار السابق داخل VS ، تصحيح التكوين ، باستخدام VS2009 ، باستخدام Core i7 (آلة تطوير الشركة).

تم إجراء ما يلي على الجهاز الخاص بي باستخدام Core 2 Duo ، باستخدام VS2010

Inside VS, Configuration: Debug

Cast: 10000000 : 309
As and Has: 10000000 : 3322
As and Is: 10000000 : 3249
Is then As: 10000000 : 1926
Is then Convert: 10000000 : 410
LINQ: 10000000 : 2018




Outside VS, Configuration: Debug

Cast: 10000000 : 303
As and Has: 10000000 : 3314
As and Is: 10000000 : 3230
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 418
LINQ: 10000000 : 1944




Inside VS, Configuration: Release

Cast: 10000000 : 305
As and Has: 10000000 : 3327
As and Is: 10000000 : 3265
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1932




Outside VS, Configuration: Release

Cast: 10000000 : 301
As and Has: 10000000 : 3274
As and Is: 10000000 : 3240
Is then As: 10000000 : 1904
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1936
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top