Ненужные переключатели контекста Java
У меня есть сеть Java Threads (потоковое программирование), обменивающаяся данными по каналам с фиксированной емкостью - работающая под управлением WindowsXP. На основании нашего опыта работы с "зелеными" потоками (без вытеснения) мы ожидали, что потоки будут реже переключать контекст (таким образом сокращая время ЦП), если каналы станут больше. Однако мы обнаружили, что увеличение размера канала не имеет никакого значения для времени выполнения. Кажется, что происходит то, что Java решает переключать потоки, даже если каналы не заполнены или пусты (то есть, хотя поток не должен приостанавливаться), что тратит процессорное время без видимого преимущества. Также изменение приоритетов потоков не имеет заметной разницы.
Мой вопрос заключается в том, существует ли какой-либо способ убедить Java не делать ненужных переключений контекста, а отложить переключение до тех пор, пока не станет действительно необходимым переключать потоки - есть ли способ изменить логику диспетчеризации Java? Или это реагирует на то, на что я не обращал внимания?! Или есть другие механизмы асинхронизма, например, фабрики потоков, Runnable (s), возможно, даже демоны (!). Ответ кажется неочевидным, так как до сих пор ни один из моих корреспондентов не придумал ответ (включая совсем недавно двух профессоров CS). Или, может быть, я упускаю что-то настолько очевидное, что люди не могут представить, что я этого не знаю...
Я добавил сюда код отправки и получения - не очень элегантно, но, похоже, он работает...;-) Если вам интересно, я подумал, что логика goLock в 'send' может вызывать проблему, но ее устранение. временно не имеет никакого значения. Я добавил код для отправки и получения...
public synchronized Packet receive() {
if (isDrained()) { return null; } while (isEmpty()) { try { wait(); } catch (InterruptedException e) { close(); return null; } if (isDrained()) { return null; } } if (isDrained()) { return null; } if (isFull()) { notifyAll(); // notify other components waiting to send } Packet packet = array[receivePtr]; array[receivePtr] = null; receivePtr = (receivePtr + 1) % array.length; //notifyAll(); // only needed if it was full usedSlots--; packet.setOwner(receiver); if (null == packet.getContent()) { traceFuncs("Received null packet"); } else { traceFuncs("Received: " + packet.toString()); } return packet;
}
синхронизированная логическая отправка (окончательный пакетный пакет, конечная операция OutputPort) {
sender = op.sender; if (isClosed()) { return false; } while (isFull()) { try { wait(); } catch (InterruptedException e) { indicateOneSenderClosed(); return false; } sender = op.sender; } if (isClosed()) { return false; } try { receiver.goLock.lockInterruptibly(); } catch (InterruptedException ex) { return false; } try { packet.clearOwner(); array[sendPtr] = packet; sendPtr = (sendPtr + 1) % array.length; usedSlots++; // move this to here if (receiver.getStatus() == StatusValues.DORMANT || receiver.getStatus() == StatusValues.NOT_STARTED) { receiver.activate(); // start or wake up if necessary } else { notifyAll(); // notify receiver // other components waiting to send to this connection may also get // notified, // but this is handled by while statement } sender = null; Component.network.active = true; } finally { receiver.goLock.unlock(); } return true;
}
Спасибо за вопрос! Я обсуждаю тот же вопрос на форуме Sun, и вот мой последний пост на этом форуме:
Наше лучшее предположение сейчас состоит в том, что этот эффект является результатом логики планирования Windows.
Похоже, что Microsoft признает, что эта область нуждается в некотором улучшении, поскольку она внедряет UMS - я цитирую: "UMS рекомендуется для приложений с высокими требованиями к производительности, которым необходимо эффективно запускать множество потоков одновременно в многопроцессорных или многоядерных системах. ... UMS доступна начиная с 64-разрядных версий Windows 7 и Windows Server 2008 R2. Эта функция недоступна в 32-разрядных версиях Windows." Надеемся, что Java воспользуется преимуществами UMS в более позднем выпуске.
Спасибо за вашу помощь!
3 ответа
Я немного смущен - сегодня днем мне пришло в голову, что, может быть, сеть, производительность которой меня беспокоит, была слишком проста, поскольку у меня было только два процесса и два процесса. Таким образом, Windows, возможно, слишком старалась держать процессоры сбалансированными! Поэтому мне было интересно, что произойдет, если я предоставлю Windows много процессов.
Я настроил две сети:
a) 50 генераций компонентов, питающих 50 компонентов Discard - то есть высокопараллельной сети - всего 100 потоков
б) 50 генерации компонентов, подающих 1 отбрасываемый компонент - то есть сильно "воронкообразная" сеть - так что это 51 поток
Я запускал каждый из них 6 раз с пропускной способностью соединения 10 и 6 раз с пропускной способностью соединения 100. Каждый прогон генерировал в общей сложности 50 * 20 000 информационных пакетов, в общей сложности 1 000 000 пакетов, и работал около 1 минуты.
Вот средние значения для 4 случаев: а) с пропускной способностью 10 - 59,151 с. а) с пропускной способностью 100 - 52,008 сек.
б) с пропускной способностью 10 - 76,745 с. б) с пропускной способностью 100 - 60,667 с.
Таким образом, похоже, что емкость подключения имеет значение! И, похоже, JavaFBP работает достаточно хорошо... Я прошу прощения за то, что был немного поспешным - но, возможно, это заставило всех нас задуматься о многопоточности в многоядерной машине...;-)
Снова прошу прощения, и спасибо всем, кто поделился своими мыслями на эту тему!
Зеленые нити исчезли (возможно, Solaris все еще поддерживает это, но я сомневаюсь в этом). Кроме того, Java не переключает потоки, это делает ОС. Единственное, что делает Java - это сигнализирует ОС, что поток простаивает / ждет / блокирует с помощью функций ОС. Поэтому, если ваша программа попадает в какие-либо точки синхронизации, выполняет Thread.wait/sleep, она будет сигнализировать, что ей больше не нужен процессор.
Кроме того, ОС поддерживает временные интервалы и забирает процессор из потока, даже если он все еще может работать, когда другие потоки ждут процессор.
Можете ли вы опубликовать еще немного кода здесь?
Извините, если это полностью фальшиво, но я уверен, что Java больше не делает зеленые потоки с Java 1.1. По крайней мере, так говорит и Википедия.
Это ограничит вас в использовании приоритетов, но в большинстве случаев я также не смог добиться заметных улучшений производительности.