Вложенные циклы с использованием потоков для достижения максимальной производительности - PullRequest
0 голосов
/ 07 ноября 2019

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

MyClass graph = new MyClass(V);

for (int i = 0; i < numOfVertices; i++) {
   for (int j = 0; j < numOfVertices; j++) {
      if (adjMatrix[i][j] != 0) {
         graph.addEdge(i, j, adjMatrix[i][j]);
      }
   }
}

Я написал это:

int b = IntStream.range(0, numOfVertices - 1).parallel()
   .map(i -> (IntStream.range(0, numOfVertices - 1))
      .map(j -> {
         if (adjMatrix[i][j] != 0) {
            graph.addEdge(i, j, adjMatrix[i][j]);
         }
      }));

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

Спасибо!

Ответы [ 2 ]

1 голос
/ 07 ноября 2019

Независимо от производительности, это простая форма, перезаписывающая ее потоком:

                IntStream.range(0, numOfVertices - 1)
                 .forEach(i -> IntStream.range(0, numOfVertices - 1)
                   .filter(j -> adjMatrix[i][j] != 0)
                     .forEach(j -> graph.addEdge(i, j, adjMatrix[i][j])));
0 голосов
/ 08 ноября 2019

Короткий ответ

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

.. long answer

Я создал тест для измерения среднего времени выполнения одной из трех опций.

"Цикл For:"

for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
        doSomething(numOfVertices[i][j]);
    }
}

"IntStream. диапазон: "

IntStream.range(0, size)
        .forEach(i -> (IntStream.range(0, size))
                .forEach(j -> {
                    doSomething(numOfVertices[i][j]);
                }));

" Параллель IntStream.range: "

IntStream.range(0, size).parallel()
        .forEach(i -> (IntStream.range(0, size))
                .forEach(j -> {
                    doSomething(numOfVertices[i][j]);
                }));

Этот раздел был удален, поскольку я не мог упростить его стоимость времени:

      if (adjMatrix[i][j] != 0) {
         graph.addEdge(i, j, adjMatrix[i][j]);
      }

Ниже приведен весь тестовый код, который выполняет измерения для разных матриц. Каждый случай выполняется 10000 раз и дает среднее время. Вот результаты для запуска на моем компьютере. Для небольших матриц (от 1 х 1 до 100 х 100) для цикла это лучший вариант. Когда матрица большая (более 500 x 500), использование «IntStream.range parallel» выполняется быстрее. «IntStream.range» всегда хуже, чем «For loop» для каждого случая.

Size: 1
IntStream.range parallel: 0.002300
         IntStream.range: 0.002200
                For loop: 0.000000
Size: 5
IntStream.range parallel: 0.023000
         IntStream.range: 0.000500
                For loop: 0.000000
Size: 10
IntStream.range parallel: 0.025800
         IntStream.range: 0.000300
                For loop: 0.000100
Size: 50
IntStream.range parallel: 0.042200
         IntStream.range: 0.004300
                For loop: 0.000900
Size: 100
IntStream.range parallel: 0.035000
         IntStream.range: 0.006000
                For loop: 0.004300
Size: 500
IntStream.range parallel: 0.060200
         IntStream.range: 0.102000
                For loop: 0.079600
Size: 1000
IntStream.range parallel: 0.122000
         IntStream.range: 0.379800
                For loop: 0.347400
    public static void main(String[] args) {

        Arrays.asList(1, 5, 10, 50, 100, 500, 1000).forEach(
                size -> {
                    System.out.println("Size: " + size);
                    Integer[][] numOfVertices = createArray(size);

                    Map<String, Runnable> toRun = Map.of(
                            "For loop:",
                            () -> {
                                for (int i = 0; i < size; i++) {
                                    for (int j = 0; j < size; j++) {
                                        doSomething(numOfVertices[i][j]);
                                    }
                                }
                            },
                            "IntStream.range:",
                            () -> {
                                IntStream.range(0, size)
                                        .forEach(i -> (IntStream.range(0, size))
                                                .forEach(j -> {
                                                    doSomething(numOfVertices[i][j]);
                                                }));
                            },
                            "IntStream.range parallel:",
                            () -> {
                                IntStream.range(0, size).parallel()
                                        .forEach(i -> (IntStream.range(0, size))
                                                .forEach(j -> {
                                                    doSomething(numOfVertices[i][j]);
                                                }));
                            }
                    );

                    int howManyTimes = 10000;
                    Map<String, Double> map = new HashMap<>();
                    toRun.entrySet().forEach(e -> {
                        double time = (((double)IntStream.range(0, howManyTimes).mapToObj(
                                (x) -> executionTime(e.getValue()))
                                .reduce(Long::sum).get()) / howManyTimes);

                        map.put(e.getKey(), time);
                    });

                    map.entrySet().forEach(e -> {
                        System.out.println(String.format("%25s %f", e.getKey(), e.getValue()));
                    });
                }
        );
    }

    private static Integer[][] createArray(int size) {
        Integer[][] val = new Integer[size][size];
        IntStream.range(0, size).forEach(
                i -> IntStream.range(0,size).forEach(
                        j -> val[i][j] = (int)(Math.random() * 100)
                )
        );
        return  val;
    }

    private static int doSomething(int val) {
        return val * val;
    }

    private static long executionTime(Runnable execThis)
    {
        long startTime = System.currentTimeMillis();
        execThis.run();
        long endTime = System.currentTimeMillis();
        return (endTime-startTime);
    }
...