Как стать автором
Обновить

Комментарии 19

ещё лучше будет вообще без копиварония а пометить начало (i0) и длинну, и доступ к элементамget(i) => get(i0+i), только чтоб юзер помнил что там ссылки на те же элементы, а метод который физически копирует должен бы иметь имя copySubList
Спецификация требует, чтобы возвращалась именно копия массива.
Спасибо, поменял.

Вообще таких недоработок в JDK вагон и маленькая тележка. Они не хотят раздувать код классов коллекций, оптимизируя каждый вариант вызова каждого метода. Хотя с моей точки зрения, стоило бы.

Увеличение размера кода негативно влияет на JIT оптимизации.

Конкретизируйте? Насколько я знаю, в JIT нет общего лимита на количество или общий объем скомпилированных методов. Имеет значение глубина стека и размер конкретного метода в байтах, но замена общих методов из какого-нибудь AbstractList на оптимизированные реализации ни на то, ни на другое не влияет.


При росте общего объема машинного кода уменьшается эффективность использования кеша инструкций, но, во-первых, это не относится к JIT, а во-вторых, не так велик эффект… лучше оптимальный метод вне кеша инструкций, чем неоптимальный в кеше.

Спасибо. Насколько я понял, это защищает от совсем патологических ситуаций, но оптимизированные методы в ArrayList погоды делают

ЕМНИП, в старых версиях у `ArrayList`-а не было собственной реализации подсписка и итератора.

А-а-а, ты везде!!! Приходишь на работу — там новые фичареквесты от тебя. Идёшь почитать, что в джаве делается, а там письма от тебя в мейлинг-листе. Хочешь передохнуть, заглядываешь на Хабр, и тут ты!!!

Спасибо )) несу высокопроизводительную яву в массы )

Кстати, пометка HotSpotIntrinsicCandidate не означает, что это на самом деле интринсик, и уж точно не «позволяет ВМ создавать для них высокопроизводительный машинный код». Чтобы сделать интринсик одной пометки мало. И помечено существенно больше методов, чем на самом деле интринсиков (на то оно и «candidate»). Поэтому закладываться на это не стоит.


Кстати, можно ещё приложиться к Arrays.asList. Там вроде SubList вообще специально не реализован, а мог бы быть.

Верно, в документации сказано:


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);

и обойтись без перебора итератором.

в каких условиях интринсификация не срабатывает.

Ну так кури сорцы :D

и обойтись без перебора итератором.

Надо, кстати, проверить. Теоретически его итератор добили до состояния, когда он может удалиться полностью escape-анализом в плотном цикле.

Проверил с помощью -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);
        }
    }
}

даёт


Benchmark           (collectionType)  (count)    Score     Error  Units
gc.alloc.rate.norm         ArrayList       10   ≈ 10⁻⁵             B/op
gc.alloc.rate.norm         ArrayList      100    0,001 ±   0,001   B/op
gc.alloc.rate.norm         ArrayList     1000   40,066 ±   0,003   B/op
gc.alloc.rate.norm           HashSet       10   ≈ 10⁻⁴             B/op
gc.alloc.rate.norm           HashSet      100   ≈ 10⁻⁴             B/op
gc.alloc.rate.norm           HashSet     1000   20,004 ±   6,817   B/op
Его проверил самым первым, там итератор всегда скаляризуется.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории