Skip to content

Commit

Permalink
[7회차] 아이템 48, 53 - 폰드(권규택) (#70)
Browse files Browse the repository at this point in the history
* 아이템 48 : 스트림 병렬화는 주의해서 적용하라

* 아이템 53 : 가변 인수는 신중히 사용하라
  • Loading branch information
tackyu committed Jun 6, 2024
1 parent 66e3807 commit 420838c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## 스트림 병렬화는 주의해서 적용하라
자바 8부터 parallel 메서드 호출을 통해 파이프라인을 병렬 실행할 수 있는 스트림을 지원.
### 성능 개선이 되지 않는 병렬화

- 데이터 소스가 **Stream.iterate**인 경우
- ~~iterate 연산은 순서에 의존하는 연산이기 때문에 스트림 원소를 분할하기 어렵다.~~
- 중간 연산으로 **limit**을 사용하는 경우.
- 파이프라인 병렬화는 limit을 다룰때 CPU 코어가 남는다면 원소를 몇 개 더 처리한 후,
제한된 개수 이후의 결과를 버려도 아무런 해가 없다고 가정.

### 적합한 자료 구조
- 데이터 소스가 **ArrayList, HashMap, HashSet, ConcurrentHashMap**의 인스턴스
- **배열, int 범위, long 범위**
- 모두 데이터를 원하는 크기로 정확하고 쉽게 나눌 수 있다.
- 다수의 스레드에 일을 분배하기 좋다.
- **참조 지역성**이 뛰어나다.
- 이웃한 원소의 참조들이 메모리에 연속해서 저장되어 있음
- 참조 지역성이 낮으면, 스레드는 데이터가 주 메모리에서 캐시 메모리로 전송되어 오기 까지 대기.
- 다량의 데이터를 처리하는 벌크 연산을 병렬화 할 때 매우 중요한 요소로 작용

### 종단 연산
종단 연산의 동작 방식도 병렬 수행 효율에 영향을 미침.
- 적합
- 축소(reduction): 모든 원소를 하나로 합치는 작업
- ```reduce, max, min, count, sum```
- 조건에 맞으면 바로 반환하는 메서드
- ```anyMatch, allMatch, nonMatch```

- 부적합
종단 연산에서 수행하는 작업량이 파이프라인 전체 작업에서 상당 비중을 차지하면서 순차적인 연산인 경우
- 가변 축소(mutable reduction)
- 컬렉션들을 합치는 부담이 크다.
- ```collect```

### 스트림 명세
스트림을 잘못 병렬화 하면 성능이 나빠지는 것 뿐만 아니라 결과 자체가 잘못 될 수 있다.(**안전 실패**)
Stream의 reduce 연산에 전달되는 accumulator, combiner 함수는 다음 조건을 만족해야 한다.
- 결합 법칙(associative)를 만족해야 한다.
- 간섭받지 않아야 한다.(non-interfering)
- 데이터 소스의 요소를 변경하지 않아야 함.
- 상태를 가지지 말아야 한다.(stateless)
- 연산 중에 외부 상태를 변경하지 않거나 의존하지 않아야 함.

### 사용 유의
- 스트림 병렬화는 오직 성능 최적화 수단
- 병렬화에 드는 추가 비용 고려해라
- 스트림 요소 분할/결합
- 스레드 할당
- 성능 테스트 해서 병렬화 할 가치가 있는지 확인해라
- 스트림 안의 원소 수와 원소당 수행되는 코드 줄 수를 곱해 최소 수십만은 되야 성능 향상을 접할 수 있다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## 가변 인수는 신중히 사용하라

가변 인수 메서드는 명시한 타입의 인수를 0개 이상 받을 수 있다.
메서드를 호출하면 인수의 개수와 길이가 같은 배열을 생성 후, 메서드에 건네준다.
- 인수가 1개 이상이어야 하는 경우
- 0개가 받아지도록 설계하는 건 🤮
```java
public void min(int... args) {
if (args.length == 0) {
throw new IllegalArgumentException();
}
int min = args[0];
for (int arg : args) {
if (arg < min) {
min = arg;
}
}
System.out.println(min);
}
```
- 가변 인수의 갯수로 검증하게끔 하면 런타임에 에러를 확인 할 수 있다.
- 지저분한 코드
- **필수 매개변수**를 추가로 받도록 하면 된다.
```java
public void min(int firstArg, int... args){
int min = firstArg;
for(int arg : args){
if(arg < min){
min = arg;
}
}
System.out.println(min);
}
```
- **성능**에 민감한 상황에 가변인수를 사용하는 패턴
가변 인수 메서드는 호출 될 떄마다 배열을 새로 할당하고 초기화
이 비용을 감당하기 힘들지만, 가변 인수의 유연성이 필요하다면 다음과 같은 패턴을 적용할 수 있다.<br><br>
ex) 해당 메서드의 호출의 대부분이 인수를 3개 이하로 사용한다면, 다음과 같이 메서드를 다중정의
```java
public void method(){ ... }
public void method(int a1){ ... }
public void method(int a1, int a2){ ... }
public void method(int a1, int a2, int a3){ ... }
public void method(int a1, int a2, int a3, int... args){ ... }
```
가변 인수를 사용하는 메서드의 호출 책임을 줄일 수 있다.

0 comments on commit 420838c

Please sign in to comment.