2013-03-27 10 views
5

Ogni volta che dovevo convertire unin un String, ho scelto ""+a o Integer.toString(a). Ora mi chiedevo quale fosse la strada più veloce, così ho scritto un semplice benchmark che chiama function_1, function_2 e function_3 10000000 volte e stampa quanto tempo ci vuole per elaborare le funzioni. Qui ci sono le funzioni:Il modo più veloce di convertire i numeri interi in stringa in java

public static String i=""; 
public static String j=""; 
public static String k=""; 

public static void function_1() 
{ 
    i=Integer.toString(getOne()); 
} 

public static void function_2() 
{ 
    j=""+1; 
} 

public static void function_3() 
{ 
    j=""+getOne(); 
} 

public static int getOne() 
{ 
    return 1; 
} 

l'output è:

Benchmarking starting... 
Executing function_1 10000000 time(s)... 
Done executing function_1 in 476 ms. 
Executing function_2 10000000 time(s)... 
Done executing function_2 in 8 ms. 
Executing function_3 10000000 time(s)... 
Done executing function_3 in 634 ms. 
Benchmarking complete! 

Credo function_2 è così veloce, perché è compilato come

public static void function_2() 
{ 
    j="1"; 
} 

in modo da evitare che, ho usato il funzione invece getOne(). Ma ecco la parte interessante (per me): function_3 deve essere compilato senza utilizzare il metodo originale toString di Object (in questo caso Integer.toString(1) perché int è primitivo). La mia domanda è: in che modo il compilatore minaccia effettivamente ""+1 quindi è più lento quindi chiama Integer.toString(1)?

+12

Hai considerato il bytecode? Inoltre, i micro-benchmark sono, in generale, inutili. Le cose potrebbero anche accadere a livello di JIT. –

+0

'" "+ 1' è il * più veloce * (di molto) in base a quei numeri. E, sembra solo intuitivo che una funzione specializzata sia [leggermente] più veloce di chiamare una funzione ed eseguire una stringa concatenando il risultato. –

+1

Se osservi il bytecode probabilmente troverai 'function_3' usa un' StringBuilder 'mentre' function_1' no. E internamente, 'StringBuilder' chiamerà' String.valueOf() '. – parsifal

risposta

4

"" e 1 sono noti al momento della compilazione. Questo è il motivo per cui in function_2"" + 1 viene realmente sostituito da "1" durante la conversione in bytecode.

getOne() il risultato è sconosciuto al momento della compilazione, quindi la concatenazione verrà eseguita in runtime. MA perché la concatenazione (+) non è efficiente, è probabile che il compilatore lo cambi in implementazione basata su StringBuilder.append().

Non credermi? Prova: javap -c ClassName.class e vedrete qualcosa di simile:

public static void function_2(); 
Code: 
    0: ldc   #39     // String 1 
    2: putstatic  #16     // Field j:Ljava/lang/String; 
    5: return   


public static void function_3(); 
Code: 
    0: new   #42     // class java/lang/StringBuilder 
    3: dup   
    4: invokespecial #44     // Method java/lang/StringBuilder."<init>":()V 
    7: invokestatic #28     // Method getOne:()I 
    10: invokevirtual #45     // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 
    13: invokevirtual #49     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    16: putstatic  #16     // Field j:Ljava/lang/String; 
    19: return 

function_2() hanno una sola stringa "1", mentre function_3 avere tutte queste chiamate di metodo con l'aggiunta di StringBuilder all'interno :)

Tenete a mente che qualche ottimizzazione può verificarsi a runtime, ma questo comportamento è JVM ed è dipendente dalla configurazione.

+0

Grazie! Finalmente una risposta. E ho indovinato giusto che "" +1 viene compilato su "1". – D180

1

ho verificato le seguenti funzioni 10.000.000 iterazioni:

public static void no_func_maybe_constant() 
{ 
    j= "" + 1; 
} 

public static void no_func_no_constant() 
{ 
    j = ""; 
    j = j + 1; 
} 

public static void yes_func_maybe_constant() 
{ 
    j = "" + getOne(); 
} 

public static void yes_func_no_constant() 
{ 
    j = ""; 
    j = j + getOne(); 
} 

miei risultati:

no_func_maybe_constant Took 0.028058674s 
no_func_no_constant Took 1.449465242s 
yes_func_maybe_constant Took 1.275561897s 
yes_func_no_constant Took 1.263362257s 

La differenza tra il non chiamare la funzione e chiamare la funzione era in effetti trascurabili, così sembra nel caso di "" + 1 stava effettivamente facendo un calcolo della costante in fase di compilazione. Interessante che senza una funzione a volte richiedeva meno tempo ...

+0

quindi chiamare una funzione richiede così tanto tempo? – D180

+0

@ D180: Sì, ci vuole molto più tempo per chiamare una funzione piuttosto che semplicemente avere una costante già lì. – Claudiu

+0

@Claudiu perché il compilatore in-line 'function_3'? – Tushar

0

La differenza tra 2 e 3 è probabilmente dovuta alla necessità di chiamare un metodo per ottenere l'intero. Quando si chiama un metodo, viene creato un nuovo record di attivazione che complica lo stack delle chiamate, quindi è possibile che si verifichi più operazioni a meno che JIT della JVM non sia in grado di incorporare quella chiamata di funzione statica al solo valore restituito (quasi certamente non accade qui).

+0

Mi aspetto che il JITC effettui la chiamata in linea. Molto probabilmente il JITC non viene attivato per qualche motivo (il che rende falso l'intero esercizio, dal momento che l'interprete in testa inonda tutto il resto). –

Problemi correlati