improve the logic of expanding byte arrays

This commit is contained in:
Shay Banon 2012-07-11 10:23:45 +02:00
parent 7ff4371f95
commit 83323f2c88
4 changed files with 179 additions and 5 deletions

View File

@ -19,11 +19,102 @@
package org.elasticsearch.common;
import org.elasticsearch.monitor.jvm.JvmUtils;
/**
*
*/
public class Bytes {
/**
* Returns an array size >= minTargetSize, generally
* over-allocating exponentially to achieve amortized
* linear-time cost as the array grows.
* <p/>
* NOTE: this was originally borrowed from Python 2.4.2
* listobject.c sources (attribution in LICENSE.txt), but
* has now been substantially changed based on
* discussions from java-dev thread with subject "Dynamic
* array reallocation algorithms", started on Jan 12
* 2010.
*
* @param minTargetSize Minimum required value to be returned.
* @param bytesPerElement Bytes used by each element of
* the array. See constants in {@link RamUsageEstimator}.
* @lucene.internal
*/
public static int oversize(int minTargetSize, int bytesPerElement) {
if (minTargetSize < 0) {
// catch usage that accidentally overflows int
throw new IllegalArgumentException("invalid array size " + minTargetSize);
}
if (minTargetSize == 0) {
// wait until at least one element is requested
return 0;
}
// asymptotic exponential growth by 1/8th, favors
// spending a bit more CPU to not tie up too much wasted
// RAM:
int extra = minTargetSize >> 3;
if (extra < 3) {
// for very small arrays, where constant overhead of
// realloc is presumably relatively high, we grow
// faster
extra = 3;
}
int newSize = minTargetSize + extra;
// add 7 to allow for worst case byte alignment addition below:
if (newSize + 7 < 0) {
// int overflowed -- return max allowed array size
return Integer.MAX_VALUE;
}
if (JvmUtils.JRE_IS_64BIT) {
// round up to 8 byte alignment in 64bit env
switch (bytesPerElement) {
case 4:
// round up to multiple of 2
return (newSize + 1) & 0x7ffffffe;
case 2:
// round up to multiple of 4
return (newSize + 3) & 0x7ffffffc;
case 1:
// round up to multiple of 8
return (newSize + 7) & 0x7ffffff8;
case 8:
// no rounding
default:
// odd (invalid?) size
return newSize;
}
} else {
// round up to 4 byte alignment in 64bit env
switch (bytesPerElement) {
case 2:
// round up to multiple of 2
return (newSize + 1) & 0x7ffffffe;
case 1:
// round up to multiple of 4
return (newSize + 3) & 0x7ffffffc;
case 4:
case 8:
// no rounding
default:
// odd (invalid?) size
return newSize;
}
}
}
public static final byte[] EMPTY_ARRAY = new byte[0];

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.io;
import org.elasticsearch.common.Bytes;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
@ -76,7 +77,7 @@ public class FastByteArrayOutputStream extends OutputStream implements BytesStre
public void write(int b) {
int newcount = count + 1;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
buf = Arrays.copyOf(buf, Bytes.oversize(newcount, 1));
}
buf[count] = (byte) b;
count = newcount;
@ -98,7 +99,7 @@ public class FastByteArrayOutputStream extends OutputStream implements BytesStre
}
int newcount = count + len;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
buf = Arrays.copyOf(buf, Bytes.oversize(newcount, 1));
}
System.arraycopy(b, off, buf, count, len);
count = newcount;

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.io.stream;
import org.elasticsearch.common.Bytes;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.BytesStream;
@ -42,7 +43,7 @@ public class BytesStreamOutput extends StreamOutput implements BytesStream {
protected int count;
public BytesStreamOutput() {
this(126);
this(1024);
}
public BytesStreamOutput(int size) {
@ -53,7 +54,7 @@ public class BytesStreamOutput extends StreamOutput implements BytesStream {
public void writeByte(byte b) throws IOException {
int newcount = count + 1;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
buf = Arrays.copyOf(buf, Bytes.oversize(newcount, 1));
}
buf[count] = b;
count = newcount;
@ -66,7 +67,7 @@ public class BytesStreamOutput extends StreamOutput implements BytesStream {
}
int newcount = count + length;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
buf = Arrays.copyOf(buf, Bytes.oversize(newcount, 1));
}
System.arraycopy(b, offset, buf, count, length);
count = newcount;

View File

@ -0,0 +1,81 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.monitor.jvm;
import java.lang.reflect.Field;
/**
*/
public class JvmUtils {
/**
* True iff running on a 64bit JVM
*/
public static final boolean JRE_IS_64BIT;
public static final boolean JRE_IS_MINIMUM_JAVA6;
public static final boolean JRE_IS_MINIMUM_JAVA7;
static {
boolean is64Bit = false;
try {
final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
final Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
final Object unsafe = unsafeField.get(null);
final int addressSize = ((Number) unsafeClass.getMethod("addressSize")
.invoke(unsafe)).intValue();
//System.out.println("Address size: " + addressSize);
is64Bit = addressSize >= 8;
} catch (Throwable e) {
final String x = System.getProperty("sun.arch.data.model");
if (x != null) {
is64Bit = x.indexOf("64") != -1;
} else {
String OS_ARCH = System.getProperty("os.arch");
if (OS_ARCH != null && OS_ARCH.indexOf("64") != -1) {
is64Bit = true;
} else {
is64Bit = false;
}
}
}
JRE_IS_64BIT = is64Bit;
// this method only exists in Java 6:
boolean v6 = true;
try {
String.class.getMethod("isEmpty");
} catch (NoSuchMethodException nsme) {
v6 = false;
}
JRE_IS_MINIMUM_JAVA6 = v6;
// this method only exists in Java 7:
boolean v7 = true;
try {
Throwable.class.getMethod("getSuppressed");
} catch (NoSuchMethodException nsme) {
v7 = false;
}
JRE_IS_MINIMUM_JAVA7 = v7;
}
}