Re[8]: Ускорение выполнения задачи
От: Golovach Ivan Украина http://kharkovitcourses.blogspot.com
Дата: 09.09.09 10:27
Оценка: 26 (3)
Здравствуйте, 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
                }
            }
        }
    }
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.