C ++ 11의 async (launch :: async)로 인해 비싼 스레드 생성을 피하기 위해 스레드 풀이 더 이상 사용되지 않습니까?
느슨하게이 질문에 관련되어 있습니다이 표준 : ++ 11 C에서 풀링 스레드를? . 질문은 다르지만 의도는 동일합니다.
질문 1 : 고가의 스레드 생성을 피하기 위해 여전히 자체 (또는 타사 라이브러리) 스레드 풀을 사용하는 것이 합리적입니까?
다른 질문의 결론은 std::thread풀링 에 의존 할 수 없다는 것입니다 (그렇거나 그렇지 않을 수 있음). 그러나 std::async(launch::async)풀링 될 확률이 훨씬 높은 것 같습니다.
표준에 의해 강제되는 것은 아니지만 IMHO 나는 스레드 생성이 느리면 모든 우수한 C ++ 11 구현에서 스레드 풀링을 사용할 것으로 기대합니다. 새로운 스레드를 만드는 것이 저렴한 플랫폼에서만 항상 새로운 스레드를 생성 할 것으로 기대합니다.
질문 2 : 이것은 단지 내가 생각하는 것이지만 그것을 증명할 사실이 없습니다. 나는 착각을 잘 할 수 있습니다. 교육받은 추측입니까?
마지막으로, 여기에서는 스레드 생성을 다음과 같이 표현할 수있는 방법을 보여주는 샘플 코드를 제공했습니다 async(launch::async).
예 1 :
thread t([]{ f(); });
// ...
t.join();
된다
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
예 2 : 실을 발사하고 잊어 버리십시오
thread([]{ f(); }).detach();
된다
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
질문 3 : async버전보다 thread버전을 원하십니까?
나머지는 더 이상 질문의 일부가 아니라 설명을 위해서만 :
반환 값을 더미 변수에 할당해야하는 이유는 무엇입니까?
불행히도, 현재 C ++ 11 표준은 std::async소멸자가 실행되는 것처럼 액션이 종료 될 때까지 차단되는 반환 값을 강제로 캡처하도록 합니다. 일부에서는 표준에서 오류로 간주됩니다 (예 : Herb Sutter).
cppreference.com 의이 예제는이를 잘 보여줍니다.
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
또 다른 설명 :
내가 알고 스레드 풀은 다른 합법적 인 용도가있을 수 있지만,이 질문에 나는 비싼 쓰레드 생성 비용을 피할 수있는 측면에 관심이 있어요 .
스레드 풀이 매우 유용한 상황, 특히 리소스를 더 많이 제어 해야하는 경우가 여전히 있다고 생각합니다. 예를 들어, 서버는 빠른 응답 시간을 보장하고 메모리 사용 예측 성을 높이기 위해 고정 된 수의 요청 만 동시에 처리하기로 결정할 수 있습니다. 스레드 풀은 괜찮습니다.
스레드 로컬 변수는 자신의 스레드 풀에 대한 인수 일 수도 있지만 실제로 관련이 있는지 확실하지 않습니다.
std::thread스레드 로컬 변수를 초기화하지 않고 시작 하여 새 스레드를 만듭니다. 아마도 이것은 당신이 원하는 것이 아닐 수도 있습니다.- 에 의해 생성 된 스레드
async에서 스레드를 재사용 할 수 있었기 때문에 다소 불분명합니다. 내 이해에서 스레드 로컬 변수가 재설정 될 수는 없지만 실수 할 수 있습니다. - Using your own (fixed-size) thread pools, on the other hand, gives you full control if you really need it.
Question 1:
I changed this from the original because the original was wrong. I was under the impression that Linux thread creation was very cheap and after testing I determined that the overhead of function call in a new thread vs. a normal one is enormous. The overhead for creating a thread to handle a function call is something like 10000 or more times slower than a plain function call. So, if you're issuing a lot of small function calls, a thread pool might be a good idea.
It's quite apparent that the standard C++ library that ships with g++ doesn't have thread pools. But I can definitely see a case for them. Even with the overhead of having to shove the call through some kind of inter-thread queue, it would likely be cheaper than starting up a new thread. And the standard allows this.
IMHO, the Linux kernel people should work on making thread creation cheaper than it currently is. But, the standard C++ library should also consider using pool to implement launch::async | launch::deferred.
And the OP is correct, using ::std::thread to launch a thread of course forces the creation of a new thread instead of using one from a pool. So ::std::async(::std::launch::async, ...) is preferred.
Question 2:
Yes, basically this 'implicitly' launches a thread. But really, it's still quite obvious what's happening. So I don't really think the word implicitly is a particularly good word.
I'm also not convinced that forcing you to wait for a return before destruction is necessarily an error. I don't know that you should be using the async call to create 'daemon' threads that aren't expected to return. And if they are expected to return, it's not OK to be ignoring exceptions.
Question 3:
Personally, I like thread launches to be explicit. I place a lot of value on islands where you can guarantee serial access. Otherwise you end up with mutable state that you always have to be wrapping a mutex around somewhere and remembering to use it.
I liked the work queue model a whole lot better than the 'future' model because there are 'islands of serial' lying around so you can more effectively handle mutable state.
But really, it depends on exactly what you're doing.
Performance Test
So, I tested the performance of various methods of calling things and came up with these numbers on an 8 core (AMD Ryzen 7 2700X) system running Fedora 29 compiled with clang version 7.0.1 and libc++ (not libstdc++):
Do nothing calls per second: 35365257
Empty calls per second: 35210682
New thread calls per second: 62356
Async launch calls per second: 68869
Worker thread calls per second: 970415
And native, on my MacBook Pro 15" (Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz) with Apple LLVM version 10.0.0 (clang-1000.10.44.4) under OSX 10.13.6, I get this:
Do nothing calls per second: 22078079
Empty calls per second: 21847547
New thread calls per second: 43326
Async launch calls per second: 58684
Worker thread calls per second: 2053775
For the worker thread, I started up a thread, then used a lockless queue to send requests to another thread and then wait for a "It's done" reply to be sent back.
The "Do nothing" is just to test the overhead of the test harness.
It's clear that the overhead of launching a thread is enormous. And even the worker thread with the inter-thread queue slows things down by a factor of 20 or so on Fedora 25 in a VM, and by about 8 on native OS X.
I created a Bitbucket project holding the code I used for the performance test. It can be found here: https://bitbucket.org/omnifarious/launch_thread_performance
'IT박스' 카테고리의 다른 글
| clang으로 더 빠른 코드 완성 (0) | 2020.08.06 |
|---|---|
| Malloc vs New – 다른 패딩 (0) | 2020.08.06 |
| Python 3 온라인 통역사 / 쉘 (0) | 2020.08.06 |
| Twitter API에서 오류 215, 잘못된 인증 데이터가 반환 됨 (0) | 2020.08.06 |
| HTML5 입력 유형 날짜 시간이 이미이를 지원하는 브라우저에서 제거 된 이유는 무엇입니까? (0) | 2020.08.06 |