Несколько мыслей об оптимизации вообще, оптимизации Java-приложений и оптимизации для достижения low-latency.
Оптимизация вообще

кривая оптимизаций им. Алексея Шипилёва
Чаще всего до начала оптимизации мы находимся в зелёной зоне. Удаляя бредовый код и исправляя неэффективный, мы сравнительно легко повышаем производительность, снижая при этом сложность кода. Здесь мы достигаем пика красоты и простоты кода, но не исчерпали еще все возможности оптимизации.
Далее мы вступаем в жёлтую зону тщательной оптимизации. Здесь мы достигаем еще большего улучшения производительности, но уже за счет небольшого усложнения кода.
Далее мы попадаем в красную зону, где дальнешее улучшение производительности достигается за счёт тщательной отладки и профилирования кода с упором на оптимизацию. Дополнительные проценты улучшения производительности достигаются за счет еще большего усложенения кода.
Выходим за пределы красной зоны и достигаем Нирваны за счёт написания сложного кода, который уже смогут понять только несколько человек и то — после долгой дзен-медитации.
Отсюда вывод: при оптимизации надо знать, где на этой кривой остановиться: нужны ли вам эти последние проценты производительности, если это повлечет усложнение кода? И обратный вывод: если вы хотите выжать из своего кода последние капли производительности, вам неизбежно придется заплатить за это усложнением кода.
Конкретика: Оптимизация в Java …
Latency в несколько секунд: достигается легко и просто: методы небольшого размера, чистые абстракции, минимальное использование наследования, чистый код.
Latency в несколько сотен миллисекунд: общие практики хорошего кода: оптимальные структуры данных и алгоритмы, минимальная сложность кода, кэширование данных, пакетная обработка данных.
Latenecy в несколько десятков миллисекунд: специфический подход: оптимальный доступ к памяти и оптимальное использование кэша процессора, безблокировочные алгоритмы, асинхронная обработка, неиспользование кода, сохраняющего состояние, отображение файловой системы в память, тонкая настройка сборщика мусора и жизненного цикла объектов.
Latency в сотни микросекунд и меньше: очень специфические подходы: NUMA, large pages в ОС, избегать false sharing, обращать внимание на расположение структур данных в памяти, минимальное копирование данных из одной области памяти в другую (zero copy). Сборщик мусора легко может остановить ваше приложение более, чем на 1 миллисекунду, за меньшее время он просто не управится, значит — никакого сбора мусора (т.е. нужен такой код, который не создает мусор).
Latency в десятки микросекунд и меньше: достигаются переходом на аппаратный уровень: FPGA, ASIC.
… и low-latency
Еще одно правило, которое я вынес из просмотра множества презентаций и лекций и нашел подтверждение в собственной практике: нельзя превратить простое приложение путем оптимизации в low-latency приложение. Если ставится задача достигнуть минимального latency, приложение уже с самых первых строк кода должно писаться с учетом этого требования.
Для low-latency приложения используются совершенно особые подходы к написанию кода, особые решения по архитектуре приложения, особые методы его тестирования и измерения производительности. Проще переписать приложение с нуля, чем сначала написать неповоротливого монстра, а потом пытаться его оптимизировать. Это все равно, что попытаться заставить БелАЗ ездить со скоростью Ламборгини путем форсирования двигателя, тюнинга и оптимизаций подвески. «Запорожец» при всем желании, бюджете и настройках не сможет участвовать в гонках Formula 1. Если нужен гоночный болид для F1, строят именно гоночный автомобиль, а не переделывают в него «Запорожец». Знаменитое правило Дональда Кнута «преждевременная оптимизация — корень всех зол» здесь не применимо.
В заключение
Теперь, когда мы рассмотрели общие подходы к оптимизации, давайте рассмотрим в отдельной статье более подробно подходы оптимизации на всех системных уровнях.