لنفترض أن هناك سلسلة، سنقوم بالكثير من عمليات الربط الحلقي على هذه السلسلة، باستخدام "+" سيحصل على أقل أداء. ولكن ما مدى سوء هذا الأداء؟ ماذا ستكون النتيجة إذا وضعنا أيضًا StringBuffer أو StringBuilder أو String.concat() في اختبار الأداء؟ هذه المقالة سوف تعطي إجابة على هذه الأسئلة!
سوف نستخدم Per4j لحساب الأداء، لأن هذه الأداة يمكن أن تعطينا مجموعة كاملة من مؤشرات الأداء، مثل الحد الأدنى والحد الأقصى للوقت المستغرق، والانحراف المعياري للفترة الزمنية الإحصائية، وما إلى ذلك. في رمز الاختبار، من أجل الحصول على قيمة انحراف معياري دقيقة، سنقوم بإجراء اختبار 20 ربط "*" 50000 مرة. إليك الطريقة التي سنستخدمها لتسلسل السلاسل:
انسخ رمز الكود كما يلي:
عامل التسلسل (+)
طريقة سلسلة concat concat (سلسلة سلسلة)
طريقة إلحاق StringBuffer إلحاق (String str)
طريقة إلحاق StringBuilder إلحاق (String str)
أخيرًا، سنلقي نظرة على الكود الثانوي لنرى كيف يتم تنفيذ هذه الطرق بالضبط. الآن، لنبدأ بإنشاء صفنا. لاحظ أنه من أجل حساب أداء كل حلقة، يجب تغليف كل كود اختبار في الكود بمكتبة Per4J. أولاً نحدد عدد التكرارات
انسخ رمز الكود كما يلي:
نهائي ثابت خاص OUTER_ITERATION=20;
نهائي ثابت خاص INNER_ITERATION=50000;
بعد ذلك، سوف نستخدم الطرق الأربع المذكورة أعلاه لتنفيذ كود الاختبار الخاص بنا.
انسخ رمز الكود كما يلي:
String addTestStr = "";
String concatTestStr = "";
StringBuffer concatTestSb = null;
StringBuilder concatTestSbu = null;
for (int ExternalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringAddConcat");
addTestStr = "";
لـ (intInnerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
addTestStr += "*";
stopWatch.stop();
}
for (int ExternalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringConcat");
concatTestStr = "";
لـ (intInnerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestStr.concat("*");
stopWatch.stop();
}
for (int ExternalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBufferConcat");
concatTestSb = new StringBuffer();
لـ (intInnerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSb.append("*");
stopWatch.stop();
}
for (int ExternalIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {
StopWatch stopWatch = new LoggingStopWatch("StringBuilderConcat");
concatTestSbu = new StringBuilder();
لـ (intInnerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)
concatTestSbu.append("*");
stopWatch.stop();
}
قم بعد ذلك بتشغيل البرنامج لإنشاء مقاييس الأداء. بيئة التشغيل الخاصة بي هي نظام تشغيل Windows 7 64 بت، وجهاز JVM (7-ea) 32 بت مع ذاكرة سعة 4 جيجابايت ووحدة معالجة مركزية رباعية النواة بسرعة 2.00 جيجا هرتز.
لقد اتضح الأمر تمامًا كما تخيلنا. الشيء الوحيد المثير للاهتمام هو أن String.concat جيد جدًا أيضًا. نعلم جميعًا أن String هي فئة ثابتة (فئة لن تتغير بعد التهيئة)، فلماذا يكون أداء concat أفضل؟ (ملاحظة المترجم: في الواقع، هناك مشكلة في رمز الاختبار للمؤلف الأصلي. يجب كتابة رمز الاختبار لطريقة concat() بالشكل concatTestStr=concatTestStr.concat("*").) للإجابة على هذا السؤال سؤال، يجب أن ننظر إلى عملية فك التجميع المتزامنة، حيث يخرج الرمز الثانوي. تحتوي حزمة التنزيل لهذه المقالة على جميع الرموز الثانوية، ولكن الآن دعونا نلقي نظرة على مقتطف التعليمات البرمجية الخاص بـ concat:
انسخ رمز الكود كما يلي:
46: جديد #6؛ //class java/lang/StringBuilder
49 : مكرر
50: invocespecial #7; //Method java/lang/StringBuilder."<init>":()V
53: aload_1
54: استدعاء افتراضي #8؛ //الطريقة java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
57: إل دي سي #9؛
59: استدعاء افتراضي #8؛ //الطريقة java/lang/StringBuilder.append:
(Ljava/lang/String;)Ljava/lang/StringBuilder;
62: استدعاء افتراضي #10؛ //الطريقة java/lang/StringBuilder.toString:()
جافا/لانج/سلسلة؛
65: أستور_1
66: inc 7، 1
69: اذهب إلى 38
هذا الرمز هو الرمز الفرعي لـ String.concat(). من هذا الرمز، يمكننا أن نرى بوضوح أن طريقة concat() تستخدم StringBuilder. يجب أن يكون أداء concat() جيدًا مثل StringBuilder، ولكن بسبب إنشاء StringBuilder الإضافي. سيؤدي إجراء عملية .append(str).append(str).toString() إلى التأثير على أداء سلسلة، لذلك StringBuilder وString أوقات Cancate هي 1.8 و 3.3.
لذلك، حتى عند القيام بأبسط عملية تسلسل، إذا لم نرغب في إنشاء مثيل StringBuffer أو StringBuilder، فيجب علينا استخدام concat. ولكن بالنسبة لعدد كبير من عمليات ربط السلسلة، لا ينبغي لنا استخدام concat (ملاحظة المترجم: نظرًا لأن رمز الاختبار ليس متكافئًا تمامًا في الوظيفة، فإن متوسط وقت معالجة concat في رمز الاختبار المستبدل هو 1650.9 مللي ثانية. هذه النتيجة في التعليق النصي الأصلي)، لأن concat سيقلل من أداء البرنامج ويستهلك وحدة المعالجة المركزية الخاصة بك. لذلك، من أجل الحصول على أعلى أداء دون مراعاة سلامة الخيط والمزامنة، يجب أن نحاول استخدام StringBuilder.