tipos - En java, ¿es más eficiente usar byte o short en lugar de int y float en lugar de double?
tipos de variables en java netbeans (5)
Me he dado cuenta de que siempre he usado int y double, sin importar cuán pequeño o grande deba ser el número. Entonces, en Java, ¿es más eficiente usar byte
o short
lugar de int
y float
lugar de double
?
Así que supongamos que tengo un programa con muchos acertijos y dobles. ¿Valdría la pena revisar y cambiar mis datos a bytes o cortos si supiera que el número cabría?
Sé que Java no tiene tipos sin firmar, pero ¿hay algo más que pueda hacer si supiera que el número sería positivo solamente?
Por eficiencia, principalmente me refiero al procesamiento. Asumiría que el recolector de basura sería mucho más rápido si todas las variables fueran de la mitad de tamaño y que los cálculos también serían un poco más rápidos. (Supongo que ya que estoy trabajando en Android necesito preocuparme un poco por el ram también)
(Supongo que el recolector de basura solo trata con Objetos y no es primitivo, pero aún elimina todas las primitivas en objetos abandonados ¿no?)
Lo probé con una pequeña aplicación de Android que tengo, pero realmente no noté ninguna diferencia. (Aunque no medí "científicamente" nada).
¿Me equivoco al asumir que debería ser más rápido y más eficiente? Odiaría ir y cambiar todo en un programa masivo para descubrir que perdí el tiempo.
¿Valdría la pena hacerlo desde el principio cuando empiece un nuevo proyecto? (Es decir, creo que cada poco ayudaría pero, de ser así, ¿por qué no parece que alguien lo hace?)
¿Me equivoco al asumir que debería ser más rápido y más eficiente? Odiaría ir y cambiar todo en un programa masivo para descubrir que perdí el tiempo.
Respuesta corta
Sí, estás equivocado. En la mayoría de los casos, hace poca diferencia en términos de espacio utilizado.
No vale la pena tratar de optimizar esto ... a menos que tenga una clara evidencia de que la optimización es necesaria. Y si necesita optimizar el uso de memoria de los campos de objetos en particular, probablemente necesite tomar otras medidas (más efectivas).
Respuesta más larga
La Máquina Virtual de Java modela pilas y campos de objeto usando compensaciones que son (en efecto) múltiplos de un tamaño de celda primitivo de 32 bits. Por lo tanto, cuando declara una variable local o un campo de objeto como (por ejemplo) un byte
, la variable / campo se almacenará en una celda de 32 bits, al igual que un int
.
Hay dos excepciones a esto:
-
long
valoreslong
ydouble
requieren 2 celdas primitivas de 32 bits - las matrices de tipos primitivos se representan en forma empaquetada, de modo que (por ejemplo) una matriz de bytes contenga 4 bytes por palabra de 32 bits.
Por lo tanto, valdría la pena optimizar el uso de matrices long
y double
... y grandes de primitivas. Pero en general no.
En teoría, un JIT podría ser capaz de optimizar esto, pero en la práctica nunca he oído hablar de un JIT que lo haga. Un impedimento es que el JIT por lo general no puede ejecutarse hasta que se hayan creado instancias de la clase que se está compilando. Si el JIT optimiza el diseño de la memoria, podría tener dos (o más) "sabores" de objetos de la misma clase ... y eso presentaría enormes dificultades.
Revisitación
Si observamos los resultados del índice de referencia en la respuesta de @ meriton, parece que el uso de short
y byte
lugar de int
genera una penalización de rendimiento para la multiplicación. De hecho, si considera las operaciones en forma aislada, la penalización es significativa. (No deberías ... pero eso es otro asunto).
Creo que la explicación es que JIT probablemente está haciendo las multiplicaciones con instrucciones de multiplicar de 32 bits en cada caso. Pero en los casos de byte
y short
, ejecuta instrucciones adicionales para convertir el valor intermedio de 32 bits a byte
o short
en cada iteración de bucle. (En teoría, esa conversión podría hacerse una vez al final del ciclo ... pero dudo que el optimizador sea capaz de resolverlo).
De todos modos, esto apunta a otro problema con el cambio a short
y byte
como una optimización. Podría empeorar el rendimiento ... en un algoritmo que es aritmético y de cálculo intensivo.
¡La diferencia apenas se nota! Es más una cuestión de diseño, adecuación, uniformidad, hábito, etc. A veces es solo una cuestión de gusto. Cuando lo único que te importa es que tu programa se ponga en funcionamiento y sustituir un float
por un int
no dañe la corrección, no veo ninguna ventaja en ir por uno u otro a menos que puedas demostrar que el uso de cualquier tipo altera el rendimiento. Ajustar el rendimiento basado en tipos que son diferentes en 2 o 3 bytes es realmente lo último que debe preocuparse; Donald Knuth dijo una vez: "La optimización prematura es la raíz de todo mal" (no estoy seguro de que sea él, edítala si tienes la respuesta).
Eso depende de la implementación de la JVM, así como del hardware subyacente. La mayoría del hardware moderno no obtendrá bytes individuales de la memoria (o incluso de la memoria caché de primer nivel), es decir, el uso de los tipos primitivos más pequeños generalmente no reduce el consumo de ancho de banda de memoria. Del mismo modo, la CPU moderna tiene un tamaño de palabra de 64 bits. Pueden realizar operaciones en menos bits, pero eso funciona descartando los bits adicionales, que tampoco es más rápido.
El único beneficio es que los tipos primitivos más pequeños pueden dar como resultado un diseño de memoria más compacto, sobre todo cuando se utilizan matrices. Esto ahorra memoria, lo que puede mejorar la localización de la referencia (reduciendo así la cantidad de errores de caché) y reducir la sobrecarga de recolección de basura.
En términos generales, sin embargo, el uso de los tipos primitivos más pequeños no es más rápido.
Para demostrar eso, he aquí el siguiente punto de referencia:
package tools.bench;
import java.math.BigDecimal;
public abstract class Benchmark {
final String name;
public Benchmark(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 100000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + "/t" + time() + " ns";
}
public static void main(String[] args) throws Exception {
Benchmark[] benchmarks = {
new Benchmark("int multiplication") {
@Override int run(int iterations) throws Throwable {
int x = 1;
for (int i = 0; i < iterations; i++) {
x *= 3;
}
return x;
}
},
new Benchmark("short multiplication") {
@Override int run(int iterations) throws Throwable {
short x = 0;
for (int i = 0; i < iterations; i++) {
x *= 3;
}
return x;
}
},
new Benchmark("byte multiplication") {
@Override int run(int iterations) throws Throwable {
byte x = 0;
for (int i = 0; i < iterations; i++) {
x *= 3;
}
return x;
}
},
new Benchmark("int[] traversal") {
@Override int run(int iterations) throws Throwable {
int[] x = new int[iterations];
for (int i = 0; i < iterations; i++) {
x[i] = i;
}
return x[x[0]];
}
},
new Benchmark("short[] traversal") {
@Override int run(int iterations) throws Throwable {
short[] x = new short[iterations];
for (int i = 0; i < iterations; i++) {
x[i] = (short) i;
}
return x[x[0]];
}
},
new Benchmark("byte[] traversal") {
@Override int run(int iterations) throws Throwable {
byte[] x = new byte[iterations];
for (int i = 0; i < iterations; i++) {
x[i] = (byte) i;
}
return x[x[0]];
}
},
};
for (Benchmark bm : benchmarks) {
System.out.println(bm);
}
}
}
que se imprime en mi cuaderno algo viejo:
int multiplication 1.530 ns
short multiplication 2.105 ns
byte multiplication 2.483 ns
int[] traversal 5.347 ns
short[] traversal 4.760 ns
byte[] traversal 2.064 ns
Como puede ver, las diferencias de rendimiento son bastante menores. La optimización de los algoritmos es mucho más importante que la elección del tipo primitivo.
Usar bytes en lugar de int puede aumentar el rendimiento si los usa en gran cantidad. Aquí hay un experimento:
import java.lang.management.*;
public class SpeedTest {
/** Get CPU time in nanoseconds. */
public static long getCpuTime() {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
return bean.isCurrentThreadCpuTimeSupported() ? bean
.getCurrentThreadCpuTime() : 0L;
}
public static void main(String[] args) {
long durationTotal = 0;
int numberOfTests=0;
for (int j = 1; j < 51; j++) {
long beforeTask = getCpuTime();
// MEASURES THIS AREA------------------------------------------
long x = 20000000;// 20 millions
for (long i = 0; i < x; i++) {
TestClass s = new TestClass();
}
// MEASURES THIS AREA------------------------------------------
long duration = getCpuTime() - beforeTask;
System.out.println("TEST " + j + ": duration = " + duration + "ns = "
+ (int) duration / 1000000);
durationTotal += duration;
numberOfTests++;
}
double average = durationTotal/numberOfTests;
System.out.println("-----------------------------------");
System.out.println("Average Duration = " + average + " ns = "
+ (int)average / 1000000 +" ms (Approximately)");
}
}
Esta clase prueba la velocidad de creación de una nueva TestClass. Cada prueba lo hace 20 millones de veces y hay 50 pruebas.
Aquí está la TestClass:
public class TestClass {
int a1= 5;
int a2= 5;
int a3= 5;
int a4= 5;
int a5= 5;
int a6= 5;
int a7= 5;
int a8= 5;
int a9= 5;
int a10= 5;
int a11= 5;
int a12=5;
int a13= 5;
int a14= 5;
}
He ejecutado la clase SpeedTest y al final obtuve esto:
Average Duration = 8.9625E8 ns = 896 ms (Approximately)
Ahora estoy cambiando las entradas en bytes en TestClass y lo estoy ejecutando de nuevo. Aquí está el resultado:
Average Duration = 6.94375E8 ns = 694 ms (Approximately)
Creo que este experimento muestra que si estás instaurando una gran cantidad de variables, usar byte en lugar de int puede aumentar la eficiencia
el byte generalmente se considera de 8 bits. corto generalmente se considera de 16 bits.
En un entorno "puro", que no es Java, ya que la implementación de bytes y largos, cortos y otras cosas divertidas generalmente está oculta, byte hace un mejor uso del espacio.
Sin embargo, su computadora probablemente no sea de 8 bits, y probablemente no sea de 16 bits. esto significa que para obtener 16 u 8 bits en particular, necesitaría recurrir a "trucos", lo que desperdicia tiempo para pretender que tiene la capacidad de acceder a esos tipos cuando sea necesario.
En este punto, depende de cómo se implemente el hardware. Sin embargo, desde mi punto de vista, la mejor velocidad se logra almacenando las cosas en pedazos que son cómodos de usar para su CPU. A un procesador de 64 bits le gusta tratar con elementos de 64 bits, y cualquier cosa menos que eso a menudo requiere "magia de ingeniería" para pretender que le gusta tratar con ellos.