[英]Why is the C++ thread/future overhead so big
我有一个工作程序(下面的代码),当我在单独的线程中运行它时,它的运行速度较慢。 据我所知,工作程序代码和数据完全独立于其他线程。 工作人员要做的就是将节点附加到树上。 目标是让多名工人并行种植树木。
有人可以帮我理解为什么在单独的线程中运行工作程序时会有很大的开销吗?
编辑 :最初我曾两次测试WorkerFuture,现在我进行了更正,现在在无线程和延迟异步的情况下,我获得了相同(更好)的性能,并且在涉及到额外线程时会产生可观的开销。
编译命令(Linux):g ++ -std = c ++ 11 main.cpp -o main -O3 -pthread
这是输出(时间以毫秒为单位):
Thread : 4000001 size in 1861 ms
Async : 4000001 size in 1836 ms
Defer async: 4000001 size in 1423 ms
No thread : 4000001 size in 1455 ms
码:
#include <iostream>
#include <vector>
#include <random>
#include <chrono>
#include <thread>
#include <future>
struct Data
{
int data;
};
struct Tree
{
Data data;
long long total;
std::vector<Tree *> children;
long long Size()
{
long long size = 1;
for (auto c : children)
size += c->Size();
return size;
}
~Tree()
{
for (auto c : children)
delete c;
}
};
int
GetRandom(long long size)
{
static long long counter = 0;
return counter++ % size;
}
void
Worker_(Tree *root)
{
std::vector<Tree *> nodes = {root};
Tree *it = root;
while (!it->children.empty())
{
it = it->children[GetRandom(it->children.size())];
nodes.push_back(it);
}
for (int i = 0; i < 100; ++i)
nodes.back()->children.push_back(new Tree{{10}, 1, {}});
for (auto t : nodes)
++t->total;
}
long long
Worker(long long iterations)
{
Tree root = {};
for (long long i = 0; i < iterations; ++i)
Worker_(&root);
return root.Size();
}
void ThreadFn(long long iterations, long long &result)
{
result = Worker(iterations);
}
long long
WorkerThread(long long iterations)
{
long long result = 0;
std::thread t(ThreadFn, iterations, std::ref(result));
t.join();
return result;
}
long long
WorkerFuture(long long iterations)
{
std::future<long long> f = std::async(std::launch::async, [iterations] {
return Worker(iterations);
});
return f.get();
}
long long
WorkerFutureSameThread(long long iterations)
{
std::future<long long> f = std::async(std::launch::deferred, [iterations] {
return Worker(iterations);
});
return f.get();
}
int main()
{
long long iterations = 40000;
auto t1 = std::chrono::high_resolution_clock::now();
auto total = WorkerThread(iterations);
auto t2 = std::chrono::high_resolution_clock::now();
std::cout << "Thread : " << total << " size in " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms\n";
t1 = std::chrono::high_resolution_clock::now();
total = WorkerFuture(iterations);
t2 = std::chrono::high_resolution_clock::now();
std::cout << "Async : " << total << " size in " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms\n";
t1 = std::chrono::high_resolution_clock::now();
total = WorkerFutureSameThread(iterations);
t2 = std::chrono::high_resolution_clock::now();
std::cout << "Defer async: " << total << " size in " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms\n";
t1 = std::chrono::high_resolution_clock::now();
total = Worker(iterations);
t2 = std::chrono::high_resolution_clock::now();
std::cout << "No thread : " << total << " size in " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms\n";
}
看来该问题是由动态内存管理引起的。 当涉及多个线程时(即使主线程什么也不做),C ++运行时必须同步对动态内存(堆)的访问,这会产生一些开销。 我在GCC上做了一些实验,解决您的问题的方法是使用一些可伸缩的内存分配器库。 例如,当我使用tbbmalloc
,例如,
export LD_LIBRARY_PATH=$TBB_ROOT/lib/intel64/gcc4.7:$LD_LIBRARY_PATH
export LD_PRELOAD=libtbbmalloc_proxy.so.2
整个问题消失了。
原因很简单。 您不能并行执行任何操作。 当多余的线程在做某事时,主线程什么都不做(等待线程工作完成)。
在使用线程的情况下,您还有其他事情要做(处理线程和同步),因此需要权衡取舍。
要看到任何收益,您必须至少同时做两件事。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.