Накладные расходы на создание streamов Java

Обычная мудрость говорит нам о том, что приложения большого объема предприятия Java должны использовать пул streamов, предпочитая создавать новые рабочие streamи. Использование java.util.concurrent делает это простым.

Однако существуют ситуации, когда пул streamов не подходит. Конкретным примером, с которым я сейчас борюсь, является использование InheritableThreadLocal , которое позволяет передавать переменные ThreadLocal для любых порожденных streamов. Этот механизм прерывается при использовании пулов streamов, поскольку рабочие streamи, как правило, не генерируются из streamа запросов, но уже существуют.

Теперь есть пути вокруг этого (локаторы streamов могут быть явно переданы), но это не всегда правильно или практично. Самое простое решение – создать новые рабочие streamи по требованию и позволить InheritableThreadLocal выполнять свою работу.

Это возвращает нас к вопросу – если у меня есть сайт с большим объемом, где streamи пользовательского запроса порождают полдюжины рабочих streamов каждый (т. Е. Не используют пул streamов), будет ли это проблемой для JVM? Мы потенциально говорим о создании нескольких сотен новых streamов каждую секунду, каждая из которых длится менее секунды. Могут ли современные JVM оптимизировать это? Я помню дни, когда объединение объектов было желательно в Java, потому что создание объекта было дорогостоящим. С тех пор это стало ненужным. Мне интересно, если это относится к пулу streamов.

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

Примечание: мудрость использования локаторов streamов здесь не проблема, поэтому, пожалуйста, не предлагайте, чтобы я их не использовал.

Вот пример микробизнеса:

 public class ThreadSpawningPerformanceTest { static long test(final int threadCount, final int workAmountPerThread) throws InterruptedException { Thread[] tt = new Thread[threadCount]; final int[] aa = new int[tt.length]; System.out.print("Creating "+tt.length+" Thread objects... "); long t0 = System.nanoTime(), t00 = t0; for (int i = 0; i < tt.length; i++) { final int j = i; tt[i] = new Thread() { public void run() { int k = j; for (int l = 0; l < workAmountPerThread; l++) { k += k*k+l; } aa[j] = k; } }; } System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms."); System.out.print("Starting "+tt.length+" threads with "+workAmountPerThread+" steps of work per thread... "); t0 = System.nanoTime(); for (int i = 0; i < tt.length; i++) { tt[i].start(); } System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms."); System.out.print("Joining "+tt.length+" threads... "); t0 = System.nanoTime(); for (int i = 0; i < tt.length; i++) { tt[i].join(); } System.out.println(" Done in "+(System.nanoTime()-t0)*1E-6+" ms."); long totalTime = System.nanoTime()-t00; int checkSum = 0; //display checksum in order to give the JVM no chance to optimize out the contents of the run() method and possibly even thread creation for (int a : aa) { checkSum += a; } System.out.println("Checksum: "+checkSum); System.out.println("Total time: "+totalTime*1E-6+" ms"); System.out.println(); return totalTime; } public static void main(String[] kr) throws InterruptedException { int workAmount = 100000000; int[] threadCount = new int[]{1, 2, 10, 100, 1000, 10000, 100000}; int trialCount = 2; long[][] time = new long[threadCount.length][trialCount]; for (int j = 0; j < trialCount; j++) { for (int i = 0; i < threadCount.length; i++) { time[i][j] = test(threadCount[i], workAmount/threadCount[i]); } } System.out.print("Number of threads "); for (long t : threadCount) { System.out.print("\t"+t); } System.out.println(); for (int j = 0; j < trialCount; j++) { System.out.print((j+1)+". trial time (ms)"); for (int i = 0; i < threadCount.length; i++) { System.out.print("\t"+Math.round(time[i][j]*1E-6)); } System.out.println(); } } } 

Результаты на 64-битной Windows 7 с 32-разрядной клиентской виртуальной машиной Java 1.6.0_21 Sun на Intel Core2 Duo E6400 @ 2.13 ГГц следующие:

 Number of threads 1 2 10 100 1000 10000 100000 1. trial time (ms) 346 181 179 191 286 1229 11308 2. trial time (ms) 346 181 187 189 281 1224 10651 

Выводы. Два streamа выполняют работу почти в два раза быстрее, чем один, как и ожидалось, поскольку мой компьютер имеет два ядра. Мой компьютер может генерировать почти 10000 streamов в секунду, то есть накладные расходы на создание streamов составляют 0,1 миллисекунды . Следовательно, на такой машине несколько сотен новых streamов в секунду создают незначительные накладные расходы (что также можно увидеть, сравнивая числа в столбцах для 2 и 100 streamов).

Прежде всего, это, конечно же, будет зависеть от того, какую именно JVM вы используете. ОС также будет играть важную роль. Предполагая, что Sun JVM (Hm, мы все еще называем это?):

Одним из основных факторов является память стека, выделенная для каждого streamа, которую вы можете настроить с помощью параметра -Xssn JVM – вы захотите использовать самое низкое значение, с которым вы можете избавиться.

И это всего лишь предположение, но я думаю, что «несколько сотен новых streamов каждую секунду» определенно выше того, что JVM предназначен для удобного управления. Я подозреваю, что простой тест быстро выявит довольно непростые проблемы.

  • для вашего теста вы можете использовать JMeter + профилировщик, который должен дать вам прямой обзор поведения в такой сильно загруженной среде. Просто позвольте ему работать в течение часа и следить за памятью, процессором и т. Д. Если ничего не сломается, и CPU (ы) не перегреваются, все в порядке 🙂

  • возможно, вы можете получить пул streamов или настроить (продлить) тот, который вы используете, добавив некоторый код, чтобы каждый раз, когда Thread был получен из пула streamов, у вас есть соответствующий InheritableThreadLocal s. Каждая Thread имеет эти частные свойства:

     /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 

    Вы можете использовать их (ну, с reflectionм) в сочетании с Thread.currentThread() чтобы иметь желаемое поведение. Однако это немного ад-скакалка, и, кроме того, я не могу сказать, не отразится ли это (с reflectionм) даже большие накладные расходы, чем просто создание streamов.

Мне интересно, нужно ли создавать новые streamи для каждого пользовательского запроса, если их типичный жизненный цикл короче секунды. Не могли бы вы использовать какую-то очередь Notify / Wait, в которой вы порождаете заданное количество streamов (daemon), и все они ждут, пока не будет решена задача. Если очередь задач становится длинной, вы создаете дополнительные streamи, но не в соотношении 1-1. Скорее всего, это будет лучше, чем порождать сотни новых streamов, жизненные циклы которых настолько коротки.

  • Микропроцессорность DateTime.DayOfWeek
  • Когда и почему базы данных объединяются дорого?
  • Почему GCC генерирует на 15-20% быстрее код, если я оптимизирую размер вместо скорости?
  • Почему петли медленны в R?
  • Как вы проверяете эффективность веб-сайта?
  • Эффективность: массивы против указателей
  • Как я могу сделать запуск Emacs быстрее?
  • Является ли изменчивая переменная «читается» так же быстро, как обычно?
  • Потоки Java 8: почему параллельный stream медленнее?
  • Получить количество циклов процессора?
  • jQuery: первый против .first ()
  • Interesting Posts

    Как вы создаете сопоставление ключей vim, которое требует номеров перед горячей клавишей (например, <G>)?

    Сенсор Android TYPE_LINEAR_ACCELERATION – что он показывает?

    Ближайшая точка на кубической кривой Безье?

    ProcessBuilder: пересылка stdout и stderr запущенных процессов без блокировки основного streamа

    Как уменьшить расстояние между закрепленными значками в панели задач Windows 7 (по горизонтали)?

    Эффективный, лаконичный способ найти следующий соответствующий друг?

    Как получить экземпляр classа типа, связанного с привязкой к контексту?

    Запись dd на флэш-накопитель USB вызывает массивные чтения

    Git Bash Здесь, в ConEmu, встроенном в проводник Windows

    Raycast вызывает ошибку NullReferenceException

    Как получить прокрутку колесика мыши, работающую с Synergy?

    Обнаруживать, когда UIGestureRecognizer вверх, вниз, влево и вправо Cocos2d

    Android: действие будет уничтожено после вызова камеры Intent

    Autostart MySQL Server в Mac OS X Yosemite / El Capitan

    Почему лямбды лучше оптимизируются компилятором, чем простые функции?

    Давайте будем гением компьютера.