2017-01-25 2 views
1

Может ли кто-нибудь сказать мне, почему использование java 8 stream/lambda намного медленнее, чем для каждого цикла в коде ниже?java 8 уникальный набор ключей из множества карт медленный, используя lambda

Set<Map<Path, String>> set = new HashSet<>(); 
Map<Path, String> map1 = new HashMap<>(); 
map1.put(Paths.get("foo"), "bar"); 
set.add(map1); 

Map<Path, String> map2 = new HashMap<>(); 
map2.put(Paths.get("foo"), "ham"); 
set.add(map2); 

long start = System.currentTimeMillis(); 
Set<Path> uniqueFromStream = set.stream().flatMap(m -> m.keySet().stream()).collect(Collectors.toSet()); 
System.err.println("miliseconds for streams: " + (System.currentTimeMillis() - start)); 

start = System.currentTimeMillis(); 
Set<Path> uniqueFromLoop = new HashSet<>(); 
for(final Map<Path,String> map : set){ 
    uniqueFromLoop.addAll(map.keySet()); 
} 
System.err.println("miliseconds for loops: " + (System.currentTimeMillis() - start)); 

, когда я запускаю его, в среднем потоки/лямбда 63 милисекунд, но для каждого цикла составляет 0 миллисекунды!

+1

лямбды и 'Stream's были добавлен для поддержки распараллеливания на больших наборах объектов. Тем не менее, он очень неэффективен для последовательного доступа и изменения небольших наборов данных. Если вы хотите попробовать с параллельной поддержкой, замените '.stream()' на '.parallelStream()' – CraigR8806

+3

. Также существует проблема с загрузкой классов, что этот пример не учитывает. Для потоков нужно загружать намного больше. –

+2

Вы проверяете каждый метод только один раз, что на самом деле не является хорошим эталоном. По крайней мере, вы должны попытаться измерить это в цикле (то есть 1000 раз), чтобы уменьшить влияние JIT, класс-нагрузки и других подобных вещей. Или (лучше), используйте специализированную инфраструктуру микро-бенчмаркинга, такую ​​как [JMH] (http://openjdk.java.net/projects/code-tools/jmh/). В противном случае нельзя доверять тому, что вы наблюдаете на одном тестовом прогоне. – zeppelin

ответ

0

Хорошо, что нет волшебства.

Обработка потока занимает немного времени для инициализации и затрудняет компилятор для оптимизации кода. Вызовы Lambdas добавляют накладные расходы. И, конечно же, вы «платите» за сборку (Collectors.toSet()) в конце.

С другой стороны, для циклов проще и агрессивно оптимизировать/вставлять в компилятор.

+0

С только 1 итерацией я не делаю JIT, так что здесь нет никакой оптимизации afaik. –

+0

Я говорил о java-компиляторе (статическом) оптимизации. Уровень байткода, а не родной. –

+0

Нет оптимизированной оптимизации байт-кода, которая бы противоречила идее иметь JIT в первую очередь. –

1

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

С другой стороны, lambdas объясняет больше намерений разработчика функциональным программированием, и ваш код более поддается регулированию.

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

Был интересное выступление на Devoxx Бельгии По Триша Gee на эту тему: https://www.youtube.com/watch?v=dlzMV83RTtw