2. Java Platform Performance BoF @
о чём у нас
• Отвечаем на вопросы
– В основном, предварительно собранные в разных местах Рунета
– Есть возможность задать вопрос прямо здесь
• Пишем на бумажке, передаём вперёд ;)
• Здесь и сейчас:
– Performance Engineering
– Benchmarking
– JIT
– Concurrency and synchronization
– Startup
• Не здесь или не сейчас, а на других сессиях:
– “Диагностика проблем и настройка GC”
#2, Oracle
4. Performance Engineering
абстрактно и отлично об отличиях в абстракциях
• Computer Science → Software Engineering
– Строим приложения по функциональным требованиям
– В большой степени абстрактно, в “идеальном мире”
• Теоретически неограниченная свобода – искусство!
• Можно строить воздушные зАмки
– Рассуждения при помощи формальных методов
• Software Performance Engineering
– “Real world strikes back!”
– Исследуем взаимодействие софта с железом на типичных данных
• Производительность уже нельзя оценить
• Производительность можно только измерить
– Естественно-научные методы
• Основываемся на эмпирических данных
#4, Oracle
5. Performance Engineering
первый шаг
• Классические ошибки первого шага
– “я вижу, что метод foo() реализован неэффективно”
– “по профилю видно, что метод bar() – самый горячий и занимает 5%”
– “по-моему, у нас тормозит БД, и необходимо перейти с DBX на DBY”
• Правильный первый шаг:
– Необходимо выбрать метрику
• ops/sec, transactions/sec
• время исполнения
• время отклика
– Убедиться в корректности метрики
• релевантна (учитывает реальный сценарий работы приложения)
• повторяема
ЦЕЛЬ → улучшение метрики!
#5, Oracle
6. Performance Engineering
анализ узких мест (tips)
• Низкая утилизация CPU
– Высокая дисковая, сетевая активность
– Конфликт блокировок
– Конфликт ресурсов ОС
– Слабая параллелизация приложения
• Высокая утилизация ядра ОС
– Частые блокировки
– Частое обращение к ОС
• Высокая утилизация CPU
– Неоптимальная архитектура приложения
– Неправильное использование API
– Неоптимизированные горячие методы
– Неоптимальные настройки GC
#6, Oracle
7. Performance Engineering
инструменты для анализа системы
Solaris Linux Windows Что смотрим
Сеть netstat, dtrace netstat perfmon количество
соединений, объем
трафика
Диск iostat, dtrace iostat perfmon количество обращений
к диску, задержка
Память vmstat, prstat, vmstat, top perfmon подкачка страниц,
dtrace размер памяти
Процессы ps, vmstat, ps, vmstat, top perfmon количество нитей,
mpstat, prstat, состояние нитей,
dtrace переключения
контекста
Ядро ОС mpstat, lockstat, vmstat perfmon kernel time, блокировки,
plockstat, dtrace, системные вызовы,
intrstat, vmstat прерывания ...
#7, Oracle
8. Performance Engineering
tools, tools, tools again, more tools
• VisualVM
– http://visualvm.dev.java.net
• JRockit Mission Control
– http://www.oracle.com/technetwork/middleware/jrockit/mission-control/index.html
• Sun Studio Analyzer
– http://www.oracle.com/technetwork/server-storage/solarisstudio/overview/index.html
• NetBeans Profiler
– http://www.netbeans.org
• DTrace
– http://www.oracle.com/technetwork/systems/dtrace/dtrace/index.html
• Ещё могут быть полезны:
– JProbe
– OptimizeIt
– YourKit
#8, Oracle
10. JVM tuning
настройка параметров JVM
• JVM сама подбирает оптимальные параметры своей
работы
– Server vs. Client
– Large pages (Solaris)
– CompressedOops (64-bit VM)
• Так что же настраивать?
– GC/Heap tuning
– -XX:+UseNUMA (Solaris, Linux)
– -XX+:UseLargePages (Linux, Windows)
• http://www.oracle.com/technetwork/java/javase/tech/largememory-jsp-137182.html
• Не забыть
– Использовать последнюю версию JDK
#10, Oracle
12. Benchmarking
на чём измерять производительность?
“микро” синтетические авто-сценарии реальные
бенчмарки бенчмарки реальных приложения
приложений
релевантность плохая средняя хорошая отличная
расходы на низкие средние средние высокие
разработку и
поддержку
расходы на низкие низкие средние высокие
запуск
сложность очень средняя средняя сложно
интерпретации сложно
результатов
#12, Oracle
13. Benchmarking
наша стратегия
• Сделать реальные приложения быстрее
– Но, слишком сложно поддерживать и запускать
– Поэтому...
• Сфокусироваться на синтетических бенчмарках и авто-
сценариях
– SPECjbb*, SPECjvm*, SPECjAppServer*, SPECjEnterprise*
– Glassfish, Weblogic, NetBeans, и т.п.
– Большие in-house нагрузки
• Пользоваться микробенчмарками с опаской
– Иногда от них никуда не деться
– Очень легко написать, очень легко запустить
– Очень легко “выстрелить себе в ногу”
– ...что чуть ли не каждую неделю демонстрируется в профильных
блогах
#13, Oracle
14. Benchmarking
итак, вы решили написать свой бенчмарк
• Опасайтесь типичных ошибок
• Дизайн эксперимента
– Что хотим измерить?
– Каким способом будем измерять?
• Реализация эксперимента
– Отсутствие прогрева, “мёртвый код”, etc.
– Многопоточность, утилизация
– Контроль переменных: результат воспроизводится?
– Контроль переменных: результат вообще зависит от входов?
• Интерпретация результатов
– Что в итоге измерили?
• Почему мой бенчмарк не может работать быстрее?
– Значим ли результат?
• 1000 ops/sec против 1050 ops/sec – прирост или нет?
#14, Oracle
16. JIT
факты
• … есть
• … работает
• … работает хорошо
• … знает о железе всё:
– Количество и тип CPU
– Поддерживаемые инcтрукции (SSEx, AVX, VIS)
– Топологию памяти (в т.ч. размеры кэшей и их характеристики)
• … знает о приложении много всего:
– Иерархию загруженных классов
– Актуальную статистику создания объектов
– Горячий код
– Какие ветвления исполнялись
– Какие значения использовались
– Многое другое
• … не боится использовать эти знания для компиляции
#16, Oracle
18. JIT
performance urban legends
final дает лучшую
производительность
копируйте поля в
локальные переменные!
volatile запрещает JIT
Reflection –
оптимизировать
дорого
доступ к полю
вызов виртуального
метода - дорого избегайте get/set вручную вылизанный метод
методов внутри лучше аналога из classlib
самого класса
immutable классы –
плохо вручную выполненый
native методы дорогие, System.arraycopy() inline – хорошо
нативный – значит ...
Java медленна потому, что нельзя вручную создание объектов дорого –
выключить проверку выхода индекса за используйте Object pooling
границы массива
#18, Oracle
19. JIT
как писать код
• Используйте стандартные библиотеки
– Зачем писать собственный стандартный контейнер?
• Используйте высокоуровневый API:
– java.util.*
– java.util.concurrent.*
– NIO, NIO.2
– вообще библиотеки
• Код должен правильным и понятным
– Сначала правильно
– Потом алгоритмически “быстро”
– Код не должен быть JIT-oriented
• Правильно используйте возможности языка
– EPIC FAIL: штатная передача управления exception'ами
– FAIL: Возврат “исключения” через return <код_ошибки>
– FAIL: int вместо Enum или boolean
– ...
#19, Oracle
20. JIT
для любопытных
Как получить ассемблерный код метода?
– Обычным дебаггером ;)
– JVMTI
– -XX:+PrintAssembly
• http://wikis.sun.com/display/HotSpotInternals/PrintAssembly
#20, Oracle
22. Concurrency
с чем его есть
• Только мы научились программировать, новая беда
– Новое входное данное в программе: время
– Подавляющее большинство concurrency-багов – гейзен-баги!
– TDD “отдыхает”
• Читаем хорошие книги
– “Учиться, учиться и ещё раз учиться” (с)
– “Java Concurrency in Practice”
– “The Art Of Multiprocessor Programming”
– “JSR 133 Cookbook”
– “A Little Book of Semaphores”
• Пользуемся высокоуровневыми примитивами
– java.util.concurrent.*
– … или другими, построенными на их основе
– … при условии, что знаем, как их можно композировать без потери корректности
– … при условии, что вообще знаем, как показать корректность
– … при условии, что понимаем, сколько стоит тот или иной подход
– … при условии, что обладаем 42-ухкратным запасом терпения и усидчивости
#22, Oracle
31. Startup
длинные приложения
• Важно только для коротких приложений
– Чем дольше работает приложение, тем меньше удельные затраты на
загрузку классов и компиляцию
• Пример: 8 часа работает IntelliJ IDEA 10.x:
– 26.600 классов загружено
– 5315 методов скомпилировано
• Загрузка классов:
– Всего потрачено 202 с., ~0.7% общего времени
– 10 мсек на класс
• Компиляция:
– Всего потрачено 112 с., ~0.03% общего времени
– 20 мсек на метод
#31, Oracle
32. What's Next
• Послушать ещё?
– JavaOne Moscow (апрель 2011)
• Проблема со слайдами?
– Найдите нас на конференции
– Напишите нам письмо
• Проблема с JDK?
– http://openjdk.java.net
– Задайте вопрос в OpenJDK
– ...а лучше сделайте патч и дайте его в OpenJDK на review
#32, Oracle
35. Performance Engineering
итеративный подход
Измерения
(эксперименты)
Старт Разработка Сбор и обработка данных
(кодирование решений) (профилировка, статистика)
Поиск решений Анализ узких мест
(прототипирование)
Важно:
– Одно изменение за цикл!
– Документировать все изменения
#35, Oracle
36. Performance Engineering
анализ узких мест
• Что ограничивает скорость работы приложения?
• CPU
• Ядро ОС
• I/O (Сеть, Диск)
#36, Oracle
37. Performance Engineering
”нисходящий” метод поиска узких мест
• Уровень системы
– Сеть
– Диск
– Database
– Операционная система
– Процессор/память
• Уровень приложения
– Блокировки, синхронизация
– Execution Threads
– API
– Алгоритмические проблемы
• Уровень JVM
– Выбор JVM
– Heap tuning
– JVM tuning
#37, Oracle
38. New Features,
Compatibility,
Support, and beyond
#38, Oracle
39. Java 7, Java 8
что ожидать в области производительности
• Java 7
– invokedynamic
– NIO.2
– Concurrency and Collection updates (Fork/Join)
– XRender pipeline for Java2D (client)
• Java 8 (или позже)
– Модульность
– λ-выражения (замыкания)
– Collection updates (filter, map, reduce)
#39, Oracle
40. Обратная совместимость
• Мешает ли улучшать производительность JVM?
– Да, поэтому иногда расширяемся:
• invokedynamic
• Модульность
• Стоит ли расширяться по первому требованию?
– Нет, развитие JVM/JIT, реализация новых методов оптимизации
позволяет получить бОльший выигрыш
• Вложенные классы
• Reflection
#40, Oracle
41. Лучшая ОС для Java
угадайте, какая?
Solaris
• “Engineered Together”
• Высокопроизводительный TCP/IP стек
– low-latency
– up to 50% faster
• DTrace
– мониторинг
• NUMA
– MPO, Memory Placement Optimization
• Large Pages
– Автоматическая аллокация
– Разные размеры
#41, Oracle
42. Benchmarking
ближе к делу, пример
• Computer Language Benchmark Game
– http://shootout.alioth.debian.org/
– Самый известный полигон для расстрела невинных ног
• Тест “PiDigits”
– Вычислить первые N цифр в десятичном разложении π
– Вычисляется приближение π специальным рядом
• Требуется arbitrary precision для представления рациональных
членов ряда
– Потенциально параллелизуема
– Две версии Java-теста:
• Текущая использует GNU MP, внутренне параллельна, 240 строк
кода, Java + нативные врапперы + GMP
• Предыдущая использует BigInteger, не параллельна, 130 строк
кода, plain Java
#42, Oracle
43. Benchmarking
ближе к делу, пример
• Computer Language Benchmark Game
– http://shootout.alioth.debian.org/
– Самый известный полигон для расстрела невинных ног
• Тест “PiDigits”
– Вычислить первые N цифр в десятичном разложении π
– Вычисляется приближение π специальным рядом
• Требуется arbitrary precision для представления рациональных
членов ряда
– Потенциально параллелизуема
– Две версии Java-теста:
• Текущая использует GNU MP, внутренне параллельна, 240 строк
кода, Java + нативные врапперы + GMP
• Предыдущая использует BigInteger, не параллельна, 130 строк
кода, plain Java
#43, Oracle
44. Benchmarking
ближе к делу, пример
• Метрика: время исполнения
– time java -server pidigits 1000
• “Прогреем”:
pidigits.javasteady:
public static void main(String[] args){
pidigits m = new pidigits(Integer.parseInt(args[0]));
for (int i=0; i<65; ++i) m.pidigits(false);
m.pidigits(true);
}
#44, Oracle
45. Benchmarking
ближе к делу, пример
• Метрика: время исполнения
– time java -server pidigits 1000
• “Прогреем”:
pidigits.javasteady:
public static void main(String[] args){
pidigits m = new pidigits(Integer.parseInt(args[0]));
for (int i=0; i<65; ++i) m.pidigits(false);
m.pidigits(true);
}
pidigits.java:
public static void main(String[] args){
pidigits m = new pidigits(Integer.parseInt(args[0]));
// for (int i=0; i<19; ++i) m.pidigits(false);
m.pidigits(true);
}
#45, Oracle
46. Benchmarking
в век многоядерных систем
• Обычная платформа
– 2x2 Intel i5 2.6 Ghz, Ubuntu 10.10 i686, JDK 6u25
• Три варианта эксперимента:
– 1 экземпляр бенчмарка
• по “методологии CLBG” (+ корректный прогрев)
• GMP будет использовать 1x4=4 потока
• BI будет использовать 1x1=1 поток
– 4 экземпляра бенчмарка
• чтобы утилизировать все ядра = снормировать на систему
• GMP будет использовать 4х4=16 потоков
• BI будет использовать 4x1=4 потока
– 16 потоков
• ради одинаковой нагрузки на OS = снормировать на систему+OS
• GMP будет использовать 4х4=16 потоков
• BI будет работать в 16x1=16 потоков
• Какой из них наиболее релевантен?
#46, Oracle
47. Benchmarking
а вот и данные
• Метрика: операции в секунду
– 20 итераций
– [a; b] – доверительный интервал на 95%
1 экземпляр 4 экземпляра 16 потоков
“GMP”
8.84 13.28 13.28
[8.69; 8.99] [12.86; 13.71] [12.86; 13.71]
“BigInteger”
6.21 13.46 14.34
[6.19; 6.24] [13.35; 13.56] [14.21; 14.46]
#47, Oracle
48. Concurrency
элементная база
• OS Threading
– мьютексы
• mutex_lock()/mutex_unlock()
– conditional waits
• cond_wait()/cond_signal()
• WaitForSingleObject
• Compare-and-Swap (CAS)
– CAS(x1, x2, x3) = { if (x1 == x2) { x1 = x3 }; }
– атомарная операция, поддерживаемая в “железе”: из нескольких
одновременных CAS'ов успешно завершается только один
– Миф: локальный CAS блокирует шину, и стоит больше на
многопроцессорных системах
– Факт: глобальный CAS требует трафика на шине
http://blogs.sun.com/dave/entry/biased_locking_in_hotspot
#48, Oracle
49. Concurrency
atomics
• java.util.concurrent.Atomic*
– обеспечивают атомарные операции над примитивами и указателями
– альтернатива: synchronized {} или Lock'и
• Трюк в использовании CAS'а:
– Изменение состояния атомика делается при помощи одного CAS'а
– Чтение состояния не требует CAS'а
mov %ecx,%edx
mov 0x8(%ecx),%eax
public final int incrementAndGet() { lea 0x8(%ecx),%edi
for (;;) { mov %eax,%ecx
int current = get(); inc %ecx
int next = current + 1; lock cmpxchg %ecx,(%edi)
if (compareAndSet(current, next)) mov $0x0,%ebx
return next; jne [ok]
} mov $0x1,%ebx
} test %ebx,%ebx
je [ok]
#49, Oracle
50. Concurrency
volatile
• Volatile определяет порядок чтения-записей в поле
– Точная семантика определена в Java Memory Model
• НЕ обеспечивает атомарности
– Реализуется расстановкой барьеров
• Какие из них вставятся в код, зависит от Hardware Memory Model
• Эффект барьера зависит от HMM
PUSHL EBP
SUB ESP,8 push %ebp
MOV EBX,[ECX + #12] sub $0x8,%esp
MEMBAR-acquire mov 0xc(%ecx),%ebx
MEMBAR-release inc %ebx
INC EBX mov %ebx,0xc(%ecx)
MOV [ECX + #12],EBX lock addl $0x0,(%esp)
MEMBAR-volatile add $0x8,%esp
LOCK ADDL [ESP + #0], 0 pop %ebp
ADD ESP,8 test %eax,0xb779c000
POPL EBP ret
TEST PollPage,EAX
RET
http://g.oswego.edu/dl/jmm/cookbook.html
#50, Oracle
51. Concurrency
intrinsic synchronization
• synchronized(object) { }
• 4 состояния:
– Init
• Начальное “неопределённое” состояние
– Biased
• Захватывается одним “владеющим” потоком, нет конфликтов
• Захват владельцем: проверка на threadID
• Захват не-владельцем: переход либо в Biased, либо в Thin
– Thin
• Захватывается несколькими потоками, но конфликтов нет
• Захват: CAS
• Конфликтный захват: переход в Fat
– Fat
• Захватывается несколькими потоками, конфликт на блокировке
• Вызов примитива синхронизации из ОС
#51, Oracle
52. Concurrency
java.util.concurrent.Lock
• Построены на базе j.u.c.AbstractQueueSynchronizer
– Использует атомики
• CAS
– Использует Unsafe.park()/unpark()
• интринзики для cond_wait()/cond_signal()/WaitForSingleObject()
• ReentrantLock
– По семантике эквивалентен synchronized {}
• Рекурсивный захват: проверка на threadID
• Ставит потоки во внутреннюю очередь и делает park()
– Non-Fair (default)
• Не гарантирует отсутствие starvation
• Barging FIFO (CAS)
• Лучшая производительность
– Fair
• Гарантирует отсутствие starvation
• FIFO
• Честность в обмен на производительность
http://gee.cs.oswego.edu/dl/papers/aqs.pdf
#52, Oracle
53. Concurrency
атомарный счётчик
private AtomicInteger atomic = new AtomicInteger();
private ReentrantLock lock = new ReentrantLock();
private final Object intrinsicLock = new Object();
private int primCounter = 0;
@GenerateMicroBenchmark
public void testAtomicInteger() {
atomic.incrementAndGet();
}
@GenerateMicroBenchmark
public void testReentrantLock() {
lock.lock();
primCounter++;
lock.unlock();
}
@GenerateMicroBenchmark
public void testIntrinsicLock() {
synchronized (intrinsicLock) {
primCounter++;
}
}
#53, Oracle
55. Concurrency
ReentrantLock vs. synchronized
• Семантика одинакова
– Требования к видимости памяти
– Рекурсивный
• Плюсы j.u.c.RL
– Очередь потоков держится на стороне JVM
• опционально, FIFO-политика при захвате-освобождении
• позволяет быть “честным” на любой платформе
– Barging FIFO policy
• lock() может быть сразу удовлетворён, даже если в очереди есть
потоки
• сильно улучшает производительность при конфликте блокировок
– Допускается несколько Condition
• Минусы j.u.c.RL
– Нет scope'ов, требуется ручной unlock() через finally
• Должно быть проще с ARM в JDK7
#55, Oracle