هل استخدام طريقة إزالة StringBuilder أكثر كفاءة في الذاكرة من إنشاء StringBuilder جديد في الحلقة؟

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

سؤال

في C# وهو أكثر كفاءة في الذاكرة:الخيار رقم 1 أو الخيار رقم 2؟

public void TestStringBuilder()
{
    //potentially a collection with several hundred items:
    string[] outputStrings = new string[] { "test1", "test2", "test3" };

    //Option #1
    StringBuilder formattedOutput = new StringBuilder();
    foreach (string outputString in outputStrings)
    {
        formattedOutput.Append("prefix ");
        formattedOutput.Append(outputString);
        formattedOutput.Append(" postfix");

        string output = formattedOutput.ToString();
        ExistingOutputMethodThatOnlyTakesAString(output);

        //Clear existing string to make ready for next iteration:
        formattedOutput.Remove(0, output.Length);
    }

    //Option #2
    foreach (string outputString in outputStrings)
    {
        StringBuilder formattedOutputInsideALoop = new StringBuilder();

        formattedOutputInsideALoop.Append("prefix ");
        formattedOutputInsideALoop.Append(outputString);
        formattedOutputInsideALoop.Append(" postfix");

        ExistingOutputMethodThatOnlyTakesAString(
           formattedOutputInsideALoop.ToString());
    }
}

private void ExistingOutputMethodThatOnlyTakesAString(string output)
{
    //This method actually writes out to a file.
    System.Console.WriteLine(output);
}
هل كانت مفيدة؟

المحلول

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

ولقد عدلت الخيار رقم 1 للاستفادة منTy اقتراح لاستخدام StringBuilder.Length = 0 بدلا من أسلوب إزالة. جعل هذا الرمز من الخيارين أكثر مماثلة. الاختلافات هما الآن ما إذا كان منشئ للب StringBuilder في أو الخروج من حلقة والخيار رقم 1 يستخدم الآن طريقة طول لمسح ل StringBuilder. تم تعيين كل من الخيارات لدهس مجموعة outputStrings مع 100،000 عناصر لجعل جامع القمامة القيام ببعض الأعمال.

وهناك إجابات زوجين عرضت تلميحات للنظر في مختلف عدادات بيرفمون وكذا واستخدام النتائج لاختيار الخيار. لقد قمت ببعض البحوث، وانتهى الأمر باستخدام المدمج في مستكشف أداء Visual Studio فريق نظام المطور الطبعة التي لدي في العمل. لقد وجدت دخول بلوق الثاني من سلسلة متعددة أن أوضح كيفية إعداده <لأ href = "http://blogs.msdn.com/ianhu/archive/2005/02/11/371418.aspx" يختلط = "noreferrer "> هنا . في الأساس، يمكنك سلك يصل وحدة الاختبار للإشارة إلى الرمز الذي تريد الشخصية. تذهب من خلال المعالج وبعض التكوينات. وإطلاق التنميط وحدة الاختبار. I تمكين الكائن. NET تخصيص وعمر المقاييس. نتائج التنميط حيث يصعب تنسيق لهذه الإجابة لذلك أنا وضعت لهم في نهاية المطاف. إذا قمت بنسخ ولصق النص في Excel و تدليك لهم قليلا، وأنها سوف تكون قابلة للقراءة.

والخيار رقم 1 هو الأكثر كفاءة الذاكرة لأنه يجعل جمع القمامة تفعل أقل قليلا عمل وتخصص نصف الذاكرة والحالات إلى كائن ل StringBuilder من الخيار رقم 2. لترميز كل يوم، واختيار الخيار رقم 2 على ما يرام تماما.

إذا كنت لا تزال القراءة، سألت هذا السؤال لالخيار رقم 2 سيجعل للكشف عن تسرب الذاكرة من مطور تجربة C / C ++ الذهاب البالستية. ويحدث تسرب للذاكرة ضخمة إذا لم يتم إطلاق سراح المثال ب StringBuilder قبل أن يتم تعيينهم. بطبيعة الحال، نحن C # المطورين لا تقلق بشأن مثل هذه الأشياء (حتى تقفز وعضة لنا). شكرا للجميع !!


ClassName   Instances   TotalBytesAllocated Gen0_InstancesCollected Gen0BytesCollected  Gen1InstancesCollected  Gen1BytesCollected
=======Option #1                    
System.Text.StringBuilder   100,001 2,000,020   100,016 2,000,320   2   40
System.String   301,020 32,587,168  201,147 11,165,268  3   246
System.Char[]   200,000 8,977,780   200,022 8,979,678   2   90
System.String[] 1   400,016 26  1,512   0   0
System.Int32    100,000 1,200,000   100,061 1,200,732   2   24
System.Object[] 100,000 2,000,000   100,070 2,004,092   2   40
======Option #2                 
System.Text.StringBuilder   200,000 4,000,000   200,011 4,000,220   4   80
System.String   401,018 37,587,036  301,127 16,164,318  3   214
System.Char[]   200,000 9,377,780   200,024 9,379,768   0   0
System.String[] 1   400,016 20  1,208   0   0
System.Int32    100,000 1,200,000   100,051 1,200,612   1   12
System.Object[] 100,000 2,000,000   100,058 2,003,004   1   20

نصائح أخرى

والخيار 2 يجب (أعتقد) فعلا الخيار يتفوق 1. فعل داعيا "القوات" Remove وب StringBuilder لأخذ نسخة من السلسلة انها عادت بالفعل. السلسلة قابلة للتغيير الواقع داخل ب StringBuilder، وب StringBuilder لا يأخذ نسخة إلا أنه يحتاج إلى. مع الخيار 1 فإنه ينسخ قبل تطهير أساسا مجموعة من - مع الخيار 2 أي نسخة مطلوب

.

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

وبصرف النظر عن الأداء، ومع ذلك، الخيار 2 أنظف فقط.

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

formattedOutput.Length = 0;

ومنذ كنت قلقا فقط مع ذاكرة أود أن أقترح:

foreach (string outputString in outputStrings)
    {    
        string output = "prefix " + outputString + " postfix";
        ExistingOutputMethodThatOnlyTakesAString(output)  
    }

وإخراج متغير اسمه هو نفس الحجم في تنفيذ الأصلي، ولكن هناك حاجة إلى أي أجسام أخرى. يستخدم ب StringBuilder السلاسل وغيرها من الأشياء داخليا، وسوف يتم إنشاء العديد من الكائنات التي تحتاج إلى GC'd.

وكل خط من الخيار 1:

string output = formattedOutput.ToString();

والخط من الخيار 2:

ExistingOutputMethodThatOnlyTakesAString(
           formattedOutputInsideALoop.ToString());

وسيتم إنشاء <م> غير قابل للتغيير الكائن مع قيمة البادئة + outputString + لواحق. هذه السلسلة هي نفس الحجم بغض النظر عن كيفية إنشائه. ما كنت طالبا حقا هو الذي هو أكثر كفاءة الذاكرة:

    StringBuilder formattedOutput = new StringBuilder(); 
    // create new string builder

أو

    formattedOutput.Remove(0, output.Length); 
    // reuse existing string builder

وتخطي ب StringBuilder تماما سوف تكون أكثر كفاءة الذاكرة من أي من أعلاه.

إذا كنت حقا بحاجة لمعرفة أي من الاثنين هو أكثر كفاءة في التطبيق الخاص بك (وهذا سوف ربما تختلف استنادا إلى حجم الخاص بك القائمة، بادئة، وoutputStrings) أوصي النمل البوابة الحمراء التعريف <لأ href = " http://www.red-gate.com/products/ants_profiler/index.htm "يختلط =" نوفولو noreferrer "عنوان =" http://www.red-gate.com/products/ants_profiler/index.htm "> http://www.red-gate.com/products/ants_profiler/index.htm

وجيسون

وأكره أن أقول ذلك، ولكن ماذا عن مجرد اختبار ذلك؟

وهذه الاشياء هي سهلة لمعرفة من قبل نفسك. تشغيل Perfmon.exe وإضافة عداد ل. NET الذاكرة + الجنرال 0 المجموعات. تشغيل التعليمات البرمجية اختبار مليون مرة. سترى أن الخيار رقم 1 يتطلب نصف عدد 2 احتياجات الخيار مجموعات #.

لقد تحدثنا عن هذا من قبل مع جافا ، وهنا و[الإصدار] نتائج C # نسخة:

Option #1 (10000000 iterations): 11264ms
Option #2 (10000000 iterations): 12779ms

تحديث: في التحليل غير العلمي بلدي السماح طريقتين لتنفيذ بينما رصد جميع عدادات أداء الذاكرة في الأداء لم تسفر عن أي نوع من الفرق واضح مع أي طريقة (عدا وجود بعض عدادات ارتفاع فقط، بينما كان إما اختبار تنفيذ).

وهنا ما كنت لاختبار:

class Program
{
    const int __iterations = 10000000;

    static void Main(string[] args)
    {
        TestStringBuilder();
        Console.ReadLine();
    }

    public static void TestStringBuilder()
    {
        //potentially a collection with several hundred items:
        var outputStrings = new [] { "test1", "test2", "test3" };

        var stopWatch = new Stopwatch();

        //Option #1
        stopWatch.Start();
        var formattedOutput = new StringBuilder();

        for (var i = 0; i < __iterations; i++)
        {
            foreach (var outputString in outputStrings)
            {
                formattedOutput.Append("prefix ");
                formattedOutput.Append(outputString);
                formattedOutput.Append(" postfix");

                var output = formattedOutput.ToString();
                ExistingOutputMethodThatOnlyTakesAString(output);

                //Clear existing string to make ready for next iteration:
                formattedOutput.Remove(0, output.Length);
            }
        }
        stopWatch.Stop();

        Console.WriteLine("Option #1 ({1} iterations): {0}ms", stopWatch.ElapsedMilliseconds, __iterations);
            Console.ReadLine();
        stopWatch.Reset();

        //Option #2
        stopWatch.Start();
        for (var i = 0; i < __iterations; i++)
        {
            foreach (var outputString in outputStrings)
            {
                StringBuilder formattedOutputInsideALoop = new StringBuilder();

                formattedOutputInsideALoop.Append("prefix ");
                formattedOutputInsideALoop.Append(outputString);
                formattedOutputInsideALoop.Append(" postfix");

                ExistingOutputMethodThatOnlyTakesAString(
                   formattedOutputInsideALoop.ToString());
            }
        }
        stopWatch.Stop();

        Console.WriteLine("Option #2 ({1} iterations): {0}ms", stopWatch.ElapsedMilliseconds, __iterations);
    }

    private static void ExistingOutputMethodThatOnlyTakesAString(string s)
    {
        // do nothing
    }
} 

والخيار 1 في هذا السيناريو هو هامشي أسرع على الرغم من الخيار 2 أسهل للقراءة والمحافظة عليها. إلا إذا كنت يحدث ليكون تنفيذ هذه العملية ملايين المرات العودة إلى الوراء وأود أن العصا مع الخيار 2 لأنني أظن أن الخيار 1 و 2 عن نفسه عند التشغيل في التكرار واحد.

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

أعتقد أن الخيار 1 سيكون أكثر قليلاً ذاكرة فعالة ككائن جديد لا يتم إنشاؤه في كل مرة.بعد قولي هذا، يقوم GC بعمل جيد جدًا في تنظيف الموارد كما هو الحال في الخيار 2.

أعتقد أنك قد تقع في فخ التحسين المبكر (أصل كل الشر -كنوث).سيستهلك الإدخال/الإخراج موارد أكثر بكثير من أداة إنشاء السلسلة.

أميل إلى اختيار الخيار الأكثر وضوحًا/الأنظف، في هذه الحالة الخيار 2.

روب

  1. قيسها، قم بقياسها
  2. قم بالتخصيص المسبق قدر الإمكان لحجم الذاكرة الذي تعتقد أنك ستحتاج إليه
  3. إذا كانت السرعة هي ما تفضله، ففكر في اتباع نهج متزامن متعدد الخيوط من الأمام إلى الوسط ومن المتوسط ​​إلى النهاية (توسيع تقسيم العمل كما هو مطلوب)
  4. قياسه مرة أخرى

ما هو الأهم بالنسبة لك؟

  1. ذاكرة

  2. سرعة

  3. وضوح

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