Здравствуйте, denis.zhdanov, Вы писали:
DZ>Итого разница составила около двух с половиной процентов, что несколько удвило, т.к. переключение контекста субъективно считалось мною более дорогой операцией. Понятно, что в тесте присутствует достаточно большая погрешность, но тем не менее тенденция налицо.
Само по себе переключение контекста занимает весьма немного времени.
Порядка 2.000-4.000 тактов процессора.
Обоснования:
1)
Вот выдержки из класса java.util.concurrent.Exchanger:
public class Exchanger<V> {
...
private static final int NCPU = Runtime.getRuntime().availableProcessors();
....
/**
* The number of times to spin (doing nothing except polling a
* memory location) before blocking or giving up while waiting to
* be fulfilled. Should be zero on uniprocessors. On
* multiprocessors, this value should be large enough so that two
* threads exchanging items as fast as possible block only when
* one of them is stalled (due to GC or preemption), but not much
* longer, to avoid wasting CPU resources. Seen differently, this
* value is a little over half the number of cycles of an average
* context switch time on most systems. The value here is
* approximately the average of those across a range of tested
* systems.
*/
private static final int SPINS = (NCPU == 1) ? 0 : 2000;
т.е. авторы java.util.concurrent считают, что на большинстве многоядерных систем переключение контекста лежит в диапазоне 3000-4000 тактов процессора.
2)
Вот выдержки из класса java.util.concurrent.SynchronousQueue:
/**
* The number of nanoseconds for which it is faster to spin
* rather than to use timed park. A rough estimate suffices.
*/
static final long spinForTimeoutThreshold = 1000L;
Если считать типичной тактовой частотой 2-3ГГц, то выходит, что авторы java.util.concurrent считают, что время, соизмеримое с переключением контекста порядка 2000-3000 тактов
3)
Следственный эксперимент: класс PingPong запускает два класса Player, а они перебрасываются классом Item.
Item содержит простой счетчик типа int, считающий количество передач. Передачи осуществляются через механизм wait()/notify().
На тестовом компьютере (AMD Duron 1300, WinXP, jdk 1.0.6_13) 100.000.000 передач было выполнено за 232с, т.е. на одно переключение контекста уходило в среднем 232 * 10 * 1.3 = 3016 тактов.
Код:
public class Item {
int counter = 0;
}
public class PingPong {
// constant
public static final int ITERATION_COUNT = 100 * 1000 * 1000;
// global variables :(
public static final Item item = new Item();
public static final CountDownLatch start = new CountDownLatch(1);
public static final CountDownLatch stop = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
// init section
new Thread(new Player(false)).start(); //player #0
new Thread(new Player(true)).start(); //player #1
// work section
long startTime = System.nanoTime();
start.countDown();
stop.await();
long deltaTime = System.nanoTime() - startTime;
// info-to-console section
System.out.println("deltaTime: " + deltaTime);
}
}
public class Player implements Runnable {
private final boolean imOdd;
public Player(boolean imOdd) {
this.imOdd = imOdd;
}
public void run() {
// await for start ping-ponging
try {
PingPong.start.await();
} catch (InterruptedException e) {e.printStackTrace();}
synchronized (PingPong.item) {
while (true) {
// wait for my turn (avoid spirious wakeup)
while (((PingPong.item.counter & 1) == 1) == imOdd) {
try {
PingPong.item.wait(); // wait for "Ping"
} catch (InterruptedException e) {e.printStackTrace();}
}
// increment Ping-Poin counter
PingPong.item.counter++;
// do "Pong"
PingPong.item.notify();
if (PingPong.item.counter >= PingPong.ITERATION_COUNT) {
PingPong.stop.countDown(); // show for PingPoint.class that my work doing
return; // break, thats all
}
}
}
}
}