开发者

Efficient strtod in Java?

So I have this Java program that I use to munch through several terabytes of data. Performance is a concern.

I've profiled the app, and a large fraction of all memory allocations as well as a large fraction of CPU time come from performing one simple operation:

I have an array of ASCII chars. I know that the character开发者_高级运维s from offset i to offset j represent a floating-point number. I need to extract that floating-point number into a double.

The naive Double.parseDouble(new String(buf, i, j - i)) does the job. However, this is where a lot of time is spent and a lot of memory allocations come from, probably because:

  • new String() creates a new object, creates an internal char[] array and copies the characters into the array;
  • Double.parseDouble() creates a FloatingDecimal object, and too creates a char[] array, also copying the characters into it.

All these allocations and all this copying are not really necessary. Can I avoid them?

What I'd really like is a strtod-like function that would take a char[] (or a byte[]) as well as start/end offsets, and return a double.

Any suggestions? Should I roll out my own? Should I write a JNI wrapper around strtod? Should I use some Java library that's already out there?


What I have done in the past is write a parser for ByteBuffer (to avoid byte to char encoding conversion) to double and visa-versa. If you can avoid creating any objects it can be much faster. This approach works for memory mapped files avoiding some copy costs as well.

The core code looks like the following. It doesn't handle exponents, but you can add that.

@Override
public double read() throws BufferUnderflowException {
  long value = 0;
  int exp = 0;
  boolean negative = false;
  int decimalPlaces = Integer.MIN_VALUE;
  while (true) {
    byte ch = buffer.get();
    if (ch >= '0' && ch <= '9') {
      while (value >= MAX_VALUE_DIVIDE_10) {
        value >>>= 1;
        exp++;
      }
      value = value * 10 + (ch - '0');
      decimalPlaces++;
    } else if (ch == '-') {
      negative = true;
    } else if (ch == '.') {
      decimalPlaces = 0;
    } else {
      break;
    }
  }

  return asDouble(value, exp, negative, decimalPlaces);
}

The full code

It stops as soon as it gets any byte it doesn't expect e.g. a , or \n


I'd look at the source for java.lang.Double, copy out the code that does parseDouble to my own helper class and modify it to work on char[] with offset and length directly.


Out of curiosity I copied the strtod function into Java and got ~10 time speedup comparing to Double.parseDouble(String) method (even without creating new Strings in loop). But maybe that's not enough for your implementation.

Micro benchmarking gives:

Double.parseDouble(): 1.6M conversions/second
Java strtod() method: 10.5M conversions/second


If you know an efficient C implementation, you could write a wrapper for it with JNI.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜