Есть крутые доклады Шипилёва и Куксенко, они об общих началах улучшений производительности и о низкоуровневых вещах. Универсальную статью написать сложно, т. к. примеры из этой статьи, основаны на нашем конкретном проекте и совсем не обязательно подойдут для вашего. Как мне кажется, именно поэтому тот же Шипилёв, Черёмин или Вакарт в основном рассказывают об улучшениях кода, привязанного только к JDK как к некой постоянной для многих разработчиков.
Я думал, что исполняться будет ява-кода из `String::equals`, но интринзифицированный метод, насколько я понимаю, исполняет нативный код, оптимизированный под конкретную платформу.
Проверил с помощью -prof gc, действительно, даже на восьмёрке итератор не создаётся: gc.alloc.rate.norm постоянно около 0. По времени счётный перебор конечно же быстрее, хотя и ненамного.
А вот для Collection.containsAll() скаляризация срабатывает только на небольших объёмах:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms1g", "-Xmx1g"})
public class AllMatchVsContainsAllBenchmark {
@Benchmark
public boolean collectionContainsAll(Data data) {
return data.original.containsAll(data.half);
}
@State(Scope.Thread)
public static class Data {
@Param({"10", "100", "1000"})
int count;
@Param({"ArrayList", "HashSet"})
String collectionType;
Collection<Integer> half;
Collection<Integer> original;
@Setup
public void setup() {
if ("ArrayList".equals(collectionType)) {
original = IntStream.range(0, count).boxed().collect(toCollection(ArrayList::new));
half = new HashSet<>(halfOfCollection(original));
} else {
original = IntStream.range(0, count).boxed().collect(toCollection(HashSet::new));
half = new HashSet<>(halfOfCollection(original));
}
}
private List<Integer> halfOfCollection(Collection<Integer> original) {
int newLength = original.size() / 2;
Integer[] integers = copyOf(original.toArray(new Integer[0]), newLength);
return asList(integers);
}
}
}
It indicates that an annotated method may be (but is not guaranteed to be) intrinsified by the HotSpot VM. A method is intrinsified if the HotSpot VM replaces the annotated method with hand-written assembly and/or hand-written compiler IR — a compiler intrinsic — to improve performance.
Думаю, вместо "создавать для них высокопроизводительный машинный код" мне стоило написать "подменять их реализацию высокопроизводительным машинным кодом". Кстати, интересно было бы узнать, в каких условиях интринсификация не срабатывает.
В Arrays.asList много чего не реализовано, например hashCode(): в текущей реализации вызывается метод абстрактного списка, использующий итератор, хотя можно было бы написать
@Override
public int hashCode() {
return Arrays.hashCode(a);
Тоже вариант, только в этом случае
orElseGet
не даёт преимущества передorElse
, т. к.Collections.emptyList()
возвращает кэшированный список.В Spring Data запросы вида
возвращают пустой список (
ArrayList
) для пустой выборки.Проверил с помощью
-prof gc
, действительно, даже на восьмёрке итератор не создаётся:gc.alloc.rate.norm
постоянно около 0. По времени счётный перебор конечно же быстрее, хотя и ненамного.А вот для
Collection.containsAll()
скаляризация срабатывает только на небольших объёмах:даёт
Верно, в документации сказано:
Думаю, вместо "создавать для них высокопроизводительный машинный код" мне стоило написать "подменять их реализацию высокопроизводительным машинным кодом". Кстати, интересно было бы узнать, в каких условиях интринсификация не срабатывает.
В Arrays.asList много чего не реализовано, например hashCode(): в текущей реализации вызывается метод абстрактного списка, использующий итератор, хотя можно было бы написать
и обойтись без перебора итератором.