Я пытаюсь выяснить, как правильно использовать Java-исполнителей. Я понимаю, что выполнение задач для ExecutorService
имеет свои собственные накладные расходы. Тем не менее, я удивлен, увидев, что это так высоко, как есть.
Моя программа должна обрабатывать огромное количество данных (данные на фондовом рынке) с максимально возможной задержкой. Большинство вычислений - довольно простые арифметические операции.
Я попытался проверить что-то очень простое: "Math.random() * Math.random()
"
Простейший тест запускает это вычисление в простом цикле. Второй тест выполняет одно и то же вычисление внутри анонимного Runnable (это должно измерять стоимость создания новых объектов). Третий тест проходит Runnable
до ExecutorService
(это измеряет стоимость введения исполнителей).
Я провел тесты на моем ноутбуке dinky (2 процессора, 1,5 гигабайта):
(in milliseconds)
simpleCompuation:47
computationWithObjCreation:62
computationWithObjCreationAndExecutors:422
(примерно один раз из четырех прогонов, первые два числа в итоге равны)
Обратите внимание, что исполнители занимают гораздо больше времени, чем выполнение в одном потоке. Номера были примерно одинаковыми для размеров пула потоков от 1 до 8.
Вопрос: Я пропустил что-то очевидное или ожидаются эти результаты? Эти результаты говорят мне, что любая задача, которую я передаю исполнителю, должна выполнять некоторые нетривиальные вычисления. Если я обрабатываю миллионы сообщений, и мне нужно выполнить очень простые (и дешевые) преобразования для каждого сообщения, я все равно не смогу использовать исполнителей... попытка распространения вычислений на нескольких процессорах может оказаться дороже, чем просто делая их в одном потоке. Конструктивное решение становится намного сложнее, чем я думал изначально. Любые мысли?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecServicePerformance {
private static int count = 100000;
public static void main(String[] args) throws InterruptedException {
//warmup
simpleCompuation();
computationWithObjCreation();
computationWithObjCreationAndExecutors();
long start = System.currentTimeMillis();
simpleCompuation();
long stop = System.currentTimeMillis();
System.out.println("simpleCompuation:"+(stop-start));
start = System.currentTimeMillis();
computationWithObjCreation();
stop = System.currentTimeMillis();
System.out.println("computationWithObjCreation:"+(stop-start));
start = System.currentTimeMillis();
computationWithObjCreationAndExecutors();
stop = System.currentTimeMillis();
System.out.println("computationWithObjCreationAndExecutors:"+(stop-start));
}
private static void computationWithObjCreation() {
for(int i=0;i<count;i++){
new Runnable(){
@Override
public void run() {
double x = Math.random()*Math.random();
}
}.run();
}
}
private static void simpleCompuation() {
for(int i=0;i<count;i++){
double x = Math.random()*Math.random();
}
}
private static void computationWithObjCreationAndExecutors()
throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(1);
for(int i=0;i<count;i++){
es.submit(new Runnable() {
@Override
public void run() {
double x = Math.random()*Math.random();
}
});
}
es.shutdown();
es.awaitTermination(10, TimeUnit.SECONDS);
}
}