Значительные потери производительности при использовании более 6 потоков - PullRequest
2 голосов
/ 05 марта 2012

Я делаю простой эксперимент по подбрасыванию монет для класса, который включает в себя подбрасывание определенного количества монет на определенное количество нитей.Чтобы запустить наши тесты производительности на ускорение, мы используем фиксированное количество монетных флипов (я использовал миллиард) и меняем количество потоков.Для выполнения этих тестов мы используем экземпляры AWS Extra High CPU с 8 ядрами.По какой-то причине, как только я использую более 6 потоков, я получаю значительное замедление.Хуже того, это противоречиво.Иногда я получаю 14 секунд, иногда 2 для того же количества потоков и сальто.Это не имеет никакого смысла.Я пытался использовать разные JVM (OpenJRE и Sun JVM) и пробовал новый экземпляр.Ниже мой код и результаты теста (в мс).Я хотел бы помочь.Спасибо.

РЕДАКТИРОВАТЬ: Кажется, я решил это, во многом благодаря предложениям Ядаба и Бруно Рейса.Они предложили использовать локальную переменную для отслеживания количества головок, что, я думаю, могло быть фактором.Они также предложили запустить все мои тесты в рамках одного сеанса JVM, что почти определенно имело значение.Спасибо всем за помощь.

Speedup:
Threads | Flips | Time
1       1000000000  16402 16399  16404
2       1000000000  8218  8216   8217
3       1000000000  5493  5483   5492
4       1000000000  4125  4127   4140
5       1000000000  3306  3304   3311
6       1000000000  2758  2766   2756
7       1000000000  8346  7874   10617
8       1000000000  14370 14414  17831
9       1000000000  14956  14764  15316
10      1000000000  13595 14491  14031
11      1000000000  12642 11188   10625
12      1000000000  10620 10629  10876
13      1000000000  8422  9950   9756
14      1000000000  9284  9546   10194
15      1000000000  8524  4134   8046
16      1000000000  6915  6361   7275

Код:

import java.util.Random;

public class CoinFlip implements Runnable {
    private final long iterations; //iterations is the number of times the program will run, numHeads is the number of heads counted
    private long numHeads;
    public CoinFlip(long iterations) {
        this.iterations = iterations;
    }

    @Override
    public void run() {
        Random rand = new Random();
        numHeads = 0;
        for (long i = 0; i < iterations; i++) {
            if (rand.nextBoolean()) { //True represents heads, false represents a tails
                numHeads++;
            }
        }
    }

    public long getHeads() { //numHeads getter
        return numHeads;
    }

    public static void main(String[] args) {
        final long numIterations , itersPerThread; //iterations: number of iterations, threads: number of threads to run on, itersPerThread: how many iterations each thread is responsible for
        final int threads;
        if (args.length != 2) {
            System.out.println("Usage: java CoinFlip #threads #iterations");
            return;
        }
        try {
            threads = Integer.parseInt(args[0]);
            numIterations = Long.parseLong(args[1]);
        } catch (NumberFormatException e) {
            System.out.println("Usage: java CoinFlip #threads #iterations");
            System.out.println("Invalid arguments");
            return;
        }
        itersPerThread = numIterations / ((long)threads); //Might cause rounding errors, but we were told to ignore that
        Thread[] threadList = new Thread[threads]; //List of running threads so we can join() them later
        CoinFlip[] flipList = new CoinFlip[threads]; //List of our runnables so that we can collect the number of heads later
        for (int i = 0; i < threads; i++) { //create each runnable
            flipList[i] = new CoinFlip(itersPerThread);
        }
        long time = System.currentTimeMillis(); //start time
        for (int i = 0; i < threads; i++) { //create and start each thread
            threadList[i] = new Thread(flipList[i]);
            threadList[i].start();
        }
        for (int i = 0; i < threads; i++) { //wait for all threads to finish
            try {
                threadList[i].join();
                System.out.println("Collected thread " + i);
            } catch (InterruptedException e) {
                System.out.println("Interrupted");
                return;
            }
        }
        time = System.currentTimeMillis() - time; //total running time
        long totHeads = 0; 
        for (CoinFlip t : flipList) { //Collect number of heads from each CoinFlip object
            totHeads += t.getHeads();
        }

        //Print results
        System.out.println(totHeads + " heads in " + (numIterations / threads)
                * threads + " coin tosses on " + threads + " threads");
        System.out.println("Elapsed time: " + time + "ms");
    }
}

Ответы [ 6 ]

5 голосов
/ 05 марта 2012

Пока вы выполняете только операции с привязкой к процессору, есть смысл использовать больше потоков, чем доступных ядер.Напротив, использование дополнительных потоков увеличивает издержки переключения контекста и планирования.

3 голосов
/ 05 марта 2012

Если вы используете виртуальную машину, доступные ядра также могут быть виртуальными.Иногда вы можете получить 8 разных ядер, а иногда вы можете получить 4 ядра по 2 потока каждое.

Я подозреваю, что базовый компьютер имеет 6 ядер с 2 потоками в каждом, из которых вы можете использовать до 8.

2 голосов
/ 05 марта 2012

Любые тесты Java, которые выполняются в течение короткого времени (менее 30 секунд), просто не подходят для тестирования производительности.Компилятор hotspot и другие механизмы времени выполнения Java оптимизируют ваш код в течение первых большого количества секунд, в течение которых ваше приложение работает.Ваши временные отклонения могут быть легко отнесены к запуску JVM, оптимизации и выключению.

Если вы хотите более реалистичное время, то вам придется работать в течение примерно 30 секунд и , а затем начните отсчет времени.Кроме того, я бы порекомендовал вам умножить количество выполненных тестов по крайней мере на порядок, чтобы лучше усреднить влияние накладных расходов ОС, сборщика мусора, фоновых задач и т. Д. Так что прогрейте приложение, чтобы оно работало в течение 30 секунд, запуститетестирование и таймер, дайте ему поработать не менее минуты, остановите таймер и запишите свои результаты, а затем отключите JVM.

Кроме того, имеет больше смысла график того, сколько монетных бросков вы делаете взатем определенное количество времени, чтобы увидеть, сколько времени потребуется, чтобы сделать определенное количество монетных бросков.Разница в том, что вы хотите, чтобы все тесты выполнялись в течение одного и того же промежутка времени, если вы можете.

2 голосов
/ 05 марта 2012

Ваши потоки загружают процессор, то есть они не блокируют ожидание готовности какого-либо медленного ресурса, поэтому потоки конкурируют друг с другом за процессор.

Могу поспорить, что каждый поток приостановленПриказ поставить другого товарищеского потока на исполнение.То есть временные интервалы выполнения всегда исчерпаны.Таким образом, существует много переключений контекста между потоками без реального усиления, сравнивая его только с 6 потоками (при условии, что шесть потоков могут быть выполнены одновременно).

1 голос
/ 05 марта 2012

Быстрый взгляд на ваш источник не показывает узких мест (нигде нет мелкой синхронизации).

В комментариях вы упоминаете, что работаете в облачном сервисе. Скорее всего, это виртуализированная система, которая также предоставляет услуги другим клиентам. Если это предположение верно, вы не можете ожидать какого-либо значимого бенчмаркинга, поскольку не знаете, какую другую обработку система могла бы выполнить, помимо вашей собственной рабочей нагрузки.

Попробуйте свой тест на локальной рабочей станции, он должен демонстрировать гораздо меньшие вариации - но, естественно, все еще будут некоторые вариации (нет никаких гарантий, что каждый поток получит одинаковый фрагмент ЦП).

0 голосов
/ 05 марта 2012

Количество потоков не должно превышать количество имеющихся у вас процессорных ядер.Вы будете оштрафованы, потому что виртуальная машина должна будет переключать потоки на

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...