Подтвердить что ты не робот

Как обеспечить, чтобы потоки Java выполнялись на разных ядрах

Я пишу многопоточное приложение на Java, чтобы повысить производительность по сравнению с последовательной версией. Это параллельная версия решения динамического программирования для проблемы с рюкзаком 0/1. У меня есть Intel Core 2 Duo с Ubuntu и Windows 7 Professional на разных разделах. Я бегу в Ubuntu.

Моя проблема в том, что параллельная версия на самом деле занимает больше времени, чем последовательная версия. Я думаю, это может быть потому, что потоки все сопоставлены с одним и тем же потоком ядра или что они распределяются по одному ядру. Могу ли я гарантировать, что каждый поток Java сопоставляется с отдельным ядром?

Я прочитал другие сообщения об этой проблеме, но ничего не помогает.

Вот конец main() и все run() для класса KnapsackThread (который расширяет Thread). Обратите внимание, что они используют срез и дополнительные для вычисления myLowBound и myHiBound, чтобы каждый поток не перекрывался в домене dynProgMatrix. Поэтому не будет никаких условий гонки.

    dynProgMatrix = new int[totalItems+1][capacity+1];
    for (int w = 0; w<= capacity; w++)
        dynProgMatrix[0][w] = 0;
    for(int i=0; i<=totalItems; i++)
        dynProgMatrix[i][0] = 0;
    slice = Math.max(1,
            (int) Math.floor((double)(dynProgMatrix[0].length)/threads.length));
    extra = (dynProgMatrix[0].length) % threads.length;

    barrier = new CyclicBarrier(threads.length);
    for (int i = 0; i <  threads.length; i++){
        threads[i] = new KnapsackThread(Integer.toString(i));
    }
    for (int i = 0; i < threads.length; i++){
        threads[i].start();
    }

    for (int i = 0; i < threads.length; i++){
        try {
            threads[i].join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public void run(){
    int myRank = Integer.parseInt(this.getName());

    int myLowBound;
    int myHiBound;

    if (myRank < extra){
        myLowBound = myRank * (slice + 1);
        myHiBound = myLowBound + slice;
    }
    else{
        myLowBound = myRank * slice + extra;
        myHiBound = myLowBound + slice - 1;
    }

    if(myHiBound > capacity){
        myHiBound = capacity;
    }

    for(int i = 1; i <= totalItems; i++){
        for (int w = myLowBound; w <= myHiBound; w++){

            if (allItems[i].weight <= w){
               if (allItems[i].profit + dynProgMatrix[i-1][w-allItems[i].weight]
                        > dynProgMatrix[i-1][w])
                {
                    dynProgMatrix[i][w] = allItems[i].profit +
                                      dynProgMatrix[i-1][w- allItems[i].weight];
                }
                else{
                    dynProgMatrix[i][w] = dynProgMatrix[i-1][w];
                }
            }
            else{
                dynProgMatrix[i][w] = dynProgMatrix[i-1][w];
            }
        }
        // now place a barrier to sync up the threads
        try {
            barrier.await(); 
        } catch (InterruptedException ex) { 
            ex.printStackTrace();
            return;
        } catch (BrokenBarrierException ex) { 
            ex.printStackTrace(); 
            return;
        }
    }
}

Обновление:

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

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

Есть ли у кого-нибудь проницательность?

4b9b3361

Ответ 1

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

Возможные причины для этого:

  • Множество синхронизаций (необходимых или ненужных)
  • Задачи, занимающие такое короткое время, что создание потоков занимает значительную долю времени
  • Контекстное переключение, если вы создаете слишком много потоков - для задач с интенсивным процессором создайте столько же потоков, сколько у вас есть.

Ответ 2

У меня была такая же проблема некоторое время. У меня была процессорная программа, которую я разделил на 2 потока (двухъядерный процессор), но один прекрасный день, обрабатывая еще несколько данных, он просто прекратил использовать оба ядра. Я просто увеличил размер памяти кучи (-Xmx1536m в моем случае), и он снова работал отлично.

Ответ 3

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