3 февраля 2011 г.
Что каждый программист...
...должен знать о плавающей запятой. Увлекся чтением очередной книги из серии. Черт, парни, которые создавали стандарт IEEE 754 -- да они просто гении! Надеюсь, лет через 10-15 я буду способен создавать настолько же продуманные системы.
1 февраля 2011 г.
NaN-ов много, надо это использовать
Сегодня на хабре проскочила замечательная статья Что нужно знать про арифметику с плавающей запятой. Там довольно подробное "человеческое" описание IEEE754, даже немного истории есть. Нет, принципиально нового в ней не так уж много, но все в одном месте, и на русском -- это дорогого стоит. Автору большое спасибо.
Что меня в ней поразило, так это фраза, цитирую:
Неопределенность или NaN (от not a number) – это представление, придуманное для того, чтобы арифметическая операция могла всегда вернуть какое-то не бессмысленное значение. В IEEE754 NaN представлен как число, в котором E=Emax+1, а мантисса не нулевая. Любая операция с NaN возвращает NaN. При желании в мантиссу можно записывать информацию, которую программа сможет интерпретировать. Стандартом это не оговорено и мантисса чаще всего игнорируется.
Что NaN-ов вообще-то много я знал, но что разные NaN-ы можно использовать для передачи каких-то подробностей -- это свежая идея. Нет, конечно, это не для использования в каждодневном программировании, но если есть какая-нибудь часто вызываемая функция, которая возвращает NaN как индикатор "не получилось", то ведь можно вместе с ним передавать, например, конкретную причину. Хотя будет ли это быстрее, чем бросать в таких ситуациях исключение специального типа, с нужной информацией -- вопрос. Нужно потестировать
Что меня в ней поразило, так это фраза, цитирую:
Неопределенность или NaN (от not a number) – это представление, придуманное для того, чтобы арифметическая операция могла всегда вернуть какое-то не бессмысленное значение. В IEEE754 NaN представлен как число, в котором E=Emax+1, а мантисса не нулевая. Любая операция с NaN возвращает NaN. При желании в мантиссу можно записывать информацию, которую программа сможет интерпретировать. Стандартом это не оговорено и мантисса чаще всего игнорируется.
Что NaN-ов вообще-то много я знал, но что разные NaN-ы можно использовать для передачи каких-то подробностей -- это свежая идея. Нет, конечно, это не для использования в каждодневном программировании, но если есть какая-нибудь часто вызываемая функция, которая возвращает NaN как индикатор "не получилось", то ведь можно вместе с ним передавать, например, конкретную причину. Хотя будет ли это быстрее, чем бросать в таких ситуациях исключение специального типа, с нужной информацией -- вопрос. Нужно потестировать
21 декабря 2010 г.
MySQL HandlerSocket
HandlerSocket -- это расширение (плагин) для MySQL, позволяющий делать простые CRUD запросы непосредственно к движку хранения данных MySQL, обходя слой SQL. По словам автора, производительность такого решения в случае, когда данные БД влезают в память выше, чем производительность memcached. При том, что мы сохраняем возможность работать с теми же данными через SQL.
Здесь краткое введение в тему.
А здесь -- драйвер для java. Осталось только найти повод его где-нибудь попользовать
Здесь краткое введение в тему.
А здесь -- драйвер для java. Осталось только найти повод его где-нибудь попользовать
11 октября 2010 г.
Range check elimination
Узнал приятную новость -- оказывается в OpenJDK JIT умеет делать (пока что простейшие) array range check elimination (пруф). Другими словами -- в простых циклах по массивам вида
если компилятор может доказать, что счетчик цикла не выходит за границы массива -- runtime проверки индекса при доступе к элементам массива будут убраны. А это означает, что скорость такого доступа уже точно ничем дополнительным, по сравнению с С, не ограничена
Пока я, правда, не понял, используется ли это в какой-нибудь release версии JDK.
for(int i=0;i<arr.length;i++){ arr[i] = ...; }
если компилятор может доказать, что счетчик цикла не выходит за границы массива -- runtime проверки индекса при доступе к элементам массива будут убраны. А это означает, что скорость такого доступа уже точно ничем дополнительным, по сравнению с С, не ограничена
Пока я, правда, не понял, используется ли это в какой-нибудь release версии JDK.
28 сентября 2010 г.
Импорт классов из default package
C удивлением обнаружил, что, оказывается, классы из дефолтного пакета (которые в корне иерархии) нельзя импортировать и использовать в классах из любых других пакетов! Т.е. использовать можно, но только через reflection -- Class.forName(".."), etc. Более того, оказывается когда-то в java это было разрешено -- специальной конструкцией
import Unfinished;
(а я даже не знал, что такая есть...). О, сколько нам открытий чудных...
15 сентября 2010 г.
Развернутый связный список
Стыдно признаться, но я никогда еще в своей практике не использовал LinkedList. Не находилось оказии -- всегда оказывалось, что ArrayList либо в принципе подходит лучше, либо алгоритм можно так перестроить, что будет подходить лучше, либо в принципе он хуже, но на потребных размерах выигрывает. Последовательный доступ, большие накладные расходы памяти на хранение указателей в узлах, недружественность к кэшу и векторным инструкциям -- на мой взгляд, этого достаточно, чтобы 10 раз подумать, прежде чем его использовать. Тем не менее, никуда не деться -- вставка в случайное место для ArrayList дороже, чем для Linked уже начиная где-то с 500-1000 элементов (ну еще есть многопоточные приложения, где недружественность к кэшу становится преимуществом -- меньше конкуренция разных вычислительных ядер за строки кэша).
Не далее как вчера я обдумывал эту мысль, и пришел к выводу, что если бы мне нужен был быстрый список без (быстрого) случайного доступа но со всеми остальными плюшками, я бы попробовал сделать что-то вроде смеси array и linked -- узлы с массивами по нескольку элементов, так, чтобы узел вместе со всей служебной информацией влезал, скажем, в cache line. Получаем сразу и меньшие накладные расходы памяти -- одна пара указателей не на один, а сразу на несколько элементов. И лучшую локальность данных, и поле применимости для векторых инструкций. Все плюшки, кроме быстрого случайного доступа.
А сегодня обнаруживаю, что идею украли прямо из головы. И даже обозвать уже успели, и в википедии записали. Называется развернутый связный список (unrolled linked list). Неплохая статья на MSDN по этой структуре данных. Автор провел небольшое исследование производительности, и нашел, что развернутый список обходится (последовательно) примерно в 2.5 раза медленнее массива (обычный связный -- в 12 раз медленнее). На мой взгляд -- вполне разумный компромисс.
Еще вчера же, когда я дочитывал про B-trees, мне так же пришло в голову, что обычные бинарные диревья в нынешних условиях многоуровневых кэшей и векторных инструкций тоже очень не в тему. Всякие TreeMap разумнее было бы делать на базе B-trees, нежели на основе красно-черных бинарных деревьев. Ведь в конце-концов B-trees и были придуманы для ситуаций, когда операция сравнения элементов гораздо (на порядки) дешевле операции загрузки очередной страницы дерева. Некогда имелась в виду загрузка с диска/ленты, но нынче в роли "медленного хранилища" вполне подходит основная оперативная память. При разнице в скоростях доступа к кэшу и оперативке в несколько порядков -- куда уж медленнее. Вместо того, чтобы делать одно сравнение, и переходить к следующему узлу -- с высокой вероятностью кэш-промаха -- было бы оптимальнее сделать десяток-другой сравнений сразу.
Не далее как вчера я обдумывал эту мысль, и пришел к выводу, что если бы мне нужен был быстрый список без (быстрого) случайного доступа но со всеми остальными плюшками, я бы попробовал сделать что-то вроде смеси array и linked -- узлы с массивами по нескольку элементов, так, чтобы узел вместе со всей служебной информацией влезал, скажем, в cache line. Получаем сразу и меньшие накладные расходы памяти -- одна пара указателей не на один, а сразу на несколько элементов. И лучшую локальность данных, и поле применимости для векторых инструкций. Все плюшки, кроме быстрого случайного доступа.
А сегодня обнаруживаю, что идею украли прямо из головы. И даже обозвать уже успели, и в википедии записали. Называется развернутый связный список (unrolled linked list). Неплохая статья на MSDN по этой структуре данных. Автор провел небольшое исследование производительности, и нашел, что развернутый список обходится (последовательно) примерно в 2.5 раза медленнее массива (обычный связный -- в 12 раз медленнее). На мой взгляд -- вполне разумный компромисс.
Еще вчера же, когда я дочитывал про B-trees, мне так же пришло в голову, что обычные бинарные диревья в нынешних условиях многоуровневых кэшей и векторных инструкций тоже очень не в тему. Всякие TreeMap разумнее было бы делать на базе B-trees, нежели на основе красно-черных бинарных деревьев. Ведь в конце-концов B-trees и были придуманы для ситуаций, когда операция сравнения элементов гораздо (на порядки) дешевле операции загрузки очередной страницы дерева. Некогда имелась в виду загрузка с диска/ленты, но нынче в роли "медленного хранилища" вполне подходит основная оперативная память. При разнице в скоростях доступа к кэшу и оперативке в несколько порядков -- куда уж медленнее. Вместо того, чтобы делать одно сравнение, и переходить к следующему узлу -- с высокой вероятностью кэш-промаха -- было бы оптимальнее сделать десяток-другой сравнений сразу.
5 сентября 2010 г.
Потестировал на днях свою реализацию lock-free BufferedQueue. Неожиданно, но версия с блокировками всухую обходит CAS. Пока не могу понять, в чем дело -- грешу на ConcurrentLinkedQueue
Оказывается, с атомиками все тоже не так-то просто. Они, конечно, дешевле блокировок, но создают ничуть не меньший траффик на межпроцессорной (межядерной) шине. В определенных условиях это может сильно ухудшать производительность.
Оказывается, с атомиками все тоже не так-то просто. Они, конечно, дешевле блокировок, но создают ничуть не меньший траффик на межпроцессорной (межядерной) шине. В определенных условиях это может сильно ухудшать производительность.
1 сентября 2010 г.
Shared exceptions
Доктор Хейнс Кабуц в очередной рассылке пишет про интересную вещь -- оказывается, если в коде часто выбрасываются RuntimeException типа NPE, или ArrayIndexOutOfBoundsException, то JIT в серверном режиме их оптимизирует -- начиная с какого-то момента он перестанет создавать новый экземпляр исключения на каждый случай, а начнет каждый раз бросать один и тот же экземпляр, с пустым stack trace! Я припоминаю, что в самом деле видел такие исключения без стека вызовов -- но как-то не подумал, с чем это может быть связано, списал на баг протоколирования. Оказывается эта оптимизация выполняется аж с 2002 года -- хотя я до сих пор нигде не встречал ее описания.
27 августа 2010 г.
Immutable vs Mutable объекты
Когда я узнал, что final поля "гарантированно видимы любому потоку" после завершения инициализации, я подумал, что ведь это чего-то да стоит. В том смысле, что создание immutable объекта с final полями должно быть дороже (как минимум, в многопоточном окружении, чтобы JIT не смог это оптимизировать). Т.е. как минимум нужен же мембар по завершении конструктора. Сегодня попытался это проверить -- объявил два идентичных класса, у одного из которых все поля final, и создавал их в 150 потоков параллельно. Черта с два -- одинаково. Даже наоборот, есть слабое преимущество у immutable класса, но я бы не назвал его статистически значимым -- где-то 0.5%, слишком мало, чтобы принимать во внимание в случае микробенчмарка.
P.S. Постфактум это становится довольно очевидным. Семантика final полей обеспечивается StoreStore барьером между инициализацией полей и публикацией ссылки на созданный объект:
Но на x86, где я тестировал, StoreStore барьер не нужен -- тамошняя "железная" модель памяти (TSO -- total store order) достаточно строгая сама по себе.
Более того, по словам DL в его кулинарной книге StoreStore барьеры, даже если они и требуются на какой-то архитектуре, обычно одни из самых дешевых. Так что, с высокой степенью вероятности, их влияние на производительность даже там, где они есть (и требуются) будет теряться на фоне, например, затрат на выделение памяти
P.S. Постфактум это становится довольно очевидным. Семантика final полей обеспечивается StoreStore барьером между инициализацией полей и публикацией ссылки на созданный объект:
localRef = alloc(...); localRef.field1 = ...; localRef.field2 = ...; .... StoreStore; globallyAvailableRef = localRef;
Но на x86, где я тестировал, StoreStore барьер не нужен -- тамошняя "железная" модель памяти (TSO -- total store order) достаточно строгая сама по себе.
Более того, по словам DL в его кулинарной книге StoreStore барьеры, даже если они и требуются на какой-то архитектуре, обычно одни из самых дешевых. Так что, с высокой степенью вероятности, их влияние на производительность даже там, где они есть (и требуются) будет теряться на фоне, например, затрат на выделение памяти
Производительность
Тестировал, ради интереса, пиковую производительность разных методов доступа к разделяемому состоянию в многопоточном окружении. Участники соревнования -- sunchronized, ReentrantLock, AtomicInteger, volatile+AtomicIntegerFieldUpdater.
Результаты, в целом, предсказуемы -- атомики в 2-3 раза быстрее блокировок. Две интересные вещи:
ReentrantLock опять обогнал synchronized! Заметно -- на четверть почти. Как-то я уже обнаруживал этот феномен. До сих пор не могу понять, в чем здесь цимус -- Сановцы забили на оптимизацию "старого" способа синхронизации? -- но на ус придется намотать :)
AtomicInteger немного обогнал volatile+AtomicIntegerFieldUpdater. Чуть-чуть -- процентов на 5 в среднем. Не то, чтобы неожиданно -- накладные расходы на reflection-based доступ -- но все же приятно видеть их вживую в цифрах. Разница есть, она измерима, но по величине явно не критична. Т.е. на AtomicIntegerFieldUpdater можно закладываться как на альтернативу AtomicInteger.
Результаты, в целом, предсказуемы -- атомики в 2-3 раза быстрее блокировок. Две интересные вещи:
ReentrantLock опять обогнал synchronized! Заметно -- на четверть почти. Как-то я уже обнаруживал этот феномен. До сих пор не могу понять, в чем здесь цимус -- Сановцы забили на оптимизацию "старого" способа синхронизации? -- но на ус придется намотать :)
AtomicInteger немного обогнал volatile+AtomicIntegerFieldUpdater. Чуть-чуть -- процентов на 5 в среднем. Не то, чтобы неожиданно -- накладные расходы на reflection-based доступ -- но все же приятно видеть их вживую в цифрах. Разница есть, она измерима, но по величине явно не критична. Т.е. на AtomicIntegerFieldUpdater можно закладываться как на альтернативу AtomicInteger.
22 августа 2010 г.
Lock-free BufferedQueue
Дали тестовое задание перед собеседованием -- написать буфферизированную очередь. Т.е. на вход метода
Первая моя версия использовала ReadWriteLock -- неэксклюзивным ReadLock-ом защищался append, эксклюзивным WriteLock-ом -- flush. Но потом я взмедитнул, и подумал, что блокировки там вообще лишние. И получилась у меня lock-free версия вот такой:
Это первый мой опыт в самостоятельном написании lock-free алгоритмов на CAS-примитивах, поэтому написал я этот код часа за два, а медитировал над ним дня 3. Но косяков так и не нашел -- все должно работать как задумано. Увлекательное это занятие -- обдумывать корректность таких алгоритмов, мозг работает явно на повышенных оборотах
append(T item)
из нескольких потоков сыплются некие объекты, и, раз в какой-то интервал времени, задаваемый при создании, накопившиеся объекты сбрасываются списком в заранее заданный callback. Причем, понятно, очередь должна иметь как можно большую пропускную способность, т.е. засинхронизировать метод append -- это не выход. Первая моя версия использовала ReadWriteLock -- неэксклюзивным ReadLock-ом защищался append, эксклюзивным WriteLock-ом -- flush. Но потом я взмедитнул, и подумал, что блокировки там вообще лишние. И получилась у меня lock-free версия вот такой:
public class LockFreeBufferedQueue<ItemType> { private final ICallback<Collection<ItemType>> callback; private final long delay; private final TimeUnit timeUnit; private final ScheduledExecutorService executor; private final CallableVoid> flushBufferTask = new Callable<Void>() { public Void call() { assert ( pending.get() > 0 ) : "Call without being scheduled"; flushBuffer(); return null; } }; private final AtomicInteger pending = new AtomicInteger( 0 ); /** * volatile чтобы обойтись без блокировки в методе isClosed() */ private volatile boolean closed = false; private volatile ScheduledFuture<Void> scheduledTask = null; /** * lock-free Queue implementation */ private final ConcurrentLinkedQueue<ItemType> queue = new ConcurrentLinkedQueue<ItemType>(); public LockFreeBufferedQueue( final ICallback<Collection<ItemType>> callback, final long delay, final TimeUnit timeUnit, final ScheduledExecutorService executor ) { this.callback = callback; this.delay = delay; this.timeUnit = timeUnit; this.executor = executor; } /** * Не специфицированно, что должен делать метод close(), поэтому мы делаем так: * * Уже добавленные задачи будут выполнены немедленно, в текущем потоке, добавление * новых приведет к IllegalStateException в методе append(). */ public void close() { if ( !closed ) { closed = true; if ( scheduledTask != null ) { if ( scheduledTask.cancel( false ) ) { flushBuffer(); } scheduledTask = null; } //todo but if executor is external? executor.shutdown(); } } /** * @throws IllegalStateException если очередь уже закрыта вызовом close() * @throws NullPointerException если item == null */ public void append( final ItemType item ) { if ( closed ) { throw new IllegalStateException( "Queue was closed" ); } queue.add( item ); if ( pending.getAndIncrement() == 0 ) { schedule(); } } private void flushBuffer() { final int size = pending.get(); assert ( !queue.isEmpty() ) : "Call with empty queue"; assert ( size > 0 ) : "Call with 0 pending"; final Collection<ItemType> buffer = new ArrayList<ItemType>( size ); for ( int i = 0; i < size; i++ ) { buffer.add( queue.poll() ); } if ( pending.getAndAdd( -size ) > size ) { schedule(); } //todo что делать с исключениями? callback.call( buffer ); } private void schedule() { scheduledTask = executor.schedule( flushBufferTask, delay, timeUnit ); } public boolean isClosed() { return closed; } } //============================= public interface ICallback<T> { public void call( final T arg ); }
Это первый мой опыт в самостоятельном написании lock-free алгоритмов на CAS-примитивах, поэтому написал я этот код часа за два, а медитировал над ним дня 3. Но косяков так и не нашел -- все должно работать как задумано. Увлекательное это занятие -- обдумывать корректность таких алгоритмов, мозг работает явно на повышенных оборотах
18 августа 2010 г.
Clipboard
Несколько дней уже разбираюсь с задачей экспорта картинки из Матконструктора в офисный пакет. Хочется картинку выдавать векторную, чтобы в, скажем, Word можно было ее потом масштабировать и редактировать как удобно. Собственно, экспортировать картинку в какой-нибудь векторный формат не проблема -- библиотека FreeHEP дает сразу полдюжины вариантов, от SVG до родного виндового EMF. Вопрос в том, как объяснить буферу обмена, что тот набор, казалось бы, случайных байт, что в нем лежит -- это не данные датчика случайных чисел, а-таки EMF.
Оказывается, сделать это все-таки можно. Похоже, первыми на решение набрели разработчики JFreeChart (во всяком случае, остальные источники ссылаются на них как на оригинал). Внимание, сейчас будет фокус:
(источник)
Если я все правильно понял, волшебное заклинание sfm.addUnencodedNativeForFlavor(EMF_FLAVOR, "ENHMETAFILE"); объясняет джаве, что данные, маркированные как EMF_FLAVOR при передаче в системный буфер обмена никак не надо обрабатывать, надо отдать их туда как есть (массивом байт), и снабдить описанием типа "ENHMETAFILE". Интересно, какие еще типы можно так передавать? Пока я нашел только вариант, где удалось передать MathML.
Второй способ придумал автор Java Vector Cut and Paste Library. Финт ушами -- формат rtf поддерживается джавой из коробки, в том числе его передача через Clipboard. А rtf может служить контейнером для таких форматов как MACPICT, EMF и WMF. Через одно место, но уже неплохо -- в частности, MACPICT по его словам понимается как на Windows так и на Mac OS X. Но rtf не воспринимается, например, PowerPoint-ом -- в отличие от первого способа.
Оказывается, сделать это все-таки можно. Похоже, первыми на решение набрели разработчики JFreeChart (во всяком случае, остальные источники ссылаются на них как на оригинал). Внимание, сейчас будет фокус:
//на самом деле mime-type здесь не важен, //можно использовать image/emf, application/emf, image/x-mgx-emf, etc public static final DataFlavor EMF_FLAVOR= new DataFlavor("image/x-emf", "Enhanced Meta File"); static { // EMF graphics clipboard format try { final SystemFlavorMap sfm = (SystemFlavorMap)SystemFlavorMap.getDefaultFlavorMap(); sfm.addUnencodedNativeForFlavor(EMF_FLAVOR, "ENHMETAFILE");//seems to be a key command!! } catch(Exception e) { e.printStackTrace(); } }
(источник)
Если я все правильно понял, волшебное заклинание sfm.addUnencodedNativeForFlavor(EMF_FLAVOR, "ENHMETAFILE"); объясняет джаве, что данные, маркированные как EMF_FLAVOR при передаче в системный буфер обмена никак не надо обрабатывать, надо отдать их туда как есть (массивом байт), и снабдить описанием типа "ENHMETAFILE". Интересно, какие еще типы можно так передавать? Пока я нашел только вариант, где удалось передать MathML.
Второй способ придумал автор Java Vector Cut and Paste Library. Финт ушами -- формат rtf поддерживается джавой из коробки, в том числе его передача через Clipboard. А rtf может служить контейнером для таких форматов как MACPICT, EMF и WMF. Через одно место, но уже неплохо -- в частности, MACPICT по его словам понимается как на Windows так и на Mac OS X. Но rtf не воспринимается, например, PowerPoint-ом -- в отличие от первого способа.
4 августа 2010 г.
MAC-адрес в java
Периодически обнаруживаю в JDK функциональность, которую я там не ожидал. Например, сегодня узнал, что можно получить MAC-адрес сетевой карты вызовом NetworkInterface.getHardwareAddress(). Причем судя по документации, метод еще с 1.4 тянется. А я почему-то был уверен, что хрен там, а не MAC из java. Приятная неожиданность.
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. Странно.
Пришло. Даже 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). У меня смутное ощущение, что
Кстати, такая же подстава с 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, и упросить синхронизацию :)
Ну а еще таким образом можно сделать ссылку на объект 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 каждый раз при создании новой задачи. То есть вместо
мы имеем
Разумеется, "верхний" ForkJoinTask все равно нужно явно поместить выполняться в какой-то пул -- но внутренняя реализация ForkJoinTask.exec не имеет никакой информации о пуле -- она вообще пул не видит, она видит только задачи (ForkJoinTask). На мой взгляд, это очень удобно. Последний раз, когда я реализовывал асинхронный рекурсивный обход дерева, я сильно ругался на то, что мне все время приходится тащить с собой ссылку на ExecutorService. Т.е. именно для рекурсивных алгоритмов, как я вижу, такой подход становится особенно удобным.
Дополнение можно просто скачать как отдельную библиотеку. Возможно, оно будет включено в jdk 1.7. Я бы приветствовал :)
Статья Дога Ли с описанием исходного 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"
Класс 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" это ни разу не помогает что-либо понять.
7 июня 2010 г.
IntellJ IDEA 9 Community Edition
Она меня достала. Я не знаю, как нежно любимая мною компания JetBrains могла выпустить на рынок такое говно. Я не знаю, какие цели они при этом преследовали. Но на мой взгляд, это epic fail. Она дико тормозит, на совершенно скромных по размерам проектах. Она жрет память так, как будто рассчитывает, что я буду втыкать новые планки по мере ее желания. На P4 3.2Ghz, у нее в распоряжении 700Мб памяти -- и проект открывается 10 минут!!! Перерасчет индексов -- 10 минут! Разработчики -- да вы просто охуели!
Более того, она глючит чуть ли не каждый день. Это само по себе не так важно -- старые версии тоже подглючивали, безглючных программ не бывает. Но в старых версиях это ничему не мешало -- ну не выполнилась команда сейчас, выполнится в следующий раз. Они глючили безопасно -- а эта падла то настройки собьет, то JDK забудет.
Лучи поноса JetBrains.
P.S. Ах, да. Совсем забыл. Ее папка кэшей занимает 7Гб. Семь гигабайт! Разработчики, да вы что, совсем белены объелись? Чего там хранить на 7Гб??? content.dat.storageData -- 7.5Гб один файл. Неудивительно, что ей нужно 10 минут чтобы хотя бы прочитать его.
Более того, она глючит чуть ли не каждый день. Это само по себе не так важно -- старые версии тоже подглючивали, безглючных программ не бывает. Но в старых версиях это ничему не мешало -- ну не выполнилась команда сейчас, выполнится в следующий раз. Они глючили безопасно -- а эта падла то настройки собьет, то JDK забудет.
Лучи поноса JetBrains.
P.S. Ах, да. Совсем забыл. Ее папка кэшей занимает 7Гб. Семь гигабайт! Разработчики, да вы что, совсем белены объелись? Чего там хранить на 7Гб??? content.dat.storageData -- 7.5Гб один файл. Неудивительно, что ей нужно 10 минут чтобы хотя бы прочитать его.
Подписаться на:
Сообщения (Atom)