Я пробую службу исполнителя на Java и написал следующий код для запуска Фибоначчи (да, массово рекурсивную версию, просто чтобы подчеркнуть службу исполнителя).
Удивительно, но он будет работать быстрее, если я установлю для nThreads значение 1. Это может быть связано с тем, что размер каждой «задачи», отправляемой службе-исполнителю, очень мал. Но все же это должно быть то же число, если я установлю nThreads на 1.
Чтобы увидеть, может ли доступ к общим переменным Atomic вызвать эту проблему, я закомментировал три строки комментарием «см. текст» и посмотрел на системный монитор, чтобы увидеть, сколько времени занимает выполнение. Но результаты такие же.
Любая идея, почему это происходит?
Кстати, я хотел сравнить это с аналогичной реализацией с Fork/Join. Это оказывается намного медленнее, чем реализация F/J.
public class MainSimpler {
static int N=35;
static AtomicInteger result = new AtomicInteger(0), pendingTasks = new AtomicInteger(1);
static ExecutorService executor;
public static void main(String[] args) {
int nThreads=2;
System.out.println("Number of threads = "+nThreads);
executor = Executors.newFixedThreadPool(nThreads);
Executable.inQueue = new AtomicInteger(nThreads);
long before = System.currentTimeMillis();
System.out.println("Fibonacci "+N+" is ... ");
executor.submit(new FibSimpler(N));
waitToFinish();
System.out.println(result.get());
long after = System.currentTimeMillis();
System.out.println("Duration: " + (after - before) + " milliseconds\n");
}
private static void waitToFinish() {
while (0 < pendingTasks.get()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
executor.shutdown();
}
}
class FibSimpler implements Runnable {
int N;
FibSimpler (int n) { N=n; }
@Override
public void run() {
compute();
MainSimpler.pendingTasks.decrementAndGet(); // see text
}
void compute() {
int n = N;
if (n <= 1) {
MainSimpler.result.addAndGet(n); // see text
return;
}
MainSimpler.executor.submit(new FibSimpler(n-1));
MainSimpler.pendingTasks.incrementAndGet(); // see text
N = n-2;
compute(); // similar to the F/J counterpart
}
}
Время работы (приблизительно):
- 1 поток: 11 секунд
- 2 потока: 19 секунд
- 4 потока: 19 секунд
Обновление: я заметил, что даже если я использую один поток внутри службы-исполнителя, вся программа будет использовать все четыре ядра моей машины (каждое ядро в среднем использует около 80%). Это может объяснить, почему использование большего количества потоков внутри службы-исполнителя замедляет весь процесс, но почему эта программа использует 4 ядра, если внутри службы-исполнителя активен только один поток?