java - parse - string to int c´++
Concatenar char literal(''x'') vs single char cadena literal("x") (3)
Además de perfilar esto, tenemos otra posibilidad de obtener algunas ideas. Quiero centrarme en las posibles diferencias de velocidad y no en las cosas que las eliminan de nuevo.
Así que comencemos con esta clase de Test
:
public class Test {
// Do not optimize this
public static volatile String A = "A String";
public static void main( String [] args ) throws Exception {
String a1 = A + "B";
String a2 = A + ''B'';
a1.equals( a2 );
}
}
Compilé esto con javac Test.java (usando javac -v: javac 1.7.0_55)
Usando javap -c Test.class obtenemos:
Compiled from "Test.java"
public class Test {
public static volatile java.lang.String A;
public Test();
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: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: getstatic #4 // Field A:Ljava/lang/String;
10: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
13: ldc #6 // String B
15: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
21: astore_1
22: new #2 // class java/lang/StringBuilder
25: dup
26: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
29: getstatic #4 // Field A:Ljava/lang/String;
32: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: bipush 66
37: invokevirtual #8 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
40: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
43: astore_2
44: aload_1
45: aload_2
46: invokevirtual #9 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
49: pop
50: return
static {};
Code:
0: ldc #10 // String A String
2: putstatic #4 // Field A:Ljava/lang/String;
5: return
}
Podemos ver que hay dos StringBuilders involucrados (líneas 4, 22). Entonces, lo primero que descubrimos es que usar +
para concat Strings
es efectivamente lo mismo que usar StringBuilder.
Lo segundo que podemos ver aquí es que los StringBuilders son llamados dos veces. Primero para agregar la variable volátil (líneas 10, 32) y la segunda para agregar la parte constante (líneas 15, 37)
En el caso de A + "B"
se llama append
con un Ljava/lang/String
(a String) mientras que en el caso de A + ''B''
se llama con un argumento C
(a char).
Entonces, la compilación no convierte String en char, pero lo deja como está *.
Ahora mirando en AbstractStringBuilder
que contiene los métodos utilizados tenemos:
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
y
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
como los métodos realmente llaman.
Las operaciones más caras aquí sin duda son ensureCapacity
pero solo en caso de que se alcance el límite (hace una copia del viejo StringBuffers char [] en una nueva). Entonces esto es cierto para ambos y no hace una diferencia real.
Como se puede ver, hay muchas otras operaciones que se realizan, pero la distinción real es entre el value[count++] = c;
y str.getChars(0, len, value, count);
Si nos fijamos en getChars, vemos que todo se reduce a un System.arrayCopy
que se utiliza aquí para copiar el String a la matriz del Buffer más algunas comprobaciones y llamadas a métodos adicionales frente a un solo acceso a la matriz.
Entonces, en teoría, usar A + "B"
es mucho más lento que usar A + ''B''
.
Creo que en la ejecución real también es más lento. Pero para determinar esto necesitamos comparar.
EDITAR: Porque todo esto es antes de que el JIT haga magia. Ver la respuesta de Stephen C para eso.
EDIT2: He estado mirando el bytecode que generó el compilador de eclipse y es casi idéntico. Entonces, al menos, estos dos compiladores no difieren en el resultado.
EDIT2: Y AHORA LA PARTE DIVERTIDA
Los puntos de referencia. Este resultado se genera ejecutando Loops 0..100M para a+''B''
y a+"B"
algunas veces después de un calentamiento:
a+"B": 5096 ms
a+''B'': 4569 ms
a+''B'': 4384 ms
a+"B": 5502 ms
a+"B": 5395 ms
a+''B'': 4833 ms
a+''B'': 4601 ms
a+"B": 5090 ms
a+"B": 4766 ms
a+''B'': 4362 ms
a+''B'': 4249 ms
a+"B": 5142 ms
a+"B": 5022 ms
a+''B'': 4643 ms
a+''B'': 5222 ms
a+"B": 5322 ms
promedio para:
a+''B'': 4608ms
a+"B": 5167ms
Entonces, incluso en el mundo real de referencia del conocimiento sintético (jeje) a+''B''
es aproximadamente 10% más rápido que a+"B"
...
... al menos (descargo de responsabilidad) en mi sistema con mi compilador y mi CPU y realmente no hay diferencia / no se nota en los programas del mundo real . Excepto por causa de que tiene un código, se ejecuta muy a menudo y todo el rendimiento de la aplicación depende de eso. Pero, para empezar, probablemente harías las cosas de manera diferente.
EDIT4:
Pensar en ello. Este es el ciclo utilizado para comparar:
start = System.currentTimeMillis();
for( int i=0; i<RUNS; i++ ){
a1 = a + ''B'';
}
end = System.currentTimeMillis();
System.out.println( "a+''B'': " + (end-start) + " ms" );
por lo tanto, no solo estamos evaluando la única cosa que nos importa, sino también el rendimiento del bucle java, el rendimiento de la creación de objetos y la asignación al rendimiento de las variables. Entonces la diferencia de velocidad real puede ser incluso un poco más grande.
Cuando tengo un String que necesito para concatenar un solo carácter hasta su final, ¿debería preferir s = .... + '']''
sobre s = .... + "]"
por alguna razón de rendimiento?
Sé que se unen cadenas de matrices y de creadores de cadenas, y NO estoy pidiendo sugerencias sobre cómo concatenar cadenas en general.
También sé que algunos de ellos tendrían la necesidad de explicarme sobre las optimizaciones prematuras y que, en general, no debería molestarme con cosas tan menores, por favor no ...
Lo estoy preguntando porque de preferencia de estilo de codificación preferiría usar el último, pero me parece que el primero debería funcionar marginalmente mejor, porque sabiendo que lo que se adjunta es un solo carácter, no hay necesidad de ningún bucle interno. repasando este único carácter como podría haber al copiar una sola cadena de caracteres.
Actualizar
Como @Scheintod escribió, esta es una Q teórica y tiene que ver más con mi deseo de comprender mejor cómo funciona Java y menos con cualquier escenario de la vida real que permita "salvar otro microsegundo" ... tal vez debería haberlo dicho con mayor claridad.
Me gusta entender la forma en que funcionan las cosas "detrás de escena" Y me parece que a veces puede ayudarme a crear un mejor código ...
La verdad, no estaba pensando en optimizaciones de compiladores en absoluto ...
No esperaba que el JIT utilizara StringBuilder
lugar de String
s para mí ... Porque yo (posiblemente erróneamente) pensaba en los constructores de cadenas como "más pesados" que Strings por una parte, pero más rápido en la creación y modificación de las cadenas en la otra mano Así que supondría que en algunos casos usar StringBuilder
s sería aún menos eficiente que usar picaduras ... (si ese no fuera el caso, entonces toda la clase String debería haber tenido su implementación cambiada para ser como la de un StringBuilder
y usar alguna representación interna de cadenas inmutables reales ... - ¿o es eso lo que está haciendo el JIT? - asumiendo que para el caso general sería mejor no dejar que el desarrollador elija ...)
Si cambia mi código a tal grado, entonces quizás mi Q debería haber estado en ese nivel preguntando si es apropiado que el JIT haga algo como esto y sería mejor si lo usara.
tal vez debería empezar a buscar código compilado de bytes ... [Tendré que aprender a hacerlo en Java ...]
Como nota al margen y ejemplo de por qué incluso consideraría mirar bytecode, eche un vistazo a una vieja publicación de blog sobre Optimizing Actionscript 2.0 - una perspectiva de bytecode - Parte I muestra que saber en qué compila el código de hecho puede ayudar escribes mejor código
Cuando tengo un String que necesito para concatenar un solo carácter hasta su final, ¿debería preferir s = .... + '']'' sobre s = .... + "]" por alguna razón de rendimiento?
En realidad, hay dos preguntas aquí:
Q1: ¿hay una diferencia de rendimiento?
Respuesta: Depende ...
En algunos casos, posiblemente sí, dependiendo de la JVM y / o del compilador de códigos de bytes. Si el compilador de códigos de bytes genera una llamada a
StringBuilder.append(char)
lugar deStringBuilder.append(String)
entonces esperaría que el primero sea más rápido. Pero el compilador JIT podría tratar estos métodos como "intrínsecos" y optimizar las llamadas paraappend(String)
con unaappend(String)
un carácter (literal).En resumen, necesitaría comparar esto en su plataforma para estar seguro.
En otros casos, definitivamente no hay diferencia. Por ejemplo, estas dos llamadas se compilarán secuencias de código de bytes idénticas porque la concatenación es una expresión constante .
System.out.println("234" + "]"); System.out.println("234" + '']'');
Esto está garantizado por el JLS.
P2: ¿Debería preferir una versión sobre la otra?
Responder:
En el sentido general, es probable que sea una optimización prematura. Solo debe preferir una forma a otra por motivos de rendimiento si ha perfilado su código en el nivel de aplicación y ha determinado que el fragmento de código tiene un impacto medible en el rendimiento.
Si ha perfilado el código, utilice la respuesta a la Q1 como guía.
Y si valió la pena tratar de optimizar el fragmento, es esencial que vuelva a ejecutar su evaluación comparativa / creación de perfiles después de la optimización, para ver si hizo alguna diferencia. Su intuición sobre lo que es más rápido ... y lo que ha leído en algún artículo antiguo en Internet ... podría estar muy mal.
¡No! Esa es una optimización prematura y una completa pérdida de tiempo. Prefiere lo que prefieras y utiliza lo que funcione. Algunas veces '']'' no se promocionará a "]" automáticamente. Espero que el JIT optimice cualquier diferencia de rendimiento que piense que mida. Y en los casos donde funciona, funciona porque '']'' se convirtió a "]" (en su ejemplo). Ahora, usar un StringBuilder
directamente y append(char)
puede ser un poco más rápido ... pero de nuevo, esta diferencia no será significativa en ninguna aplicación real.
Editar
El mejor consejo que puedo darte sobre el rendimiento es:
- Benchmark antes de comenzar a hacer cambios en el código (y tratar de limitarse a las áreas donde es probable que tenga un impacto a través de la identificación real de la ruta crítica) y
- Si puede, transmita un resultado en lugar de crear un String temporal siempre que sea posible (esto a veces rendirá enormes dividendos con respecto al uso y el rendimiento de la memoria).