Я читал похожие вопросы, где проблема заключалась в использовании синхронизированных методов (например, Math.random()), или работа была слишком мала, чтобы оправдать накладные расходы, однако я не думаю, что это здесь имеет место.ExecutorService не имеет рекурсивного детерминанта производительности. Java
Мой процессор имеет 4 физических/8 логических ядра. После одного разогрева я тестирую следующий код с n = 1; 2; 3; 4; 8 на матрице 11x11;
ExecutorService pool = Executors.newFixedThreadPool(n);
long startTime = System.currentTimeMillis();
double result = pool.submit(new Solver(pool, matrix)).get();
System.out.println(result);
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println(elapsedTime);
Execution принимает respectivelly:
1 ~ 15500 2 ~ 13500 - 14000 3 ~ 14300 - 15500 4 ~ 14500 - 19000 8 ~ 19000 - 23000
Так я получаю небольшое повышение с 2, почти без повышения с 3, иногда почти без усиления, но иногда экстремальное замедление с 4 и полное замедление с 8;
Вот код:
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class Solver implements Callable<Double> {
private ExecutorService pool;
private double[][] matrix;
public Solver(ExecutorService pool, double[][] matrix){
this.pool = pool;
this.matrix = matrix;
}
public double determinant(double[][] matrix) {
if (matrix.length == 1)
return (matrix[0][0]);
double coefficient;
double sum = 0;
int threadsCount = ((ThreadPoolExecutor) pool).getMaximumPoolSize();
ArrayList<Double> coefficients = new ArrayList<Double>();
ArrayList<Future<Double>> delayedDeterminants = new ArrayList<Future<Double>>();
for (int k = 0; k < matrix.length; k++) {
double[][] smaller = new double[matrix.length - 1][matrix.length - 1];
for (int i = 1; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (j < k)
smaller[i - 1][j] = matrix[i][j];
else if (j > k)
smaller[i - 1][j - 1] = matrix[i][j];
}
}
coefficient = ((k % 2 == 0) ? 1 : -1) * matrix[0][k];
if (((ThreadPoolExecutor) pool).getActiveCount() < threadsCount
&& matrix.length > 5) {
coefficients.add(coefficient);
delayedDeterminants.add(pool.submit(new Solver(pool, smaller)));
} else
sum += coefficient * (determinant(smaller));
}
try {
for (int i = 0; i < coefficients.size(); i++)
sum += coefficients.get(i) * delayedDeterminants.get(i).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return (sum);
}
@Override
public Double call() throws Exception {
return determinant(matrix);
}
}
1 и 2) 11x11 - небольшая матрица, но это не небольшое количество вычислений. Алгоритм имеет ужасную сложность (что-то вроде (n!)^2). Я попробовал его с матрицей 12x12, это потребовало 3 минут на тест. Матрица 100x100 будет ** буквально ** брать сотни лет. 3) Не совсем уверен, что вы имеете в виду, если это линия getActiveCount(), идея состоит в том, что есть свободные потоки. Я запускаю рекурсивную задачу в потоке, иначе я запускаю ее в текущем потоке. Обратите внимание, что я не всегда могу запустить его в другом потоке, потому что он рекурсивный, поэтому поток не будет свободен, пока все его рекурсивно называемые потоки не будут. – ndn
Не совсем понимаю пункт (3). –
@ndn ah да, теперь он возвращается ко мне ... Он действительно взрывается вычислительно. Общий способ решения такого алгоритма деления и покорения состоит в том, чтобы спуститься до определенного уровня в одном потоке, пока не будет достаточно независимых задач, а затем запланируйте все эти задачи в ExecutorService, позволяя выполнять последующие уровни рекурсии в рамках одной задачи. Например, у вас есть 121 подматрицы для вычисления детерминанта, поэтому отправьте 121 задачу в ExecutorService. –