Самое быстрое и эффективное преобразование байтового массива в 29-битное целое число в Java
Поскольку в AMF популярны 29-битные целые числа, я хотел бы включить самую быструю / лучшую из известных процедур. В настоящее время в нашей библиотеке есть две подпрограммы, которые можно протестировать в режиме реального времени на ideone http://ideone.com/KNmYT
Вот источник для быстрого ознакомления
public static int readMediumInt(ByteBuffer in) {
ByteBuffer buf = ByteBuffer.allocate(4);
buf.put((byte) 0x00);
buf.put(in.get());
buf.put(in.get());
buf.put(in.get());
buf.flip();
return buf.getInt();
}
public static int readMediumInt2(ByteBuffer in) { byte[] bytes = new byte[3]; in.get(bytes); int val = 0; val += bytes[0] * 256 * 256; val += bytes[1] * 256; val += bytes[2]; if (val < 0) { val += 256; } return val; }
3 ответа
Обычно я делаю это с битовыми операциями. Вторая версия может в конечном итоге быть оптимизирована до чего-то близкого к этому JVM, но в этом нельзя быть уверенным. Теперь это только 24 бита, следуя вашим выборкам, но в вопросе написано "29-битное целое число". Я не уверен, что вы действительно хотели.
public static int readMediumInt(ByteBuffer buf) {
return ((buf.get() & 0xFF) << 16)
| ((buf.get() & 0xFF) << 8)
| ((buf.get() & 0xFF);
}
Если вы действительно хотите прочитать 29-разрядные целые числа AMF, это должно сработать (при условии, что я правильно понял формат):
private static int readMediumInt(ByteBuffer buf) {
int b0, b1, b2;
if ((b0 = buf.get()) >= 0) return b0;
if ((b1 = buf.get()) >= 0) return ((b0 << 7) & ((~(-1 << 7)) << 7)) | b1;
if ((b2 = buf.get()) >= 0) return ((b0 << 14) & ((~(-1 << 7)) << 14)) | ((b1 << 7) & ((~(-1 << 7)) << 7)) | b2;
return ((b0 << 22) & ((~(-1 << 7)) << 22)) | ((b1 << 15) & ((~(-1 << 7)) << 15)) | ((b2 << 8) & ((~(-1 << 7)) << 8)) | (buf.get() & 0xff);
}
Наиболее важным изменением является избегание размещения объектов в методе. Кстати, ваш микро тест не сбрасывал "старт", поэтому второй результат включает время, использованное для первого метода. Кроме того, вам нужно запускать микро-тесты несколько раз, в противном случае компилятор точно вовремя не сможет запустить. Я предлагаю использовать метод, аналогичный
public static int readMediumInt3(ByteBuffer buf) {
return ((buf.get() & 0xff) << 16) +
((buf.get() & 0xff) << 8) +
((buf.get() & 0xff));
}
Полный код:
import java.nio.ByteBuffer;
public class Main {
public static int readMediumInt(ByteBuffer in) {
ByteBuffer buf = ByteBuffer.allocate(4);
buf.put((byte) 0x00);
buf.put(in.get());
buf.put(in.get());
buf.put(in.get());
buf.flip();
return buf.getInt();
}
public static int readMediumInt2(ByteBuffer in) {
byte[] bytes = new byte[3];
in.get(bytes);
int val = 0;
val += bytes[0] * 256 * 256;
val += bytes[1] * 256;
val += bytes[2];
if (val < 0) {
val += 256;
}
return val;
}
public static int readMediumInt3(ByteBuffer buf) {
return ((buf.get() & 0xff) << 16) +
((buf.get() & 0xff) << 8) +
((buf.get() & 0xff));
}
public static void main(String[] args) {
Main m = new Main();
for (int i = 0; i < 5; i++) {
// version 1
ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(424242);
buf.flip();
long start;
start = System.nanoTime();
for (int j = 0; j < 10000000; j++) {
buf.position(0);
readMediumInt(buf);
}
start = System.nanoTime() - start;
System.out.printf("Ver 1: elapsed: %d ms\n", start / 1000000);
// version 2
ByteBuffer buf2 = ByteBuffer.allocate(4);
buf2.putInt(424242);
buf2.flip();
start = System.nanoTime();
for (int j = 0; j < 10000000; j++) {
buf2.position(0);
readMediumInt2(buf2);
}
start = System.nanoTime() - start;
System.out.printf("Ver 2: elapsed: %d ms\n", start / 1000000);
// version 3
ByteBuffer buf3 = ByteBuffer.allocate(4);
buf3.putInt(424242);
buf3.flip();
start = System.nanoTime();
for (int j = 0; j < 10000000; j++) {
buf3.position(0);
readMediumInt3(buf3);
}
start = System.nanoTime() - start;
System.out.printf("Ver 3: elapsed: %d ms\n", start / 1000000);
}
}
}
Мои результаты:
- Версия 1: истекло: 556 мс
- Версия 2: истек: 187 мс
- Версия 3: истекло: 3 мс