Показаны сообщения с ярлыком рабочее. Показать все сообщения
Показаны сообщения с ярлыком рабочее. Показать все сообщения

10 июня 2024 г.

Отладка через статистику

JB, где я сейчас работаю, отличается от моих предыдущих мест работы тем, что мы делаем продукт для широкой аудитории – миллионы пользователей – и часть этой аудитории разрешает нам собирать с IDE всякую полезную статистику.

Это придает разработке непривычный, но и очень интересный аспект: миллионы пользователей, причем десктопных – это огромное разнообразие и hardware/software конфигураций, и сценариев работы. В этом множестве комбинаций конфигураций и сценариев работы случаются самые странные и неочевидные ошибки – некоторые из них очень сложно воспроизвести. Тем более сложно, что диагностика, которую удается получить от пользователей, обычно довольно скромная.

30 января 2022 г.

Are You Sure You Want to Use MMAP in Your Database Management System?

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

В статье Are You Sure You Want to Use MMAP in Your Database Management System?1 авторы аргументируют, что это удобство – иллюзия. Memory mapped files только выглядят удобно, у них есть неочевидные на первый взгляд недостатки, аккуратный обход которых потребует усилий, сравнимых с самостоятельной реализацией пула страниц.

Самая интересная и неожиданная для меня часть: одним из крупных преимуществ mmap часто называют производительность – отображение файлов в память позволяет уменьшить количество syscalls, и избежать копирования kernel -> user space, и потому должно быть быстрее обычных методов IO.

Авторы показывают, что и это тоже иллюзия: на скоростях современных SSD/NVM и при современных объемах данных пропускная способность чтения данных с диска через отображаемые в память файлы в 2-20 раз уступает пропускной способности прямого чтения в обход файлового кэша. Происходит это потому, что на больших скоростях и больших объемах ОС приходится очень часто загружать и выгружать страницы, и узким местом становится сама машинерия виртуальной памяти – обновление таблицы страниц (page table) и выгрузка страниц на диск (page eviction).

4 августа 2021 г.

Никто не читает документацию

В комментариях к передыдущему посту я спорил с этим утверждением, но потом порефлексировал над своим собственным опытом, и передумал: нет, и правда, почти никто.

Если аккуратнее: никто не любит читать документацию, никто не любит искать ответы на свои вопросы в документации.

Сколь бы понятную и логичную документацию я не написал – скорее всего, люди сами ее не отыщут и не прочитают. Скорее всего, люди все равно будут стараться задать вопросы мне лично, если я доступен, или сделают по своему разумению, если нет.

24 мая 2021 г.

Когда имеет смысл передавать IO в отдельный поток?

Допустим, у нас есть простая система, которая принимает запросы из сети, как-то их обрабатывает ("бизнес-логика"), и отправляет результат назад, в сеть. Мы заинтересованы в быстром отклике (=latency), а отправка – это IO, так что возникает идея ее снести в отдельный поток.

Но тогда придется передавать данные из основного потока в поток отправки – а межпоточная коммуникация это какие-то накладные расходы (копирование, инструкции синхронизации, т.п.)

Стоит ли вообще игра свеч, и если стоит – то когда?

20 августа 2018 г.

Оценки квантилей распределений потока данных

Последнюю неделю копаю алгоритмы оценок для квантилей выборок. Началось все с префетчинга: есть хранилище данных на диске, часть из них кэшируется в памяти, и надо бы успевать кэшировать предварительно с такой скоростью, чтобы клиентам не приходилось ожидать ввода-вывода. Доступ последовательный, так что все сводится к вопросу "сколько страниц загрузить в текущем раунде упреждающего чтения". И обычно в таких случаях я вижу что-то вроде

2 февраля 2016 г.

Массовые расстрелы и духовная практика смирения


— Микола, ты слыхал как профессиональные программисты тм наш newValue кличут?
— Не, а как?
— "о"!
— Да ну! Вот же ж нелюди, поубивал бы гадов!

Разбирался сегодня в одном из наших сервисов, который использует довольно старую версию trove (2.1.0)

4 февраля 2014 г.

How fast the logger could be?

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

Задача взялась вполне себе из практики: есть приложение, которое зарабатывает бабло наносит пользу человечеству. У этого приложения есть legacy-версия, которая уж сколько-то лет в работе, и есть новая, с 0 переписанная (в основном мной) версия, которая сейчас готовится ее сменить на боевом посту. Разумеется, возникает масса вопросов вида "а почему вот в новой версии у этой транзакции результат чуть не такой, как в старой?". И приходится много времени шароебиться по логам, выясняя, как и что происходило. И, конечно, регулярно думаешь, что вот если бы еще здесь, здесь, и вот здесь дополнительно ключевые параметры сбрасывать в лог, то расследование было бы в разы быстрее и проще.

5 октября 2012 г.

Thread affinity binding

По следам выступления на jug.ru:

Меня спрашивали, как я задаю affinity потокам в яве в бенчмарках. Начнем с того, что из чистой явы это сделать нельзя. Но если нельзя, а очень хочется — то таки можно. Долгое время я пользовался на скорую руку сварганенным методом, подсмотренным где-то на stackoverflow. Потом какое-то время участвовал в разработке библиотеки Java-Thread-Affinity, от Питера Лоурея. В принципе, она довольно неплоха, и я могу ее вполне рекомендовать.

15 сентября 2012 г.

AtomicFieldUpdater optimized

AtomicXXXFieldUpdater — чудесные классы, открывающие значительную часть магии Unsafe без проблем с безопасностью и переносимостью. К сожалению, есть некоторая цена. Вот цитата из AtomicLongFieldUpdater.CASUpdater:

public void lazySet(T obj, long newValue) {
  if (obj == null || obj.getClass() != tclass || cclass != null) 
    fullCheck(obj);
           
  unsafe.putOrderedLong(obj, offset, newValue);
}
private void ensureProtectedAccess(T obj) {
  if (cclass.isInstance(obj)) 
    return;
  throw new RuntimeException (
        new IllegalAccessException("Class " + cclass.getName() + 
            " can not access a protected member of class " + tclass.getName() +
            " using an instance of " + obj.getClass().getName()
        )
  );
}
private void fullCheck(T obj) {
  if (!tclass.isInstance(obj))
    throw new ClassCastException();
  if (cclass != null)
    ensureProtectedAccess(obj);
}  

Выглядит все довольно страшно, хотя на практике в большинстве случаев все закончится на первых трех проверках: (obj == null || obj.getClass() != tclass || cclass != null). Эффект от них невелик, в моих бенчмарках замена AtomicUpdater на Unsafe давала 15-20% максимум в очень нагруженном конкурентном коде. По-видимому, дело в том, что результат этих проверок в правильно написанном коде всегда одинаков (false), поэтому предсказатель ветвлений в процессоре довольно быстро сообразит, какая из веток реализуется со 100% вероятностью, и будет спекулятивно выполнять код дальше. Тем не менее, иногда не хочется терять и этих процентов.

Хорошая новость состоит в том, что AtomicLongFieldUpdater — абстрактный класс, и никто не мешает вам написать свою реализацию. Например, взять за основу AtomicLongFieldUpdater.CASUpdater, и просто выбросить все "лишние" проверки. Результат будет практически drop-in-replacement для большинства сценариев использования.

Разумеется, у меня сразу возникла мысль сделать какую-то свою фабрику, на замену AtomicLongFieldUpdater.newUpdater(...), которая по-умолчанию делегировала бы к AtomicLongFieldUpdater.newUpdater, а при указании специального свойства -Djava.concurrent.run-faster начинала бы использовать оптимизированную версию. Плохая новость состоит в том, что так не получается: в конструкторе AtomicFieldUpdater-а проверяется, что вы можете создать AtomicUpdater только для того поля, которое вы и так можете видеть из создающего кода. Если я собираюсь обновлять private поле, то AtomicUpdater для него я смогу создать только изнутри этого же класса. И значит выбор реализации AtomicUpdater-а придется делать внутри каждого класса, его использующего.

24 октября 2011 г.

Open Source: Retryer

У меня наконец-то дошли руки выложить что-то из своих работ в ОС. Библиотечка, созданная изначально еще во времена работы над DataGuard-ом, потом допиленная уже на нынешней работе -- Retryer.

Задача, которую она решает: часто бывает так, что какой-то код может не выполниться успешно "без причины", и если такое происходит, нужно просто попытаться выполнить его еще несколько раз -- возможно, с различными паузами между попытками. Чаще всего такое бывает с кодом, который дергает какие-то внешние сервисы -- тут может быть куча временных/случайных причин для сбоев: временная перегрузка сети, временная неработоспособность сети, временная перегрузка сервиса, перезапуск сервиса... Какую-то степень устойчивости к этим факторам предоставляют сами протоколы (например, тот же TCP какие-то сетевые сбои обрабатывает незаметно для пользователя) но этой степени часто недостаточно для целей конкретного приложения.

Вот, собственно, задачу "стучаться пока не откроют" библиотека и выполняет. Что с ней можно делать:
  1. Задать код, который нужно выполнять "пока не получится" (==он завершится без исключений)
  2. Задать стратегию задержки (back off) между отдельными попытками. Есть библиотека простых стратегий (без задержки, фиксированная пауза, линейно растущая пауза, экспоненциально растущая пауза...) + билдер/dsl для их комбинации. Например:
    Backoff
                .withExponentialGrowingDelay()
                .startingWithDelay( 1, TimeUnit.SECONDS )
                .maxTryes( 5 )
                .maxDelay( 10, TimeUnit.SECONDS )
                .build()
    
  3. Задать "фатальные ошибки" -- т.е. при некотором классе ошибок дальнейшее продолжение попыток смысла не имеет. Например, если сервис выбросил исключение ConnectionTimeout -- имеет смысл попробовать еще разок позже, авось сеть заработает. Но если сервис выкинул IncorrectProtocolVersionException, то дальше пробовать смысла нет -- мы явно ломимся куда-то не туда

Пример использования (запрос по URL-у):

private static final String URL_TO_QUERY = "http://google.com/?q=Retryer";

public static String simpleQuery( final String urlString ) throws Exception {
    final URL url = new URL( urlString );
    final InputStream is = url.openStream();
    try {
        final InputStreamReader r = new InputStreamReader( is, "ISO-8859-1" );
        try {
            return CharStreams.toString( r );
        } finally {
            r.close();
        }
    } finally {
        is.close();
    }
}

....

public static String queryRetryableComplex( final String urlQuery ) throws Exception {
    return new Retryer().doRetryable(
            new IRetryableTask<String, Exception>() {
                public String execute( final int tryNo ) throws Exception {
                    return simpleQuery( urlQuery );
                }

                public boolean isFatalReason( final int tryNo,
                                                                     final Throwable reason ) {
                    return (reason instanceof MalformedURLException);
                }
            },
            Backoff
                        .withExponentialGrowingDelay()
                        .startingWithDelay( 1, TimeUnit.SECONDS )
                        .maxTryes( 5 )
                        .maxDelay( 10, TimeUnit.SECONDS )
                        .build()
    );
}

здесь мы будем пытаться запросить гугл с экспоненциально растущей задержкой, начиная с 1 секунды, но не более 10 секунд, и не более 5 раз подряд. При этом мы не будем повторять запрос если он выбросит MalformedURLException (какой смысл-то?).

С удовольствием послушаю всяческую ругань. Обещаю активно и интересно ругаться в ответ :)

21 декабря 2010 г.

MySQL HandlerSocket

HandlerSocket -- это расширение (плагин) для MySQL, позволяющий делать простые CRUD запросы непосредственно к движку хранения данных MySQL, обходя слой SQL. По словам автора, производительность такого решения в случае, когда данные БД влезают в память выше, чем производительность memcached. При том, что мы сохраняем возможность работать с теми же данными через SQL.

Здесь краткое введение в тему.

А здесь -- драйвер для java. Осталось только найти повод его где-нибудь попользовать

18 августа 2010 г.

Clipboard

Несколько дней уже разбираюсь с задачей экспорта картинки из Матконструктора в офисный пакет. Хочется картинку выдавать векторную, чтобы в, скажем, Word можно было ее потом масштабировать и редактировать как удобно. Собственно, экспортировать картинку в какой-нибудь векторный формат не проблема -- библиотека FreeHEP дает сразу полдюжины вариантов, от SVG до родного виндового EMF. Вопрос в том, как объяснить буферу обмена, что тот набор, казалось бы, случайных байт, что в нем лежит -- это не данные датчика случайных чисел, а-таки EMF.

Оказывается, сделать это все-таки можно. Похоже, первыми на решение набрели разработчики 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-ом -- в отличие от первого способа.

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. Странно.

9 июля 2010 г.

Не пущать!

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

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

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

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 минут чтобы хотя бы прочитать его.

4 апреля 2010 г.

Вышел jdk 1.6 под Snow Leopard

В последнем обновлении наконец-то появилась шестая джава под мак (SL).

Тут же появились и проблемы с ней. java.io.IOException: Keystore was tampered with, or password was incorrect -- вылетает при работе с SSL ссылка

Корень проблем оказался прост -- эппл сменила (скорее всего, просто по ошибке) дефолтный пароль на хранилище сертификатов с changeit на changeme. Если сменить его обратно -- все заработает как встарь. Например, вот так:

sudo -i
cd /System/Library/Frameworks/JavaVM.framework/Home/lib/security/
keytool -storepasswd -keystore cacerts

1 апреля 2010 г.

Кодировка консоли в java

Кодировка консоли в Win не совпадает с системной кодировкой (которая Charset.defaultCharset()). А знать ее часто бывает полезно. Например, запускаемые внешние процессы могут возвращать описания ошибок на русском -- и фиг вы их прочитаете. Конечно, можно просто зашить кодировку Cp866 для русской версии -- но это неправославное решение. А ну как другая локаль будет? Хотелось бы иметь возможность спросить, в какой кодировке работает наша консоль.

Я долгое время был уверен, что такого метода в jdk не предусмотрено. Но, совершенно случайно, раскапывая исходники стандартной библиотеки обнаружил решение для Sun JDK 1.6+ -- приватный статический метод java.io.Console.encoding()

final Class<Console> clazz = Console.class;
final Method method = clazz.getDeclaredMethod( "encoding", new Class[0] );
method.setAccessible( true );
final String encoding = ( String )method.invoke( null );


возвращает на моей системе Cp866, как положено.

23 марта 2010 г.

Scala

Первое впечатление от Scala IDEA plugin: "как же медленно оно компилируется!" Натуральный С++ камбэк. Успеваю заметить штук 5 стадий компиляции. Все-таки javac, пересобирающий проект из 500 файлов за 20 секунд сильно развращает.

12 марта 2010 г.

Serialization libraries

Наткнулся на интересный "проект" по сравнению производительности различных способов/библиотек сериализации/десериализации в джава. Comparing varius aspects of Serialization libraries on the JVM platform Мало того, что лишний раз подивился на разницу в скорости стандартной сериализации с Externalizable, так еще и узнал о куче интересных библиотек. Например, Kryo выглядит вполне подходящей заменой стандартному механизму -- быстрее, нагляднее, архитектурно изящнее (механизмы сериализации настраиваются отдельно от сериализуемых объектов -- в отличие от стандартного метода, где объект в любом случае сам задает свой метод сериализации, и он может быть только один). А JSON Marshaller я собираюсь рассмотреть на место XStream в DataGuard -- все равно XStream-ский json даже после тщательной доработки напильником периодически генерирует ересь. Более того, в json marshaller есть и замена для org.json.* -- по их словам она более быстрая и более удобная.

11 марта 2010 г.

Syntax highlighter

Нашел себе подсветку синтаксиса для скриптов в блоге.

В теле поста пишем
<pre class="brush: java">
public static void main( final String[] args){
final int count = args.length;
System.out.println("count: "+count);
}
</pre>




и в результате получаем:
public static void main( final String[] args){
    final int count = args.length;
    System.out.println("count: "+count);
}


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

Подробности здесь: Awesome syntax highlighting