Your slow code is slow because of the multiple String instances you create, and repleace, and so on. It is 6 times slower becauseyou have a binString, which you then format in to a padded string (which likely has internal string representations, and then finally you search/replace values in that too.
The fast code does not have any transformations, it is simply an additive process, and Strings get created just once, and there is only one creation... of a String, and 1 creation of a char[] array
There are two ways you can improve the performance of your fast method:
- set the initial size of the
StringBuilder(...) which will always be 32.
- create a single PAD array, and use a sub-portion of it for the results
I have the code:
private static final char[] PAD_ARRAY = new char[8];
static {
Arrays.fill(PAD_ARRAY, '0');
}
public static String fast(String ip) {
StringBuilder bStringBuilder = new StringBuilder(32);
String ipParts[] = ip.split("\\.");
for (String ipPart : ipParts) {
String binString = Integer.toBinaryString(Integer.parseInt(ipPart));
bStringBuilder.append(PAD_ARRAY, 0, 8 - binString.length()).append(binString);
}
//System.out.println(bStringBuilder.toString());
return bStringBuilder.toString();
}
There is a different way to solve these sorts of problems, though, and that is to pre-process a chunk of the work. I was dissappointed that this approach was only slightly faster than your fast version, though.
Consider this code which pre-populates an array of binary representations:
private static final String[] IPMAP = buildIPMap();
private static final String[] buildIPMap() {
String[] ret = new String[256];
for (int i = 0; i < ret.length; i++) {
int[] bits = new int[8];
for (int b = 0; b < 8; b++) {
if ((i & (1 << b)) == 1) {
bits[b] = 1;
}
}
final String bitwords = String.format("%d%d%d%d%d%d%d%d", bits[7], bits[6], bits[5], bits[4], bits[3], bits[2], bits[1], bits[0]);
ret[i] = bitwords;
}
return ret;
}
Now, you can have the following code:
public static String mapped(String ip) {
StringBuilder sb = new StringBuilder(32);
for (String block : ip.split("\\.")) {
sb.append(IPMAP[Integer.parseInt(block)]);
}
return sb.toString();
}
In my testing, the different solutions have the following characteristics:
Task Routing -> Slow: (Unit: MICROSECONDS)
Count : 100000 Average : 3.8060
Fastest : 2.2880 Slowest : 9464.0360
95Pctile : 6.3870 99Pctile : 11.6600
TimeBlock : 8.366 4.366 5.914 3.657 2.696 2.944 2.547 2.724 2.443 2.407
Histogram : 84962 12731 1761 277 173 81 9 0 1 4 0 0 1
Task Routing -> Fast: (Unit: MICROSECONDS)
Count : 100000 Average : 0.4710
Fastest : 0.2910 Slowest : 129.1240
95Pctile : 0.7420 99Pctile : 1.9220
TimeBlock : 1.442 0.397 0.485 0.353 0.331 0.360 0.339 0.338 0.335 0.334
Histogram : 84577 12020 2676 294 305 95 20 4 9
Task Routing -> Mapped: (Unit: MICROSECONDS)
Count : 100000 Average : 0.4540
Fastest : 0.2890 Slowest : 1663.1440
95Pctile : 0.6670 99Pctile : 1.6780
TimeBlock : 1.392 0.381 0.441 0.341 0.334 0.342 0.327 0.331 0.327 0.331
Histogram : 87774 9244 2313 283 297 69 13 0 6 0 0 0 1