السرعة إذا ما قورنت بالشرطية
-
21-12-2019 - |
سؤال
خطرت لي فكرة تحويل بعض كتل if الخاصة بي إلى أسطر مفردة، باستخدام العامل الشرطي.ومع ذلك، كنت أتساءل عما إذا كان سيكون هناك تناقض في السرعة.قمت بإجراء الاختبار التالي:
static long startTime;
static long elapsedTime;
static String s;
public static void main(String[] args) {
startTime = System.nanoTime();
s = "";
for (int i= 0; i < 1000000000; i++) {
if (s.equals("")) {
s = "";
}
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Type 1 took this long: " + elapsedTime + " ns");
startTime = System.nanoTime();
s = "";
for (int i= 0; i < 1000000000; i++) {
s = (s.equals("") ? "" : s);
}
elapsedTime = System.nanoTime() - startTime;
System.out.println("Type 2 took this long: " + elapsedTime + " ns");
}
هذه هي نتيجتي:
استغرق النوع 1 وقتًا طويلاً:3293937157 ن.س
استغرق النوع 2 هذا الوقت الطويل:2856769127 ن.س
أفعل شيئا خطأ هنا؟
على افتراض s.equals("")
هذا صحيح بالضرورة، هل هذه طريقة فعالة لجعل التعليمات البرمجية الخاصة بك أسرع؟
المحلول
تحتوي الإجابات الأخرى على معلومات مفيدة ذات صلة ولكن لا يتناول أي منها المشكلة حقيقي سؤال ما إذا كان النموذج الأول أكثر كفاءة من النموذج الثاني.
لا يوفر هذا القياس نتائج موثوقة لأنه لم يتم إجراؤه بشكل صحيح:إحدى "القواعد الأساسية" المهمة في قياس كود Java هي توفير عملية إحماء.في هذه الحالة، توفر الحلقة الأولى عملية إحماء للحلقة الثانية.
هذه الإجابة يوفر تعليمات إضافية لقياس الأداء الدقيق بالإضافة إلى بعض الروابط المفيدة.
نصائح أخرى
, is this a viable way to make your code faster?
يمكنك حتى أن تجعل الأمر أسرع إذا كان لديك String s;
هو حقل غير ثابت.الحقل الثابت أبطأ من الحقل غير الثابت عندما تكون كذلك referencing
ذلك مليار مرة
public static void main(String[] args) {
startTime = System.nanoTime();
String s = "";
.
.
}
يحرر:
لماذا هو أسرع؟؟
ويرجع ذلك إلى إشارة السلسلة إلى الحقل الثابت.
يمكنك رؤيته في رمز البايت الخاص به
0: ldc #23 // String
2: putstatic #25 // Field s:Ljava/lang/String;
5: iconst_0
6: istore_1
7: goto 22
10: getstatic #25 // Field s:Ljava/lang/String;
13: ldc #23 // String
15: invokevirtual #27 // Method java/lang/String.equals:(L
java/lang/Object;)Z
18: pop
19: iinc 1, 1
22: iload_1
23: ldc #33 // int 1000000000
25: if_icmplt 10
28: return
كما ترون getStatic
و putStatic
سيتم استدعاؤه مليار مرة، ما يفعله هو أنه سيتم استدعاء مرجع الحقل الثابت ووضع مرجع السلسلة باستخدام putStatic
com.getStatic - الحصول على قيمة حقل ثابتة للفئة، حيث يتم تحديد الحقل من خلال مرجع الحقل في فهرس التجمع الثابت (الفهرس1 << 8 + الفهرس2)
putStatic - تعيين حقل ثابت إلى قيمة في فئة، حيث يتم تحديد الحقل من خلال فهرس مرجعي للحقل في تجمع ثابت (indexbyte1 << 8 + Indexbyte2)
انظر تلك تحول قليلا وهذا هو سبب بطء البرنامج
أيضًا إذا كنت تستخدم أ global/member field
سوف يخلق نفس الشيء bytecode ولكن بدلا من ذلك سوف تستخدمgetfield
و putfield
وهو نفس ثابت getStatic
و putStatic
الآن دعونا نرى non static field
bytecode
0: ldc #21 // String
2: astore_1
3: iconst_0
4: istore_2
5: goto 23
8: aload_1
9: ldc #21 // String
11: invokevirtual #23 // Method java/lang/String.equals:(L
java/lang/Object;)Z
14: ifeq 20
17: ldc #21 // String
19: astore_1
20: iinc 2, 1
23: iload_2
24: ldc #29 // int 1000000000
26: if_icmplt 8
29: return
كما ترون فإنه يستخدم فقط astore_1
و aload_1
لحفظ وتحميل مرجع الحقل غير الثابت دون عملية إضافية.
هذه الرائحة تشبه التحسين المبكر بالنسبة لي.إذا كنت لا تزال تنوي إجراء اختبار دقيق لكلا التطبيقين بهذه الطريقة، أقترح استخدام isEmpty()
بدلاً من ذلك نظرًا لأن الكود الأساسي لذلك أكثر وضوحًا مقارنة بـ equals()
.أعني بذلك أن أي تحسين سيفعله المترجم/JVM لك سيكون أقل احتمالًا بسبب ما يحدث في equals()
, ، وأكثر انعكاسًا لأي فوائد دقيقة يتمتع بها أحد التطبيقات على الآخر، على افتراض أن ذلك مهم حقًا.
يجب أن تكون سهولة القراءة هي القاعدة الأفضل بالنسبة لك لتقرر ما إذا كنت تريد استخدامها if-else
أو ? :
.