Performance paranoia: how much expensive are Float.parseFloat(String), Integer.parseInt(String)?
I am recently using JSON to store configuration parameters for a certain number of sub-classes of the same class. To keep a uniform interface, I have provided the parent class with public void setParameter(String, String)
and String getParameter(String)
methods. Each sub-class, then,开发者_如何学编程 casts the provided parameters to their native type and do some kind of computation using them.
Now, I am wondering: since I already store each parameter inside a HashMap, does it really makes sense to keep a separate field with the right type for each parameter? What is the computational overhead of converting String parameters to their native type each time I need them, given that I need to use them very often?
Thank you
TunnuzI suggest you test it. It is a fairly expensive operation if you need to do this many, many times but can be less expensive than Double.toString() or Integer.toString() if you used those to create the data in the first place.
I also suggest you only use double
unless you know using a float
could never ever cause a rounding issue. ;)
It is about as expensive as creating objects, like String or adding an entry to a HashMap. Unless you plan to avoid do this as well, I wouldn't worry about it.
EDIT: Similar to @Stackers' benchmark I would run the test longer and use nanoTime()
int runs = 10000000;
String val = "" + Math.PI;
long start = System.nanoTime();
for (int i = 0; i < runs; i++)
Float.parseFloat(val);
long time = (System.nanoTime() - start) / runs;
System.out.println("Average Float.parseFloat() time was " + time + " ns.");
long start2 = System.nanoTime();
for (int i = 0; i < runs; i++)
Double.parseDouble(val);
long time2 = (System.nanoTime() - start2) / runs;
System.out.println("Average Double.parseDouble() time was " + time2 + " ns.");
prints
Average Float.parseFloat() time was 474 ns.
Average Double.parseDouble() time was 431 ns.
BTW: I have function which reads doubles from a direct ByteBuffer which takes 80 ns. It is faster because it doesn't need a String and it doesn't create any objects. However, it is by no means trivial to do this and you have to design your core system to avoid any object creation. ;)
Measuring is as easy as:
public class PerfTest {
public static void main(String[] args) {
String val = "" + (float) Math.PI;
long start = System.currentTimeMillis();
for ( int i = 0 ; i < 100000 ; i++ ) {
Float.parseFloat( val );
}
System.out.println( System.currentTimeMillis() - start + " ms." );
}
}
62ms for 100.000 iterations.
fwiw the microbenchmarks above seem a little unsafe, you'd expect hotspot to spot that val never changes & is never used. The other thing to bear in mind is that there are times when the averages (of 2 implementations) might be close together in absolute terms but where 1 has a pretty bad tail cost compared to the other, e.g. your 90th percentile value might be pretty similar but the last 10% is much worse.
For example, changing it to use a different value each time and dumping the value to stderr produces an somewhat higher average cost (~3300ns vs ~2500ns for the case where the value is reused) on my box. This is much higher than the other posts presumably because it takes some amount of time to actually get the time so the measurement is artificially inflated. This just shows one of the difficulties in doing a good microbenchmark though.
It might also be worth noting I can't measure that effect I've suggested might be present, e.g. if it were present then you might expect it to get optimised away completely. I suppose you could see what is going on via LogCompilation
if you're really keen.
int runs = 10000000;
long totalTime = 0;
for (int i = 0; i < runs; i++) {
String val = "" + Math.random();
long start = System.nanoTime();
float f = Float.parseFloat(val);
long end = System.nanoTime();
System.err.println(f);
totalTime += (end-start);
}
long time = totalTime / runs;
totalTime = 0;
for (int i = 0; i < runs; i++) {
String val = "" + Math.random();
long start = System.nanoTime();
double d = Double.parseDouble(val);
long end = System.nanoTime();
System.err.println(d);
totalTime += (end-start);
}
long time2 = totalTime / runs;
System.out.println("Average Float.parseFloat() time was " + time + " ns.");
System.out.println("Average Double.parseDouble() time was " + time2 + " ns.");
精彩评论