웨지
7장문제 나갑니당~~ 7장 넘 어렵네요
O/X 3개 낼게요 이유도 적어주셔요병렬 스트림 문제
- 동시성 문제를 해결했다면, 병렬 스트림을 활용하는게 항상 성능적으로 우수하다.
- 내부적으로 스트림이 분할되는 기준에 대해서는 개발자가 알 수 없다.
- Spliterator의 Characteristics 옵션은 Collectors의 것과 동일하다
나의 답
X 그렇지 않다. 원소의 개수가 적을 경우, 병렬 계산을 위한 스래드 생성 비용이 클 수도 있다.
X 그렇지 않다. 스트림의 분할 기준은 개발자가 정해서 구현한다(기본으로는 프로세서 개수 기준이다).
X 그렇지 않다. 아쉽게도 둘은 서로 다른 요소를 가지고 있다
웨지의 답
- x ,스레드를 스위칭하는 오버헤드가 더 들수도 있다. 벤치마크를 통해 오버헤드에 걸리는 시간 vs 병렬스트림 수행으로 보는 이득보는 시간 을 검증해보아야 한다
- x, spliterator를 살펴봄으로써 알 수 있다. custom spliterator를 구현함으로써 기준을 정해줄 수 도 있다.
- x, ORDERED, DISTINCT, SORTED, SIZED, NON-NULL, IMMUTABLE, CONCURRENT, SUBSIZED 가 있으며 Collector와 겹치는 Ordered, Concurrent가 있으나 다른 방식으로 정의되었다고 한다
완태
7장포크/조인 부분에서, fork의 개념에 대해서 다시금 생각해보면 좋을 것 같아서, 생각을 해볼만한 질문들을 남겨요!
- Github에서 fork를 떠서 프로젝트를 진행했었는데요, Github fork와 7장에서 설명하는 fork와는 어떤 개념적 유사성이 있을까요? 자유롭게 얘기해주세요
- 크롬 브라우저를 많이 사용하는데요, 동시에 여러 탭에 유튜브 영상을 틀어놓더라도 병렬로 실행이 됩니다. 크롬 브라우저의 탭 작동 방식을 fork로 설명을 해주세요!(실제로 그런지는 저도 모릅니다.. ㅎ)
다소 질문들이 추상적인데, fork의 개념을 잘 이해하냐가 이번 장에 중요한 요소라 생각해서 내봤습니다!
나의 답
1. 데이터를 가지고 와서 각자 처리하는 부분에서 유사성이 있다. fork를 통해 코드를 수정하고 pr을 날리는 행위가 포크조인과 비슷하다고 볼 수 있다. 다만, 데이터 일부를 가져오느냐, 전체를 가지고 오느냐의 차이점이 존재한다.
2. 개인적으로는 크롬의 탭과 포크조인의 포크의 유사성을 잘 모르겠다. 포크는 부분 작업을 처리하기 위해 스레드로 분리하고, 크롬은 각 탭이 프로세스로 작동하는 차이가 존재하며 데이터를 공유 하는지는...? 모르겠다.
<주의> 7장 아닙니다.
파즈의 stream에서 count의 감명을 받아.. 한번 해봤는데요,,,
이 테스트의 출력문은 어떻게 될까요??(주의: 함정 있음; - 나한테만 함정일수도, 함정이란 말이 함정일수도, (아무말 대잔치))
@Test
void withFilteringAndSorting() {
List<Car> cars = Arrays.asList( new Car(100), new Car(1000),new Car(2000),new Car(3000));
Stream<Car> stream1 = cars.stream();
Stream<Car> stream2 = stream1.peek(Car::printPrice);
Stream<Car> stream3 = stream2.filter(Car::isExpensive);
System.out.println("filtering");
Stream<Car> stream4 = stream3.peek(Car::printPrice);
Stream<Car> stream5 = stream4.sorted(Comparator.comparing(Car::getPrice));
System.out.println("sorting");
Stream<Car> stream6 = stream5.peek(Car::printPrice);
System.out.println(stream6.count());
}
public class Car{
private final int price;
public Car(int price) {
this.price = price;
}
public boolean isExpensive() {
return price > 1000;
}
public void printPrice() {
System.out.println("Car: " + price);
}
public int getPrice() {
return price;
}
}
나의 답
filtering
sorting
2
완태의 답
filtering
sorting
Car :100
Car :1000
Car :2000
Car :2000
Car :3000
Car :3000
Car :2000
Car :3000
2
와일더
1. 반복작업을 진행할 때, 반복문보다 병렬스트림으로 처리하는것이 효율적이다. (O/X)
2. LongStream.rangeClosed 메서드는 기본형 long을 직접 사용하므로 박싱과 언박싱 오버헤드가 사라진다. (O/X)
3. 처리해야할 요소 수와 처리하는데 드는 비용 중 처리하는게 드는 비용이 클수록 병렬스트림으로 성능 개선 가능성이 높아진다. (O/X)
4. Spliterator 를 사용하려면 어떻게 동작하는지 이해하고 직접 구현해야한다. (O/X)5. Custom Spliterator 구현하기
함수형으로 “검색하고자 하는 단어 수” 를 구하는 메서드를 구현해보자.
아래 기능이 동작하도록 WordCounter 를 만들어보자.
나의 답
1. x 데이터 요수의 수에 따라 달라진다.
2. x 어떻게 사용하느냐에 따라 다르다.
3. 맞다.
4. 아니다. 기본적으로 제공해준다.
5.
나봄
포크/조인 프레임워크 관련 문제입니다!!
책에는 안 나오는 내용이지만 그래도 포크/조인 프레임워크를 더 잘 이해하기 위해서는 원래의 ExecutorService와 어떤 점이 다른지 아는 것이 좋을 것 같아서 이번 문제를 준비하게 되었습니다!
아래의 그림은 전형적인 ExecutorService 흐름입니다!
- 과연 포크/조인 프레임워크는 어떤 방식으로 ExecutorService를 새롭게 구현했을까요? (힌트 : 쓰레드 풀에서 쓰레드가 조금씩 달라져요!!)
2. 그리고 어떠한 문제점 떄문에 포크/조인 프레임워크가 나왔을까요?
3. 가끔 일반적인 ExecutorService를 사용하는 것보다 포크/조인 프레임워크가 더 느릴 때가 있는데 어떤 이유들을 생각할 수 있을까요? (edited)
나의 답
1. 모르겠다.
나봄의 답
- 각 쓰레드에게 Task queue가 추가가 된다(deque 형식으로). 그리고 각 쓰레드 테스크 큐에서 포크작업이 일어나고 다른 쓰레드는 그 테스크 큐에서 포크된 테스크를 스틸해와 작업을 시작한다.
- 만일 3가지 작업과 3가지 쓰레드가 있다고 가정해본다. 하나의 쓰레드는 3초, 하나의 쓰레드는 5초, 마지막 다른 쓰레드는 10초가 작업을 하는 데 걸리는 시간이라면 모든 작업을 모아서 결과물로 돌려주려면 총 10초가 걸리게 된다. (3초 걸린 쓰레드와 5초 걸린 쓰레드는 그동안 노는 쓰레드가 되어버린다.) 하지만 노는 쓰레드가 없이 모두 동등하게 작업을 나눌 수만 있다면 걸리는 시간은 총 6초로 줄일 수가 있다. 이렇게 노는 쓰레드가 없이 모두 동등하게 작업을 나누기 위해 포크/조인 프레임워크가 나오게 되었다. 그렇기때문에 동등하게 작업을 나누는 포킹작업이 아주 중요하다!
- 기존 ExecutorService와 가장 다른 점은 쓰레드 개별로 작업 큐를 가진다는 것과 포킹과 조인 작업이 생긴다는 것이다. 만일 기존 ExecutorService에 task가 들어올 때 이미 거의 동등하게 나눠진 task로 들어온다면 쓰레드 개별로 작업 큐를 만드는 비용과 포킹 비용이 없기때문에 더 빠르다.
찰리
7장 문제
Spliterator가 너무 어려워서 시간을 들여서 다시 봐야할거같아요..
O/X 퀴즈로 가겠습니다
- ForkJoin 프레임워크에서 Task를 분할할 때 fork() 했던 Task는 비동기 실행되어 해당 Task의 결과를 기다리지 않고 다음 작업을 수행한다. (책의 예시에서는 leftTask)
- ForkJoin 프레임워크에서 분할된 Task에 compute() 를 호출하면 더이상 분할되지 않고 실행된다.
- LinkedList는 병렬 스트림을 구성하는 자료구조로 좋은 편이다.
- 스트림에 squential()과 parallel()이 동시에 사용된 경우 뒤에 선언한 것은 무시된다.
.squential() // 순차 스트림으로 동작한다.
.parallel()
.squential() // 순차 스트림으로 동작한다.
.parallel()
5. 소량의 데이터를 병렬 스트림으로 사용할 때 이득을 얻을 수 없는 이유는 병렬화로 얻을 수 있는 이득이 병렬화 비용보다 크기 때문이다.(병렬화 비용 < 병렬화로 얻을 수 있는 이득)
나의 답
1. 맞다. 한쪽은 비동기, 한쪽은 동기 실행 시킴으로써 스레드 생성의 성능적 이점을 얻을 수 있다.
2. 맞다. 동기적으로 실행된다.
3. 아니다. LinkedList는 특정 요서를 찾기 위해 선형탐색을 진행해야 하므로 성능상 안좋을 수 있다.
4. 아니다. 뒤에 선언된 것으로 실행된다.
5. 아니다. 병렬화로 얻을 수 있는 이득이 병렬화 비용보다 작다.
찰리의 답
- O - fork() 했던 Task는 비동기 실행되어 결과를 기다리지 않고 join()을 통해서 결과를 받습니다.
- X - compute()를 재귀적으로 호출해서 분할하는것
- X - LinkedList는 분할하기 위해 모든 요소를 탐색해야 해서 분해성이 나쁜 자료구조 입니다.
- X - 가장 마지막으로 호출된 메서드가 적용됩니다.
- X - 병렬화 비용 > 병렬화로 얻을 수 있는 이득
댓글