Расширение класса ByteBuffer

Есть ли способ создать класс, который расширяет класс ByteBuffer?

Некоторые абстрактные методы из ByteBuffer являются закрытыми для пакета, и если я создаю пакет java.nio, возникает исключение безопасности.

Я хотел бы сделать это по соображениям производительности. Например, getInt имеет около 10 вызовов метода, а также довольно много. Даже если все проверки оставлены, и только вызовы методов встроены, а большие/маленькие конечные проверки удалены, тесты, которые я создал, показывают, что это может быть примерно в 4 раза быстрее.

Ответы

Ответ 1

Вы не можете расширить ByteBuffer и благодарить Бога за.

Вы не можете продлить b/c, нет защищенных c-торов. Зачем благодарить богов? Ну, имея только 2 реальных подкласса, это гарантирует, что JVM может оптимизировать любой код с помощью ByteBuffer.

Наконец, если вам нужно расширить класс для реального, отредактируйте байтовый код и просто добавьте защищенный атрибут c-tor и public атрибут в DirectByteBuffer (и DirectByteBufferR). Расширение HeapBuffer не имеет никаких целей, поскольку вы можете в любом случае получить доступ к базовому массиву

используйте -Xbootclasspath/p и добавьте туда свои собственные классы, расширьте их в нужном вам пакете (вне java.nio). Это так.

Другой способ - использовать sun.misc.Unsafe и делать все, что вам нужно, w/прямой доступ к памяти после address().

Я хотел бы сделать это для производительности - getInt для пример имеет около 10 методов приглашений, а также немало если х. Даже если все проверки остались, и только вызовы метода включены и удаляются большие/мелкие сундуки, тесты, которые я создал, показывают, что это может быть примерно в 4 раза быстрее.

Теперь хорошая часть, используйте gdb и проверьте действительно сгенерированный машинный код, вы будете удивлены, сколько проверок будет удалено.

Я не могу себе представить, почему человек хотел бы расширить классы. Они существуют, чтобы обеспечить хорошую производительность не только для выполнения полиморфизма OO.


изменить:

Как объявить любой класс и обходить верификатор Java

В Unsafe: Unsafe имеет 2 метода, которые обходят верификатор, и если у вас есть класс, который расширяет ByteBuffer, вы можете просто вызвать любой из них. Вам нужна взломанная версия (но такая супер простая) ByteBuffer с открытым доступом и защищенный c-tor только для компилятора. Ниже приведены методы. Вы можете использовать их на свой страх и риск. После того, как вы объявите такой класс, вы можете использовать его с новым ключевым словом (при условии, что имеется подходящий c-tor)

public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);    
public native Class defineClass(String name, byte[] b, int off, int len);

Ответ 2

Вы можете игнорировать уровни защиты, используя рефлексию, но это своего рода поражает цель производительности в большой степени.

Вы не можете создать класс в пакете java.nio - делая это (и распределяя результат каким-либо образом), нарушает лицензию Sun Java и теоретически может привести вас к юридическим проблемам.

Я не думаю, что есть способ сделать то, что вы хотите сделать, не выходя из родного, - но я также подозреваю, что вы поддались искушению преждевременной оптимизации. Предполагая, что ваши тесты верны (какие микрообъективы часто отсутствуют): действительно ли вы уверены, что доступ к ByteBuffer будет узким местом производительности вашего приложения? Это не имеет значения, может ли ByteBuffer.get() быть в 4 раза быстрее, когда ваше приложение тратит только 5% своего времени и 95% обрабатывает полученные данные.

Желание обойти все проверки ради (возможно, чисто теоретической) производительности не кажется хорошей идеей. Основным правилом настройки производительности является "Сначала сделайте это правильно, THEN сделайте его работу быстрее".

Изменить: Если, как указано в комментариях, приложение фактически проводит 20-40% своего времени в методах ByteBuffer, и тесты правильные, что означает потенциал ускорения 15- 30% - значительная, но IMO не стоит начинать использовать JNI или возиться с источником API. Сначала я попытаюсь исчерпать все остальные варианты:

  • Используете ли вы -server VM?
  • Может ли приложение быть изменено, чтобы сделать меньше вызовов ByteBuffer, а не пытаться ускорить их выполнение?
  • Используйте профилировщик, чтобы узнать, откуда идут вызовы - возможно, некоторые из них совершенно ненужны.
  • Возможно, алгоритм может быть изменен, или вы можете использовать какое-то кэширование

Ответ 3

ByteBuffer является абстрактным, поэтому да, вы можете его расширить... но я думаю, что то, что вы хотите сделать, - это расширить класс, который фактически создан, что вы, вероятно, не сможете. Также может быть, что конкретный экземпляр, который получает экземпляр, переопределяет этот метод более эффективным, чем тот, который используется в ByteBuffer.

Я бы также сказал, что вы, скорее всего, ошибаетесь в целом обо всем, что нужно - возможно, это не то, что вы тестируете, но, скорее всего, код существует по какой-то причине (возможно, на других платформах).

Если вы считаете, что вы правы, откройте bug и посмотрите, что они должны сказать.

Если вы хотите добавить в пакет nio, вы можете попробовать установить путь к классу загрузки при вызове Java. Это должно позволить вам вводить ваши классы в rt.jar. Введите java -X, чтобы увидеть, как это сделать, вам нужен ключ -Xbootclasspath/p.

Ответ 4

+50 баунтин за способ обойти ограничение доступа (tt не может быть сделанное с использованием отражения в одиночку. Может быть есть способ использовать sun.misc.Unsafe и др.?)

Ответ: нет возможности обойти все ограничения доступа в Java.

  • sun.misc.Unsafe работает под руководством менеджеров безопасности, поэтому это не поможет.
  • Как сказал Сарнум:

У ByteBuffer есть пакет private абстрактные методы _set и _get, так что вы не может переопределить его. А также все конструкторы являются частными, поэтому вы не можете назвать их.

  • Отражение позволяет обойти много вещей, но только если это позволяет менеджер безопасности. Существует много ситуаций, когда у вас нет контроля над менеджером безопасности, он наложен на вас. Если ваш код должен полагаться на вождение менеджерами безопасности, он не будет "портативным" или исполняемым при любых обстоятельствах, так сказать.

Нижняя строка вопроса заключается в том, что попытка переопределить буфер байтов не решит проблему.

Нет другого выбора, кроме как реализовать класс самостоятельно, с помощью методов, которые вам нужны. Создание окончательных методов помогло компилятору в его попытке выполнить оптимизацию (уменьшить необходимость генерации кода для полиморфизма времени выполнения и вложения).

Ответ 5

Самый простой способ получить Unsafe-экземпляры - это отражение. Однако, если отражение недоступно для вас, вы можете создать другой экземпляр. Вы можете сделать это через JNI.

Я попробовал в байтовом коде создать экземпляр без вызова конструктора, позволяющий создать экземпляр объекта без доступных конструкторов. Однако этот идентификатор не работает, поскольку я получил VerifyError для байтового кода. У объекта должен быть вызванный конструктор.


У меня есть ParseBuffer, который обертывает прямой ByteBuffer. Я использую отражение для получения ссылки Unsafe и address. Чтобы избежать завершения работы буфера и уничтожения JVM, я выделяю больше страниц, чем мне нужно, и пока они не тронуты, физическая память не будет выделена для приложения. Это означает, что у меня есть гораздо меньше ограничений и проверяется только в ключевых точках.

Используя отладочную версию OpenJDK, вы можете увидеть, что методы Unsafe get/put превращаются в одну командную инструкцию машинного кода. Тем не менее, это не доступно во всех JVM и может не получить то же самое улучшение на всех платформах.

Используя этот подход, я бы сказал, что вы можете получить 40% -ное сокращение таймингов, но рискует, что нормальный код Java не имеет, то есть вы можете убить JVM. У меня есть бесплатный XML-парсер для создания объектов и процессор данных, содержащихся в Unsafe по сравнению с использованием простого прямого ByteBuffer. Одним из трюков, которые я использую в синтаксическом анализе XML, является getShort() и getInt() для одновременного изучения нескольких байтов, а не для проверки каждого байта за раз.

Использование отражения в классе Unsafe является накладными расходами, которые вы выполняете один раз. Если у вас есть экземпляр Unsafe, накладные расходы отсутствуют.

Ответ 6

A Агент Java может изменять байт-код ByteBuffer и изменять модификатор доступа к конструктору. Конечно, вам нужно будет установить агента в JVM, и вам все равно придется компилировать, чтобы ваш подкласс был скомпилирован. Если вы планируете такую ​​оптимизацию, то вы должны быть за нее!

Я никогда не делал таких манипуляций с низким уровнем. Надеюсь, ByteBuffer не нужен JVM, прежде чем ваш агент сможет подключиться к нему.

Ответ 7

Я отвечаю на вопрос, на который ВЫ ХОТИТЕ ответ, а не тот, который вы попросили. Ваш реальный вопрос: "Как я могу сделать это быстрее?" и ответ "обрабатывает целые числа массивом за раз, а не по отдельности".

Если узким местом является ByteBuffer.getInt() или ByteBuffer.getInt(location), то вам не нужно расширять класс, вы можете использовать уже существующий класс IntBuffer для сбора данных навалом для более эффективной обработки.

int totalLength = numberOfIntsInBuffer;
ByteBuffer myBuffer = whateverMyBufferIsCalled;
int[] block = new int[1024];
IntBuffer intBuff = myBuffer.asIntBuffer();
int partialLength = totalLength/1024;

//Handle big blocks of 1024 ints at a time
try{
  for (int i = 0; i < partialLength; i++) {
     intBuff.get(block);
     // Do processing on ints, w00t!
  }

  partialLength = totalLength % 1024; //modulo to get remainder
  if (partialLength > 0) {
    intBuff.get(block,0,partialLength);
    //Do final processing on ints
  }
} catch BufferUnderFlowException bufo {
   //well, dang!
}

Это МНОГО, МНОГО быстрее, чем получение int за раз. Итерация по массиву int [] с установленными и известными границами также позволит вашему коду JIT значительно упроститься, исключив проверки границ и исключения, которые ByteBuffer может выполнять.

Если вам нужна дополнительная производительность, вы можете настроить код или перевести свой собственный оптимизированный по размеру байт [] в код преобразования int []. Я смог получить некоторое повышение производительности, используя это вместо методов IntBuffer с разворачиванием частичного цикла... но это не было предложено никакими средствами.