Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chapter 1] 라이브러리 메서드에 Predicate<T>를 추가하는 것이 병렬성 문제를 일으키는 이유 #4

Open
jaeminkim90 opened this issue Aug 27, 2022 · 7 comments
Assignees
Labels
question Further information is requested

Comments

@jaeminkim90
Copy link
Contributor

jaeminkim90 commented Aug 27, 2022

p.55
filter(inventory, (Apple a) ->a.getWeight() >150 ); 하지만 병렬성이라는 중요성 때문에 설계자들은 이와 같은 설계를 포기했다.

image

"병렬성이라는 중요성 때문에 설계자들은 이와 같은 설계를 포기했다."고 설명한 내용이 잘 이해가 안되네요.
filter 라이브러리 메서드를 static <T> Collection<T> filter(Collection(<T> c, Predicate<T> p);와 같은 방식으로 구현하여, filterApples(inventory, (Apple a) -> a.getWeight() > 150 );의 기능을 대체하는 것이 병렬성에 왜 문제가 되는지 궁금합니다.

@jaeminkim90 jaeminkim90 added the question Further information is requested label Aug 27, 2022
@jaeminkim90 jaeminkim90 changed the title [Chapter 1] 라이브러리 메서드에 Predicate<T>를 추가하는 것이 병렬성 문제를 일으키는 이유? [Chapter 1] 라이브러리 메서드에 Predicate<T>를 추가하는 것이 병렬성 문제를 일으키는 이유 Aug 27, 2022
@kimjinwook1
Copy link
Contributor

kimjinwook1 commented Aug 27, 2022

책에서 언급한 병렬이라는 중요성 때문에 설계자들은 이와 같은 설계를 포기했다. 의 의미는 사용자가 직접 filter 메서드를 만들어 사용할 경우 문제가 생긴다고 생각합니다. (filter메서드를 만들어 사용하는 이유는 filterApples()나 filterGreenApples()와 같이 동일하게 생긴 로직을 필요할 때 마다 만들어 사용하는 것이 아니라 이미 만들어 놓은 메서드를 찾아서 사용하면 되기 때문이라고 생각됩니다. )

public interface Filter {

	static <T> Collection<T> filter(Collection<T> c, Predicate<T> p) {
		Collection<T> result = new ArrayList<T>();
		for (T element: c) {
			if (p.test(element)) {
				result.add(element);
			}
		}
		return result;
	}
}

다음과 같이 직접 filter 메서드를 구현할 경우 병렬로 처리되지 않습니다. 단순히 순차 스트림만을 구현하게 된 로직이기 때문입니다.

Stream.filter 를 돌릴 경우 아래와 같이 isParalell() 메서드가 맨 처음으로 시작됩니다. 병렬처리 여부를 먼저 확인한 후 로직을 실행하는 걸로 확인됩니다.

image

@sonchanwoo
Copy link
Contributor

sonchanwoo commented Aug 27, 2022

책 내용을 곱씹으며 천천히 읽어보았습니다.

  • 54p
  1. isHeavyApple, isGreenApple처 럼 한두 번만 사용할 메서드를 매번 정의하는 것은 귀찮은 일이다. 자바 에서는 이 문제도 간 단히 해결할 수 있다.
  • filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));
  • filterApples(inventory, (Apple a) -> a,getWeight() > 150 );

=> 람다,함수형인터페이스 덕분에, 동작 파라미터화 구현이 가능해지면서 filterApples라는 메서드의 오버로딩 없이 한번만 쓸 수 있게 되었습니다.

=> 다만 filterApples라는 메서드는 직접 만들어야 했습니다!


  1. 아마도 자바는 filter 그리고 다음과 같은 몇몇 일반적인 라이브러리 메서드를 추가하는 방향으로 발전했을 수도 있었다.
  • 위 filterApples라는 메서드 조차도 안 만들게 하기 위해서 filter(Collection c, Predicate p) 라는 메서드를 제공하는 방향으로 갈 수도 있었다는 말로 이해했습니다!(filterBanana, filterMelon을 따로 안 만들어도 됨)
  • 위 말은 filter(Collection c, Predicate p)는 실제로는 없다는 뜻으로 이해할 수 있습니다.(발전했을수도있었다)
  • 실제로 filter는 단일 매개변수 메서드인 filter(p)뿐입니다.

=> 병렬성을 위해서 filter(Collection c, Predicate p)가 아닌 우리가 지금 알고 있는 filter(Predicate p)를 제공한다까지 스스로 도출해보았습니다.


  • 55p
  1. 하지만 병렬성이라는 중요성 때문에 설계자들은 이와 같은 설계를 포기했다.
    왜 filter(Collection c, Predicate p)는 filter(Predicate p)에 비해 병렬성이 고려되지 않은 설계인건지 고민을 해보았습니다.
  • filter(Collection c, Predicate p)가 제공되었다면 병렬처리가 낄 여지가 없을것같습니다.
  • filter(Predicate p)는 엄밀히 말하면 stream().filter(Predicate p)입니다.
    • 병렬처리 하고 싶으면 parallelStream().filter(Predicate p) 하면 그만이다! 라고 생각했습니다.

@jaeminkim90
Copy link
Contributor Author

@kimjinwook1 설명해주신 내용 읽어보고 이해가 됐습니다. Filter 인터페이스 구현하는 것이 filterApples()나 filterGreenApples()의 추상화 이상의 의미는 없기 때문에 병렬성과는 무관하다는 말씀같네요.

스트림 내부에서 병렬 작업을 어떻게 구현하는지는 모르겠지만 Filter도 단순한 수준의 추상화 수준이 아니라, 병렬성을 지원하는 스트림 구현체 로직을 적용할 수 있는거 아닌가 생각했던 거 같습니다. 아마도 책에서는 디폴트 매서드나 스트림이란 개념 자체가 등장하기 전의 이야기를 예시로 든 거 같아요. 그래서 함수를 인수로 전달하는 것 뿐만 아니라 병렬성까지도 한번에 해결할 수 있는 개념(스트림)쪽으로 방향이 흘렀다고 말하는 거 같습니다.

한 가지 추가 질문 드립니다! 캡쳐 이미지로 공유해주신 ReferencePipeline.collect()가 Stream.filter()를 호출할 경우 �내부적으로 함께 호출되는건가요?

@kimjinwook1
Copy link
Contributor

kimjinwook1 commented Aug 27, 2022

@kimjinwook1 설명해주신 내용 읽어보고 이해가 됐습니다. Filter 인터페이스 구현하는 것이 filterApples()나 filterGreenApples()의 추상화 이상의 의미는 없기 때문에 병렬성과는 무관하다는 말씀같네요.

스트림 내부에서 병렬 작업을 어떻게 구현하는지는 모르겠지만 Filter도 단순한 수준의 추상화 수준이 아니라, 병렬성을 지원하는 스트림 구현체 로직을 적용할 수 있는거 아닌가 생각했던 거 같습니다. 아마도 책에서는 디폴트 매서드나 스트림이란 개념 자체가 등장하기 전의 이야기를 예시로 든 거 같아요. 그래서 함수를 인수로 전달하는 것 뿐만 아니라 병렬성까지도 한번에 해결할 수 있는 개념(스트림)쪽으로 방향이 흘렀다고 말하는 거 같습니다.

한 가지 추가 질문 드립니다! 캡쳐 이미지로 공유해주신 ReferencePipeline.collect()가 Stream.filter()를 호출할 경우 �내부적으로 함께 호출되는건가요?

네. 디버깅을 찍어보니 stream을 돌 때 ReferencePipeline.collect()가 처음으로 호출됩니다!

@jaeminkim90
Copy link
Contributor Author

@kimjinwook1 설명해주신 내용 읽어보고 이해가 됐습니다. Filter 인터페이스 구현하는 것이 filterApples()나 filterGreenApples()의 추상화 이상의 의미는 없기 때문에 병렬성과는 무관하다는 말씀같네요.
스트림 내부에서 병렬 작업을 어떻게 구현하는지는 모르겠지만 Filter도 단순한 수준의 추상화 수준이 아니라, 병렬성을 지원하는 스트림 구현체 로직을 적용할 수 있는거 아닌가 생각했던 거 같습니다. 아마도 책에서는 디폴트 매서드나 스트림이란 개념 자체가 등장하기 전의 이야기를 예시로 든 거 같아요. 그래서 함수를 인수로 전달하는 것 뿐만 아니라 병렬성까지도 한번에 해결할 수 있는 개념(스트림)쪽으로 방향이 흘렀다고 말하는 거 같습니다.
한 가지 추가 질문 드립니다! 캡쳐 이미지로 공유해주신 ReferencePipeline.collect()가 Stream.filter()를 호출할 경우 �내부적으로 함께 호출되는건가요?

네. 디버깅을 찍어보니 stream을 돌 때 ReferencePipeline.collect()가 처음으로 호출됩니다!

글쿤요 감사합니다! 제가 8버전 쓰고 있어서 동작 방식이 조금 다른가봐요ㅎㅎ

@jaeminkim90
Copy link
Contributor Author

@sonchanwoo 넵! 풀어서 설명해주신 내용 읽어보니 이해가 더 명확해진 거 같습니다. filter(Collection c, Predicate p)에 병렬처리가 낄 여지가 없었다는 말에도 공감이되네요. 기술적으로 가능하다 하더라도 메서드 시그니처 측면에서 너무 어색한 형태일 거 같습니다. 설명 감사합니다!

@kimjinwook1
Copy link
Contributor

람다 캡쳐링

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants