जवाबों:
आपके उदाहरण में, मुझे लगता str
है कि लूप के बाहर उपयोग नहीं किया जाता है while
, अन्यथा आप सवाल नहीं पूछेंगे, क्योंकि इसे while
लूप के अंदर घोषित करना एक विकल्प नहीं होगा, क्योंकि यह संकलन नहीं करेगा।
तो, के बाद से str
किया गया है नहीं पाश बाहर किया, के लिए छोटी संभव गुंजाइश str
है भीतर , जबकि पाश।
तो, इसका उत्तर सशक्त रूप से है कि str
पूरी तरह से लूप के भीतर घोषित किया जाना चाहिए। अगर नहीं, नहीं, और नहीं, लेकिन नहीं।
एकमात्र मामला जहां इस नियम का उल्लंघन किया जा सकता है, अगर किसी कारण से यह महत्वपूर्ण है कि हर घड़ी चक्र को कोड से बाहर निचोड़ना होगा, तो उस स्थिति में आप किसी बाहरी दायरे में किसी चीज को तत्काल करना और उसके बजाय उसका पुन: उपयोग करने पर विचार करना चाह सकते हैं। एक आंतरिक दायरे के हर पुनरावृत्ति पर इसे फिर से तत्काल करना। हालांकि, यह आपके उदाहरण पर लागू नहीं होता है, जावा में स्ट्रिंग्स की अपरिवर्तनीयता के कारण: स्ट्रै का एक नया उदाहरण हमेशा आपके लूप की शुरुआत में बनाया जाएगा और इसे इसके अंत में फेंक दिया जाएगा, इसलिए वहां वहाँ अनुकूलन करने के लिए कोई संभावना नहीं है।
संपादित करें: (उत्तर में मेरी टिप्पणी नीचे दर्ज कर रहा है)
किसी भी मामले में, चीजों को करने का सही तरीका यह है कि आप अपने सभी कोड को ठीक से लिखें, अपने उत्पाद के लिए एक प्रदर्शन की आवश्यकता स्थापित करें, इस आवश्यकता के खिलाफ अपने अंतिम उत्पाद को मापें, और यदि यह इसे संतुष्ट नहीं करता है, तो चीजों को अनुकूलित करें। और आमतौर पर जो कुछ भी होता है वह यह है कि आप कुछ अच्छे और औपचारिक एल्गोरिथम अनुकूलन के लिए कुछ ऐसे स्थानों को उपलब्ध करवाते हैं, जो हमारे कार्यक्रम को आपके संपूर्ण कोड बेस पर जाने और बदले में चीजों को हैक करने के बजाय हमारे प्रदर्शन की आवश्यकताओं को पूरा करते हैं। यहाँ और वहाँ घड़ी चक्र को निचोड़ने का आदेश।
मैंने उन दो (समान) उदाहरणों के बाइट कोड की तुलना की:
आइए 1. उदाहरण देखें :
package inside;
public class Test {
public static void main(String[] args) {
while(true){
String str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
के बाद javac Test.java
, javap -c Test
आप प्राप्त करेंगे:
public class inside.Test extends java.lang.Object{
public inside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
आइए २ उदाहरण देखें :
package outside;
public class Test {
public static void main(String[] args) {
String str;
while(true){
str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
के बाद javac Test.java
, javap -c Test
आप प्राप्त करेंगे:
public class outside.Test extends java.lang.Object{
public outside.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method java/lang/System.currentTimeMillis:()J
3: invokestatic #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
6: astore_1
7: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
10: aload_1
11: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 0
}
टिप्पणियों से पता चलता है कि उन दो उदाहरणों में कोई अंतर नहीं है । यह JVM विनिर्देशों का परिणाम है ...
लेकिन सर्वश्रेष्ठ कोडिंग अभ्यास के नाम पर चर को सबसे छोटे संभव दायरे में घोषित करने की सिफारिश की जाती है (इस उदाहरण में यह लूप के अंदर है, क्योंकि यह एकमात्र स्थान है जहां चर का उपयोग किया जाता है)।
final
प्रेमियों: घोषित करने str
के रूप में final
में inside
पैकेज मामला भी कोई फर्क नहीं पड़ता =)
छोटे दायरे में वस्तुओं को घोषित करने से पठनीयता में सुधार होता है ।
प्रदर्शन आज के संकलक के लिए मायने नहीं रखता। (इस परिदृश्य में)
रखरखाव के दृष्टिकोण से, दूसरा विकल्प बेहतर है।
एक ही जगह पर वैरिएबल को डिक्लेयर और इनिशियलाइज़ करें, संभवत: सबसे कम दायरे में।
जैसा कि डोनाल्ड एर्विन नुथ ने बताया:
"हमें छोटी दक्षता के बारे में भूलना चाहिए, समय के 97% के बारे में कहना चाहिए: समय से पहले अनुकूलन सभी बुराई की जड़ है"
यानी) ऐसी स्थिति जहां एक प्रोग्रामर प्रदर्शन विचारों को कोड के एक टुकड़े के डिजाइन को प्रभावित करता है । इसके परिणामस्वरूप एक ऐसा डिज़ाइन हो सकता है जो उतना साफ नहीं है जितना कि यह हो सकता है या कोड जो कि गलत है, क्योंकि कोड ऑप्टिमाइज़ेशन से जटिल है और प्रोग्रामर ऑप्टिमाइज़ करके विचलित हो जाता है ।
कृपया अपडेट किए गए उत्तर पर जाएं ...
प्रदर्शन की परवाह करने वालों के लिए System.out को बाहर निकालें और लूप को 1 बाइट तक सीमित करें। डबल (टेस्ट 1/2) और स्ट्रिंग (3/4) का उपयोग करके मिलीसेकंड में बीत चुके समय को विंडोज 7 प्रोफेशनल 64 बिट और जेडीके-1.7.0_21 के साथ नीचे दिया गया है। बाइटकोड (टेस्ट 1 और टेस्ट 2 के लिए भी नीचे दिया गया है) समान नहीं हैं। मैं भी परिवर्तनशील और अपेक्षाकृत जटिल वस्तुओं के साथ परीक्षण करने के लिए बहुत आलसी था।
दोहरा
टेस्ट 1 लिया: 2710 मिसे
टेस्ट 2 ने लिया: 2790 मिसे
स्ट्रिंग (बस परीक्षणों में स्ट्रिंग के साथ डबल बदलें)
टेस्ट 3 लिया: 1200 मिसे
टेस्ट 4 लिया: 3000 मिसे
संकलन और बायटेकोड प्राप्त करना
javac.exe LocalTest1.java
javap.exe -c LocalTest1 > LocalTest1.bc
public class LocalTest1 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
double test;
for (double i = 0; i < 1000000000; i++) {
test = i;
}
long finish = System.currentTimeMillis();
System.out.println("Test1 Took: " + (finish - start) + " msecs");
}
}
public class LocalTest2 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for (double i = 0; i < 1000000000; i++) {
double test = i;
}
long finish = System.currentTimeMillis();
System.out.println("Test1 Took: " + (finish - start) + " msecs");
}
}
Compiled from "LocalTest1.java"
public class LocalTest1 {
public LocalTest1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: dconst_0
5: dstore 5
7: dload 5
9: ldc2_w #3 // double 1.0E9d
12: dcmpg
13: ifge 28
16: dload 5
18: dstore_3
19: dload 5
21: dconst_1
22: dadd
23: dstore 5
25: goto 7
28: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
31: lstore 5
33: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
36: new #6 // class java/lang/StringBuilder
39: dup
40: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
43: ldc #8 // String Test1 Took:
45: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
48: lload 5
50: lload_1
51: lsub
52: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
55: ldc #11 // String msecs
57: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
63: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: return
}
Compiled from "LocalTest2.java"
public class LocalTest2 {
public LocalTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: dconst_0
5: dstore_3
6: dload_3
7: ldc2_w #3 // double 1.0E9d
10: dcmpg
11: ifge 24
14: dload_3
15: dstore 5
17: dload_3
18: dconst_1
19: dadd
20: dstore_3
21: goto 6
24: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
27: lstore_3
28: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
31: new #6 // class java/lang/StringBuilder
34: dup
35: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
38: ldc #8 // String Test1 Took:
40: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: lload_3
44: lload_1
45: lsub
46: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
49: ldc #11 // String msecs
51: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
54: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
57: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
}
सभी JVM अनुकूलन के साथ प्रदर्शन की तुलना करना वास्तव में आसान नहीं है। हालांकि, यह कुछ हद तक संभव है। Google कैलीपर में बेहतर परीक्षण और विस्तृत परिणाम
यह ऊपर दिए गए कोड के समान नहीं है। यदि आप बस एक डमी लूप को कोड करते हैं तो JVM इसे छोड़ देता है, इसलिए आपको कम से कम कुछ असाइन करने और वापस करने की आवश्यकता होती है। कैलिपर प्रलेखन में यह भी सिफारिश की गई है।
@Param int size; // Set automatically by framework, provided in the Main
/**
* Variable is declared inside the loop.
*
* @param reps
* @return
*/
public double timeDeclaredInside(int reps) {
/* Dummy variable needed to workaround smart JVM */
double dummy = 0;
/* Test loop */
for (double i = 0; i <= size; i++) {
/* Declaration and assignment */
double test = i;
/* Dummy assignment to fake JVM */
if(i == size) {
dummy = test;
}
}
return dummy;
}
/**
* Variable is declared before the loop.
*
* @param reps
* @return
*/
public double timeDeclaredBefore(int reps) {
/* Dummy variable needed to workaround smart JVM */
double dummy = 0;
/* Actual test variable */
double test = 0;
/* Test loop */
for (double i = 0; i <= size; i++) {
/* Assignment */
test = i;
/* Not actually needed here, but we need consistent performance results */
if(i == size) {
dummy = test;
}
}
return dummy;
}
सारांश: घोषित किया गया बेहतर प्रदर्शन -सामान्य रूप से छोटे को इंगित करता है- और यह सबसे छोटे दायरे के सिद्धांत के खिलाफ है। जेवीएम को वास्तव में आपके लिए यह करना चाहिए
इस समस्या का एक समाधान यह हो सकता है कि लूप को घेरते हुए एक चर गुंजाइश प्रदान की जाए:
{
// all tmp loop variables here ....
// ....
String str;
while(condition){
str = calculateStr();
.....
}
}
बाहरी दायरे के समाप्त होने पर वे स्वचालित रूप से डी-संदर्भ होंगे।
यदि आपको str
लूप (गुंजाइश से संबंधित) के बाद उपयोग करने की आवश्यकता नहीं है तो दूसरी स्थिति अर्थात
while(condition){
String str = calculateStr();
.....
}
तब से बेहतर है जब आप स्टैक पर किसी ऑब्जेक्ट को केवल तभी परिभाषित करें जब condition
वह सत्य हो। यानी अगर आपको जरूरत हो तो इसका इस्तेमाल करें
मुझे लगता है कि आपके प्रश्न का उत्तर देने के लिए सबसे अच्छा संसाधन निम्नलिखित पोस्ट होगा:
पहले या पाश में घोषित चर के बीच अंतर?
मेरी समझ के अनुसार यह बात भाषा पर निर्भर होगी। IIRC जावा इसे अनुकूलित करता है, इसलिए इसमें कोई अंतर नहीं है, लेकिन जावास्क्रिप्ट (उदाहरण के लिए) लूप में हर बार पूरी मेमोरी आवंटन करेगा। विशेष रूप से जावा में मुझे लगता है कि प्रोफाइलिंग करते समय दूसरा तेजी से चलेगा।
जैसा कि कई लोगों ने बताया है,
String str;
while(condition){
str = calculateStr();
.....
}
इससे बेहतर नहीं है:
while(condition){
String str = calculateStr();
.....
}
यदि आप इसे पुन: उपयोग नहीं कर रहे हैं, तो उनके स्कोप के बाहर चर घोषित न करें ...
चर को जहां संभव हो वहां उपयोग किया जाना चाहिए।
यह RAII (संसाधन अधिग्रहण प्रारंभिक है) को आसान बनाता है।
यह वेरिएबल के दायरे को चुस्त-दुरुस्त रखता है। इससे ऑप्टिमाइज़र बेहतर काम करता है।
Google Android विकास मार्गदर्शिका के अनुसार, चर दायरा सीमित होना चाहिए। कृपया इस लिंक की जाँच करें:
str
चर उपलब्ध हो सकता है और जाने के बाद भी, जबकि नीचे दिए गए कोड को मार डाला स्मृति में कुछ जगह सुरक्षित होगा।
String str;
while(condition){
str = calculateStr();
.....
}
str
चर उपलब्ध नहीं होगा और यह भी स्मृति में जारी किया जाएगा, जिसके लिए आवंटित किया गया था str
नीचे दिए गए कोड में चर।
while(condition){
String str = calculateStr();
.....
}
यदि हम दूसरे का अनुसरण करते हैं तो निश्चित रूप से यह हमारे सिस्टम मेमोरी को कम करेगा और प्रदर्शन बढ़ाएगा।
सचमुच, ऊपर कहा गया प्रश्न एक प्रोग्रामिंग समस्या है। आप अपने कोड को कैसे प्रोग्राम करना चाहेंगे? आपको एक्सेस करने के लिए 'STR' की आवश्यकता कहाँ है? एक चर घोषित करने का कोई फायदा नहीं है जो स्थानीय रूप से वैश्विक चर के रूप में उपयोग किया जाता है। प्रोग्रामिंग की मूल बातें मुझे विश्वास है।
इस प्रश्न में लगभग सभी के लिए चेतावनी: यहां नमूना कोड है जहां लूप के अंदर जावा 7 के साथ मेरे कंप्यूटर पर यह आसानी से 200 गुना धीमा हो सकता है (और मेमोरी की खपत भी थोड़ी अलग है)। लेकिन यह आवंटन के बारे में है और न केवल गुंजाइश है।
public class Test
{
private final static int STUFF_SIZE = 512;
private final static long LOOP = 10000000l;
private static class Foo
{
private long[] bigStuff = new long[STUFF_SIZE];
public Foo(long value)
{
setValue(value);
}
public void setValue(long value)
{
// Putting value in a random place.
bigStuff[(int) (value % STUFF_SIZE)] = value;
}
public long getValue()
{
// Retrieving whatever value.
return bigStuff[STUFF_SIZE / 2];
}
}
public static long test1()
{
long total = 0;
for (long i = 0; i < LOOP; i++)
{
Foo foo = new Foo(i);
total += foo.getValue();
}
return total;
}
public static long test2()
{
long total = 0;
Foo foo = new Foo(0);
for (long i = 0; i < LOOP; i++)
{
foo.setValue(i);
total += foo.getValue();
}
return total;
}
public static void main(String[] args)
{
long start;
start = System.currentTimeMillis();
test1();
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
test2();
System.out.println(System.currentTimeMillis() - start);
}
}
निष्कर्ष: स्थानीय चर के आकार के आधार पर, अंतर बहुत बड़ा हो सकता है, यहां तक कि बड़े चर भी नहीं।
बस यह कहने के लिए कि कभी-कभी, लूप के बाहर या अंदर क्या होता है।
bigStuff[(int) (value % STUFF_SIZE)] = value;
(2147483649L का मान ट्राई करें)
NullPointerException
यदि आपकी calculateStr()
विधि शून्य हो जाती है, तो आपको जोखिम होता है और फिर आप str पर एक विधि को कॉल करने का प्रयास करते हैं।
अधिक सामान्यतः, एक शून्य मान वाले चर होने से बचें । यह वर्ग विशेषताओं के लिए मजबूत है, वैसे।
NullPointerException.
यदि इस कोड का प्रयास किया जाता है, तो शून्य जोखिम return str;
होता है, जिससे संकलन त्रुटि हो सकती है।