Yanıtlar:
Örnekte ben presume str
edilir değil dışında kullanılan while
içine bildirerek, çünkü aksi takdirde soruyu soran olmaz, döngü while
döngü bir seçenek olmazdı derlenemeyecektir.Ancak olmaz çünkü.
Yani, çünkü str
edilir değil için mümkün olan en küçük kapsamı, döngünün dışında kullanılamaz str
olduğu içinde iken döngü.
Yani, cevap kesinlikle o str
kesinlikle süre döngü içinde ilan edilmesi gerektiğini. Hayır ifs, hayır ands, hayır buts.
Bu kuralın ihlal edilebileceği tek durum, herhangi bir nedenle, her saat döngüsünün koddan sıkılması gerektiğinde hayati önem taşıyorsa, bu durumda dış bir kapsamda bir şey başlatmayı ve yerine yeniden kullanmayı düşünebilirsiniz. bir iç kapsamın her yinelemesinde yeniden somutlaştırılır. Bununla birlikte, bu, java'daki dizelerin değişmezliği nedeniyle örneğiniz için geçerli değildir: döngünüzün başında her zaman yeni bir str örneği oluşturulur ve bunun sonunda atılır, bu nedenle orada orada optimizasyon imkanı yoktur.
EDIT: (cevabımda aşağıdaki yorumumu enjekte ediyor)
Her durumda, bir şeyler yapmanın doğru yolu, tüm kodunuzu doğru bir şekilde yazmak, ürününüz için bir performans gereksinimi oluşturmak, nihai ürününüzü bu gereksinime göre ölçmektir ve bunu karşılamıyorsa, işleri optimize edin. Ve genellikle ortaya çıkan şey, programımızın tüm kod tabanınızın her yerine geçmek ve bazı şeyleri değiştirmek ve kesmek yerine programın performans gereksinimlerini karşılamasını sağlayan birkaç yerde bazı güzel ve resmi algoritmik optimizasyonlar sağlamanın yollarını bulmanızdır. Burada ve orada saat döngülerini sıkmak için.
Bu iki (benzer) örneğin bayt kodunu karşılaştırdım:
Şimdi 1. örneğe bakalım :
package inside;
public class Test {
public static void main(String[] args) {
while(true){
String str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
sonra javac Test.java
, javap -c Test
alacaksınız:
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
}
Şuna bakalım 2. örneğe bakalım :
package outside;
public class Test {
public static void main(String[] args) {
String str;
while(true){
str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
sonra javac Test.java
, javap -c Test
alacaksınız:
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
}
Gözlemler, bu iki örnek arasında hiçbir fark olmadığını göstermektedir . JVM spesifikasyonlarının bir sonucu ...
Ancak en iyi kodlama uygulaması adına, değişkenin mümkün olan en küçük kapsamda bildirilmesi önerilir (bu örnekte, değişkenin kullanıldığı tek yer olduğundan, döngü içinde yer alır).
final
severler: bildirerek str
olarak final
içinde inside
paket durumda da hiç fark etmez =)
En küçük kapsamdaki nesnelerin bildirilmesi okunabilirliği artırır .
Performans bugünün derleyicileri için önemli değil. (Bu senaryoda)
Bakım açısından 2. seçenek daha iyidir.
Değişkenleri aynı yerde, mümkün olan en dar kapsamda tanımlayın ve başlatın.
As Donald Ervin Knuth anlattı:
"Zamanın yaklaşık% 97'sini küçük verimlilikleri unutmalıyız: erken optimizasyon tüm kötülüklerin köküdür"
ie) bir programcının performans değerlendirmelerinin bir kod parçasının tasarımını etkilemesine izin verdiği durum . Bu bir tasarım neden olabilir gibi temiz değil olabilirdi olarak veya kod, çünkü yanlıştır kod komplike tarafından optimizasyon ve programcı tarafından dikkati dağılır optimize .
Lütfen güncellenmiş cevaba atlayın ...
Performansı önemseyenler için System.out'u çıkarın ve döngüyü 1 bayt ile sınırlayın. Double (test 1/2) ve String (3/4) kullanarak geçen süreler milisaniye cinsinden aşağıda Windows 7 Professional 64 bit ve JDK-1.7.0_21 ile verilmiştir. Bayt kodları (aşağıda test1 ve test2 için de verilmiştir) aynı değildir. Değişken ve nispeten karmaşık nesnelerle test etmek için çok tembeltim.
çift
Test1 aldı: 2710 ms
Test2 aldı: 2790 msn
String (testlerde double ile string'i değiştirin)
Test3 aldı: 1200 msn
Test4 aldı: 3000 milisaniye
Derleme ve bayt kodu alma
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
}
Performansı tüm JVM optimizasyonlarıyla karşılaştırmak gerçekten kolay değil. Ancak, bu biraz mümkündür. Google Kaliper'de daha iyi test ve ayrıntılı sonuçlar
Bu, yukarıdaki kodla aynı değildir. Sadece kukla bir döngü kodlarsanız JVM atlar, bu yüzden en azından bir şey atamanız ve iade etmeniz gerekir. Bu, Kaliper belgelerinde de önerilir.
@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;
}
Özet: Dekore edilmiş önce daha iyi performans gösterir - gerçekten küçük - ve en küçük kapsam ilkesine aykırıdır. JVM aslında bunu sizin için yapmalı
Bu sorunun bir çözümü while döngüsünü kapsayan değişken bir kapsam sağlamak olabilir:
{
// all tmp loop variables here ....
// ....
String str;
while(condition){
str = calculateStr();
.....
}
}
Dış kapsam sona erdiğinde otomatik olarak referans kaldırılır.
str
While-after döngüsünü (kapsamla ilgili) kullanmanız gerekmiyorsa , ikinci koşul yani
while(condition){
String str = calculateStr();
.....
}
yığındaki bir nesneyi yalnızca condition
doğru olduğunda tanımlarsanız daha iyidir . Yani ihtiyacın olursa kullan
Sorunuzu cevaplamak için en iyi kaynak aşağıdaki yazı olacağını düşünüyorum:
Döngüden önce veya döngü içinde değişkenleri bildirme arasındaki fark nedir?
Anladığım kadarıyla bu şey dile bağlı olacaktır. IIRC Java bunu optimize eder, bu yüzden herhangi bir fark yoktur, ancak JavaScript (örneğin) döngüdeki her seferinde tüm bellek tahsisini yapacaktır.Java'da özellikle profilleme yapıldığında ikincisinin daha hızlı çalışacağını düşünüyorum.
Birçok insanın işaret ettiği gibi,
String str;
while(condition){
str = calculateStr();
.....
}
bundan daha iyi DEĞİLDİR :
while(condition){
String str = calculateStr();
.....
}
Bu nedenle, yeniden kullanmıyorsanız değişkenleri kapsamlarının dışında bildirmeyin ...
Wile döngüsünün dışında String str bildirimi, while döngüsünün içinde ve dışında referans gösterilmesini sağlar. While döngüsünün içindeki String str bildirimi, yalnızca while döngüsünün içinde referans gösterilmesini sağlar .
Değişkenler mümkün olduğunca kullanıldıkları yere yakın olarak bildirilmelidir.
RAII'yi (Kaynak Edinimi Başlatma) kolaylaştırır.
Değişkenin kapsamını sıkı tutar. Bu, optimize edicinin daha iyi çalışmasını sağlar.
Google Android Geliştirme kılavuzuna göre, değişken kapsam sınırlandırılmalıdır. Lütfen bu bağlantıyı kontrol edin:
str
Değişken kullanılabilir ve hatta kodunun altına infaz ederken sonra hafızada biraz boşluk saklıdır olacaktır.
String str;
while(condition){
str = calculateStr();
.....
}
str
Değişken kullanılamaz ve ayrıca hafıza için tahsis edildiği çıkacak str
kod aşağıda değişken.
while(condition){
String str = calculateStr();
.....
}
İkincisini takip edersek, bu sistem belleğimizi azaltacak ve performansı artıracaktır.
Gerçekten, yukarıda belirtilen soru bir programlama sorunudur. Kodunuzu nasıl programlamak istersiniz? 'STR' e nereden erişmeniz gerekiyor? Yerel olarak global değişken olarak kullanılan bir değişkeni bildirmenin bir faydası yoktur. Programlamanın temelleri inanıyorum.
Bu sorudaki hemen hemen herkes için uyarı: Burada, döngü içinde Java 7 ile bilgisayarımda 200 kat daha yavaş olabileceği örnek kod var (ve bellek tüketimi de biraz farklıdır). Ama bu sadece kapsamla değil, tahsisle de ilgilidir.
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);
}
}
Sonuç: Yerel değişkenin büyüklüğüne bağlı olarak, fark çok büyük değişkenlerde olmasa bile çok büyük olabilir.
Sadece bazen, döngünün dışında veya içinde önemli olduğunu söylemek gerekir.
bigStuff[(int) (value % STUFF_SIZE)] = value;
(2147483649L değerini deneyin)
Sen riski var NullPointerException
senin eğer calculateStr()
metot bize boş ve sonra Dairenin bir yöntemi çağırmak deneyin.
Daha genel olarak, boş değerli değişkenlere sahip olmaktan kaçının . Bu arada sınıf nitelikleri için daha güçlü.
NullPointerException.
kodu vardır. Bu kod denendiğinde return str;
derleme hatasıyla karşılaşır.