2016-12-11 2 views
5

Я хотел изучить параллельное программирование для ускорения алгоритмов и выбрать Java.
Я написал две функции для суммирования long целые числа в массиве - один простой итерационный массив, второй - разделение массива на части и суммирование частей в отдельных потоках.Многопользовательские потоки Java дают очень малый коэффициент усиления

Ожидалось, что логика будет примерно вдвое выше, используя два потока. Тем не менее, у меня есть только 24% скорости. Более того, используя больше потоков, я не получаю улучшения (возможно, менее 1%) по двум потокам. Я знаю, что должно быть создание потоков/присоединение накладных расходов, но я думаю, что это не должно быть так много.

Не могли бы вы объяснить, что мне не хватает или где ошибка в коде? Вот код:

import java.util.concurrent.ThreadLocalRandom; 


public class ParallelTest { 


public static long sum1 (long[] num, int a, int b) { 
    long r = 0; 
    while (a < b) { 
     r += num[a]; 
     ++a; 
    } 
    return r; 
} 

public static class SumThread extends Thread { 
    private long num[]; 
    private long r; 
    private int a, b; 

    public SumThread (long[] num, int a, int b) { 
     super(); 
     this.num = num; 
     this.a = a; 
     this.b = b; 
    } 

    @Override 
    public void run() { 
     r = ParallelTest.sum1(num, a, b); 
    } 

    public long getSum() { 
     return r; 
    } 
} 


public static long sum2 (long[] num, int a, int b, int threadCnt) throws InterruptedException { 
    SumThread[] th = new SumThread[threadCnt]; 
    int i = 0, c = (b - a + threadCnt - 1)/threadCnt; 

    for (;;) { 
     int a2 = a + c; 
     if (a2 > b) { 
      a2 = b; 
     } 
     th[i] = new SumThread(num, a, a2); 
     th[i].start(); 
     if (a2 == b) { 
      break; 
     } 
     a = a2; 
     ++i; 
    } 

    for (i = 0; i < threadCnt; ++i) { 
     th[i].join(); 
    } 
    long r = 0; 
    for (i = 0; i < threadCnt; ++i) { 
     r += th[i].getSum(); 
    } 
    return r; 
} 

public static void main(String[] args) throws InterruptedException { 
    final int N = 230000000; 
    long[] num = new long[N]; 

    for (int i = 0; i < N; ++i) { 
     num[i] = ThreadLocalRandom.current().nextLong(1, 9999); 
    } 

    // System.out.println(Runtime.getRuntime().availableProcessors()); 

    long timestamp = System.nanoTime(); 
    System.out.println(sum1(num, 0, num.length)); 
    System.out.println(System.nanoTime() - timestamp); 

    for (int n = 2; n <= 4; ++n) { 
     timestamp = System.nanoTime(); 
     System.out.println(sum2(num, 0, num.length, n)); 
     System.out.println(System.nanoTime() - timestamp); 
    } 


} 
} 

EDIT: Я есть i7 процессор с 4 ядрами (8 потоков). выхода задается кодом:

1149914787860 
175689196 
1149914787860 
149224086 
1149914787860 
147709988 
1149914787860 
138243999 

ответ

3

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

+0

Это означает, что если бы у меня было больше задач с интенсивным процессором в цикле, тогда у меня будет лучшее усиление производительности с большим количеством потоков? – Somnium

+0

@Somnium - правильный. – rcgldr

3

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

  1. Накладные расходы на создание материалов являются существенными. Резьба start() - это дорогостоящая операция, которая влечет за собой несколько системных вызовов для выделения стека потоков и его «красной зоны» и затем создания собственного потока.

  2. Нитки нитей не будут запускаться одновременно. Это означает, что время для завершения параллельной части вычисления будет приблизительно конечным временем последнего потока - началом первого раза. Это будет длиннее времени, когда один поток берет на себя часть своей работы. (По N-1 раз время создания потока ...)

  3. N потоков (в основном) выполняет последовательное сканирование N разделенных разделов массива. Это интенсивность полосы пропускания памяти, а способ сканирования означает, что кэши памяти будут неэффективными. Поэтому есть хорошая вероятность того, что производительность ограничена скоростью и пропускной способностью основного оборудования вашей системы.

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