다른 날에 내가 사용한 또 다른 기술이 있습니다.
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
이 Collections.nCopies
호출은 사용자가 제공하는 모든 값 의 List
포함 n
복사본을 만듭니다 . 이 경우 박스형 Integer
값 1입니다. 물론 실제로 n
요소가 있는 목록을 생성하지는 않습니다 . 값과 길이 만 포함 된 "가상화 된"목록을 만들고 get
범위 내 호출 은 값을 반환합니다. 이 nCopies
메서드는 Collections Framework가 JDK 1.2에서 도입 된 이후로 사용되었습니다. 물론 그 결과로부터 스트림을 생성하는 기능은 Java SE 8에 추가되었습니다.
큰 거래, 거의 동일한 줄 수에서 동일한 작업을 수행하는 또 다른 방법입니다.
그러나이 기술은 IntStream.generate
및 IntStream.iterate
접근 방식 보다 빠르며 놀랍게도 IntStream.range
접근 방식 보다 빠릅니다 .
의 경우 iterate
와 generate
결과는 아마도 너무 놀라운 일이 아니다. 스트림 프레임 워크 (실제로 이러한 스트림에 대한 분할 자)는 람다가 매번 다른 값을 잠재적으로 생성하고 무제한의 결과를 생성한다는 가정을 기반으로 구축됩니다. 이것은 병렬 분할을 특히 어렵게 만듭니다. 이 iterate
메서드는 또한 각 호출에 이전 호출의 결과가 필요하기 때문에 문제가 있습니다. 스트림 사용 그래서 generate
및 iterate
반복 상수를 생성하기위한 아주 잘하지 않는다.
의 상대적으로 낮은 성능 range
은 놀랍습니다. 이것도 가상화되므로 요소가 실제로 모두 메모리에 존재하는 것은 아니며 크기가 미리 알려집니다. 이것은 빠르고 쉽게 병렬화 할 수있는 분할자를 만들 것입니다. 그러나 놀랍게도 잘되지 않았습니다. 아마도 그 이유는 range
범위의 각 요소에 대한 값을 계산 한 다음 함수를 호출해야하기 때문일 것입니다. 하지만이 함수는 입력을 무시하고 상수를 반환하므로 인라인 처리되지 않고 종료되지 않은 것이 놀랍습니다.
이 Collections.nCopies
기술은 값을 처리하기 위해 boxing / unboxing을 수행해야 List
합니다.. 값이 매번 같기 때문에 기본적으로 한 번 상자에 넣어 모든 n
복사본에서 해당 상자를 공유합니다 . 나는 boxing / unboxing이 고도로 최적화되고 내재화되어 있고 잘 인라인 될 수 있다고 생각합니다.
코드는 다음과 같습니다.
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
다음은 JMH 결과입니다. (2.8GHz Core2Duo)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
ncopies 버전에는 상당한 차이가 있지만 전반적으로 범위 버전보다 20 배 더 빠릅니다. (하지만 내가 뭔가 잘못했다고 믿고 싶어요.)
이 nCopies
기술이 얼마나 잘 작동 하는지 놀랐습니다 . 내부적으로는 가상화 된 목록의 스트림이 단순히 IntStream.range
! 이 작업을 빠르게 수행하려면 특수 분할기를 만들어야한다고 예상했지만 이미 꽤 괜찮은 것 같습니다.