2014-12-05 4 views
2

Итак, у меня есть программа, которая запускает кучу разных вычислений, а затем возвращает результат, когда все вычисления выполнены.Почему мой код медленнее, используя несколько потоков в Java?

Первоначально мой код был синхронным, как следующее:

public class MyApplication{ 

    public static void main(String[] args) { 
     doCalculation(1); 
     doCalculation(2); 
     doCalculation(3); 
     doCalculation(4); 
     doCalculation(5); 
     doCalculation(6); 
     doCalculation(7); 
     doCalculation(8); 

     /* Print result */ 
    } 
} 

Я думал, что это будет более эффективно выполнять эти вычисления в новых потоках, так что теперь у меня есть что-то вроде,

public class MyApplication{ 
    public static void main(String[] args) { 
     List<Thread> threads = new ArrayList<Thread>(); 
     threads.add(doCalculation(1)); 
     threads.add(doCalculation(2)); 
     threads.add(doCalculation(3)); 
     threads.add(doCalculation(4)); 
     threads.add(doCalculation(5)); 
     threads.add(doCalculation(6)); 
     threads.add(doCalculation(7)); 
     threads.add(doCalculation(8)); 

     for(Thread t : threads){ 
      if(t.isAlive()){ 
       try{ 
        t.join(); 
       } catch(InterruptedException e) { 
        System.out.println("Error calculating fitness"); 
       } 
      } 
     } 

     /* Print result */ 
    } 
} 

Я Извините, я полный новичок в потоках. Если бы мне пришлось догадаться, я бы предположил, что у меня появилось два новых потока (в моем приложении около 50 вычислений), но любой совет будет действительно оценен!

+0

И я не знал раньше этого раньше: O Список тем. Интересно ! – Anurag

+0

doCalculation(), вероятно, занимает достаточно много времени, чтобы компенсировать накладные расходы на создание потоков. –

ответ

3

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

Мой совет будет использовать Java 8 Streams API и формулируют проблему в виде параллельного потока, такие как

IntStream.range(1, 9).map(this::doCalculation).parallel().collect(Collectors.toList()); 

Это будет выполняться на общей ForkJoinPool, что по умолчанию размер в соответствии с количество ядер процессора.

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

5

Каждый поток имеет накладные расходы, чтобы начать и присоединиться. Если работа, выполняемая нитью, значительна, накладные расходы ее стоят; но если doCalculation довольно быстр, тогда накладные расходы будут подавлять его.

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

Если каждый doCalculation занимает Х-время, и у вас есть 8 из них (и, следовательно, 8T служебных данных, где T - количество накладных расходов на поток), тогда общая программа займет больше времени, если 8T > 8TX - то есть, если накладные расходы на разворачивание 8 потоков больше, чем объем работы для выполнения всех 8 вычислений последовательно.

+0

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

+1

@MarkoTopolnik Это правда, но из этого примера видно, что OP фактически создал и начал потоки вручную (хотя при более близком чтении они никогда не вызывали 't.start()'!) – yshavit

+0

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

Смежные вопросы