在线程之间拆分任务总是值得的吗?
Is it always worth to split task between threads?
我曾经认为多线程在执行阻塞操作时是最有效的,在此期间我们可以在另一个线程上继续执行其他指令。
最近我执行了简单的测试。我创建了一个数据向量,并在线程之间平均拆分行,并将执行时间与一个线程工作线程进行比较。多线程是赢家。
这是我的代码:
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <numeric>
#include <chrono>
double g_sum = 0;
std::mutex g_mutex;
void worker(const std::vector<double>& vec)
{
const auto vectorSum = std::accumulate(vec.begin(), vec.end(), 0.0);
std::lock_guard<std::mutex> lg(g_mutex);
std::cout << "Thread-Worker adding " << vectorSum << " to final sum ("<< g_sum <<")n";
g_sum += vectorSum;
}
int main()
{
const int ROW_SIZE = 10000000;
const int threadsSize = std::thread::hardware_concurrency();
std::cout << "Task will be seprated on " << threadsSize << " threadsn";
// data vector with row for every thread
std::vector<std::vector<double>> dataVector;
double fillVal = 1.1;
for (auto i = 0; i < threadsSize; ++i, fillVal += 1.1)
{
dataVector.push_back(std::vector<double>(ROW_SIZE, fillVal));
}
std::vector<std::thread> threadContainer;
auto start = std::chrono::system_clock::now();
for (const auto& row : dataVector)
{
std::thread thread(&worker, std::ref(row));
threadContainer.push_back(std::move(thread));
}
for (auto& thread : threadContainer)
{
thread.join();
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::cout << "threads time: " << elapsed_seconds.count() << "sn";
// main thread only
g_sum = 0;
start = std::chrono::system_clock::now();
for (const auto& row : dataVector)
{
const auto vectorSum = std::accumulate(row.begin(), row.end(), 0.0);
std::cout << "Main Thread adding " << vectorSum << " to final sum ("<< g_sum <<")n";
g_sum += vectorSum;
}
end = std::chrono::system_clock::now();
elapsed_seconds = end-start;
std::cout << "one-thread time: " << elapsed_seconds.count() << "sn";
}
在具有 3 个逻辑内核的 Wandbox (https://wandbox.org/permlink/qah5auBI3ZoAe7B2( 中,多线程计时结果比单线程好两倍。
我的测试正确吗?我可以假设,跳过额外的实现时间,在线程之间拆分工作任务总是更好的选择吗?
基于任务的并行性与固定数量的线程(无超额订阅(通常是性能最佳的方法。但是,任务必须具有合理的大小,以避免过多的计划开销。IIRC 根据 TBB 的经验法则,执行任务至少需要 10k 个周期。您必须注意的一个重要细节是不同任务之间的同步。由于您通常不知道任务在哪个线程上执行,因此您必须小心不要引入死锁(例如,通过在持有锁的同时生成任务(。
但是,一个问题是否可以通过多个任务有效地解决,很大程度上取决于具体问题以及它如何映射到任务。它当然适用于您的示例,但这不能一概而论为总是更好的选择。
附带说明:我建议使用现有的任务调度框架(例如,tbb(,而不是滚动自己的框架。
没有简单的答案。 多线程计时结果取决于实现。它可能更快,也可能不更快。 有很多微妙的地方:
- 硬件线程计数。如果在硬件线程计数为 1 时创建 100 个线程,则多线程解决方案将比单线程慢。因为线程上下文切换所花费的时间会太长。
- 同步实现。例如,日志记录操作缓慢。在关键部分的上下文中记录是不好的,因为它很慢。如果要在另一个线程中移动日志记录,则可以加快速度:
std::cout << "Thread-Worker adding " << vectorSum << " to final sum ("<< g_sum <<")n";
.您可以将 vectorSum 的值放入另一个线程中,并g_sum并记录它们。这将比在锁定部分中等待输出操作更快。
相关文章:
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 异常属于C++中的线程还是进程
- C++中的线程安全删除
- C++使用params创建线程函数会导致转换错误
- 类与私有变量的其他类之间的线程安全性
- CoInitialize()在单独的线程上崩溃而不返回
- c++中的线程池
- 线程之间的布尔停止信号
- 为什么std::async使用同一个线程运行函数
- 用于矢量处理的多个线程
- C++为线程工作动态地分割例程
- 如果在 2 个线程中使用,是否值得将size_t声明为 std::atomic?