Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format a MAC address in Android/Java without creating unnecessary garbage

I am working on an Android app which needs to process thousands of packets per second while extracting and formatting the MAC address of each frame. The problem is that the garbage collector is running a dozen times per second and stalling my app, which in turn makes me miss packets. I have avoided creating new objects as much as (I think) possible.

I have used the allocation tracker in DDMS and determined that 99% of the garbage being cleaned up is coming from the following method. Here is the code I am using:

void parseMac() { 
    hex_sb.setLength(0);

    for (hex_counter = 0; hex_counter < 6; hex_counter++) {
        hex_sb.append(String.format("%02X", parser_packet_bytes[parser_skip + hex_counter])); 
        if (!(hex_counter == 5)) {
            hex_sb.append(":");
        }
    }

    formatted_mac = hex_sb.toString();
}

hex_sb is a StringBuilder, which gets reused. hex_counter is the number of bytes in a MAC address (bytes come from parser_packet_bytes, a byte[]). If it's not the last byte of the MAC, append a ":" for proper formatting. formatted_mac is a class-wide String that stores the formatted MAC. According to the allocation tracker, the only problem is the line using String.format.

My question to the StackOverflow experts is: how can I rewrite the above method so that less (preferrably no) garbage is created?

like image 579
user1722919 Avatar asked Oct 03 '22 03:10

user1722919


2 Answers

Instead of using String.format(), which is quite expensive, just manually append the nibbles. Unfortunately, the digits and letters aren't contiguous in ASCII/UTF-8, so here's how I'd handle it:

static final char HEX_DIGITS[] = "01234567890abcdef".toCharArray();
...
hex_sb.append(HEX_DIGITS[thisByte >> 4]).append(HEX_DIGITS[thisByte & 0xf]);

Since this is a MAC address (known length) and gets called a whole lot, I would probably unroll the whole thing, including appending the colon/period (which should be a char, not a String). If it's really speed-critical, manage your own char[] and feed it to String#new(char[]). You could avoid re-inserting the delimiters that way.

like image 65
chrylis -cautiouslyoptimistic- Avatar answered Oct 12 '22 12:10

chrylis -cautiouslyoptimistic-


Each new MAC will need a new String, you cant help it since String is immutable. As for StringBuilder manipulations it does not create any garbage as StringBuilder will reuse the same char array on setLength(0) and append, it will only change the current position. The only thing which would help is to work with StringBuilder directly without converting it to String if only it is possible.

like image 40
Evgeniy Dorofeev Avatar answered Oct 12 '22 10:10

Evgeniy Dorofeev