28 июля 2010 г.

java + USB, java + bluetooth (JSR-80/82)

Сегодня был на собеседовании. Задача -- программа на джаве должна общаться с девайсами через USB. Пришел на работу, полез смотреть, что есть на эту тему готового -- ну как-то не верилось мне, что до сих пор никому в голову не пришло работать с USB из java.

Пришло. Даже JSR есть -- №80. Пакет javax.usb официально зарезервирован. Есть reference implementation от IBM. Текущий статус? -- Черт его знает. Последние изменения датированы годом 2003 -- я еще в институте учился. Реализация для linux вроде худо-бедно рабочая. Реализации для windows -- нет. Внимание: вопрос на засыпку. Какой смысл в JSR для java, если реализация есть только для одной платформы? Кому нужно чисто для линукс -- мог бы и через usbfs извернуться.

Есть альтернатива: jUSB. Текущий статус -- такой же, все бросили. Реализация есть для linux/bsd/mac. Для windows -- какая-то alpha, на базе libusb. libusb в свою очередь -- проект про кроссплатформенному C-API для USB. Тоже с неизвестным статусом, тоже заброшен. win-реализация требует для сборки cygwin, и работает через свой драйвер USB уровня ядра (kernel mode). У меня смутное ощущение, что либо лыжи не едут, либо я ебанутый я чего-то здесь не понимаю -- USB что, настолько сложный стандарт? В винде невозможно работать с USB устройством иначе как проинсталлировав в систему свой драйвер? Я даже не столько жалуюсь, сколько реально не понимаю ситуации. Вроде USB очень современный, актуальный стандарт, over 9000 устройств через него подключаются -- мне бы казалось, что уже везде должны быть красивые и удобные привязки (binding) для работы с ним -- ан нет, в Java такого нет. И причина не понятна

Кстати, такая же подстава с java bluetooth. Есть JSR-82, есть какие-то реализации - в основном для J2ME. Здесь ситуация обратная -- с windows версией все вроде ок, linux/bsd версия есть, но с некоторыми шаманскими плясками. Опять же, проект стоит на месте уже лет 5. Странно.

16 июля 2010 г.

NIO

С завидной регулярностью забываю, что пакет NIO -- это не только ценный мех не только для ввода-вывода. В нем еще есть много полезных операций с примитивами -- в частности, такие вещи, которые бы в С делались простым приведением типа указателя, а в java так сходу даже и не вспоминается, как бы это сделать, и чтобы не сильно через задницу. Так вот — не через задницу это через java.nio. Вот, например, потребовалось сконвертировать int[] в byte[] -- пожалуйста:

final int[] idata = ...;
final ByteBuffer buff = ByteBuffer.allocate( idata.length * 4 );
buff.asIntBuffer().put( idata );
data = buff.array();

Интересное изнутри F/J

В статье про Fork/Join framework Дог Ли отмечает интересную деталь. Он реализовывал двунаправленную очередь (Deque) для задач на базе массива. И обнаружил, что если хранить в массиве непосредственно ссылки на объекты, то в многоядерном окружении это будет работать медленнее, чем если ввести дополнительный уровень, и хранить в массиве ссылку на промежуточный объект (Entry), а уж из объекта ссылаться на саму задачу. Он предполагает, что этот эффект возникает из-за уменьшения cache contention, потому что данные массива в памяти идут подряд, а Entry создаются джавой как получится, и чаще всего будут более-менее разбросаны по памяти. Я так понимаю, речь идет о cache contention при записи -- насколько мне помнится, в многоядерном окружении если разные ядра читают-пишут в одну и ту же область памяти -- это действительно довольно затратно, потому что приходится постоянно блокировать/разблокировать сегмент кэша на эксклюзивный доступ для конкретного ядра.

Ну а еще таким образом можно сделать ссылку на объект volatile, и упросить синхронизацию :)

JSR-166y -- fork/join

Ух ты блин! Читал "Scala 2.8.0: what's new" и наткнулся на загадочную фразу: "Actors can be configured to use the efficient JSR166y fork/join pool, resulting in significant performance improvements on 1.6 JVMs". Поскольку я никогда не слышал о fork/join в 1.6, полез копать. Оказывается, группа разработки JSR-166 (который java.util.concurrent) не угомонилась, когда их работа была включена в jdk. Нет, эти гарные хлопцы продолжили свои бесчеловечные экспериментыразработки. Они разработали расширение для JSR-166, называется JSR-166y, иначе Fork/Join framework. Это фреймворк, упрощающий разработку кода для многоядерных систем. В целом можно сказать, что это что-то в духе (отдаленно) map/reduce идеологии, когда код пишется в терминах элементарных операций, независимых друг от друга, которым совершенно по барабану, на каком ядре/узле сети они выполняются, а уже специальный управляющий модуль раскидывает куски данных по вычислительным узлам, и собирает результаты работы.

Статья Дога Ли с описанием исходного Fork/Join фраймворка здесь. Вкратце: вычисления обертываются в наследника ForkJoinTask (который, в свою очередь, наследник Future) и помещаются в ForkJoinPool (менеджер потоков). Выполняемый в пуле ForkJoinTask может внутри себя создать и запустить (асинхронно) другой ForkJoinTask, используя метод fork(). И может дождаться его завершения методом join() (близкий аналог Future.get()). В такой семантике легко записываются алгоритмы типа "разделяй и властвуй" -- в примерах обычно показывается, как удобно реализовывать merge sort в такой нотации.

Основной бонус перед ExecutorService/Future, как я его сейчас понял -- не нужно явно указывать ExecutorService каждый раз при создании новой задачи. То есть вместо
final ExecutorService es = ...;
final Future<?> f = es.submit( task );
final Object res = f.get();

мы имеем

final ForkJoinTask task = ...;
task.fork();
final Object res = task.join();


Разумеется, "верхний" ForkJoinTask все равно нужно явно поместить выполняться в какой-то пул -- но внутренняя реализация ForkJoinTask.exec не имеет никакой информации о пуле -- она вообще пул не видит, она видит только задачи (ForkJoinTask). На мой взгляд, это очень удобно. Последний раз, когда я реализовывал асинхронный рекурсивный обход дерева, я сильно ругался на то, что мне все время приходится тащить с собой ссылку на ExecutorService. Т.е. именно для рекурсивных алгоритмов, как я вижу, такой подход становится особенно удобным.

Дополнение можно просто скачать как отдельную библиотеку. Возможно, оно будет включено в jdk 1.7. Я бы приветствовал :)

9 июля 2010 г.

Не пущать!

Вчера и сегодня потратил два дня, пытаясь локализовать реализацию javax.measure из org.jscience. Уже не первый раз наталкиваюсь на странную штуку -- грамотные, вроде бы, программисты (Жан-Мари Дотель -- очень грамотный программист) создавая библиотеку, предназначенную для расширения ухитряются закрыть все возможности для этого.

Класс UnitFormat extends java.util.text.Format. Изначально, по задумке Жан-Мари, предназначен для локализации. Там даже комментарии в коде такие есть. И локализация была бы просто элементарной, если бы Жан-Мари не ухитрился во всех удобных точках вмешательства проставить такие права доступа, что к ним хрен доберешься.

В прошлый раз такая же засада была с gui-commands -- в конечном счете, штук 10 классов из этой библиотеки перекочевали в source path моего проекта, и подверглись вивисекции. Хотя этот метод мне всегда напоминал "guriella patching"

6 июля 2010 г.

Никогда, никогда, никогда блядь не пишите так:
public MyObject(final int param){    
    if( param < 0 ){        
        throw new IllegalArgumentException("Incorrect param");   
    }
}
надо как минимум вот так:
public MyObject(final int param){    
    if( param < 0 ){
        throw new IllegalArgumentException("Incorrect param: must be >=0, but got "+param);
    }
}
Это всего на пару дюжин символов длиннее -- но знали бы вы, сколько времени это иногда может сэкономить при отладке... Собственно, часто это может сделать саму отладку просто не нужной -- будет сразу понятно, где возник косяк. Особенно обидно, что такие вещи часто встречаются в самом jdk -- класс Color тому наглядный пример. В свое время в Матконструкторе мы частенько создавали Color из javascript, и когда получаешь "Color parameter outside of expected range: Alpha" это ни разу не помогает что-либо понять.