有多种因素导致多线程代码的执行速度比单线程代码慢得多。
您的数组大小太小
多线程通常对特别小的数据集几乎没有影响。在您的代码的两个版本中,您都生成了 2000 个整数,并且每个逻辑线程(因为std::async
通常根据线程池实现,可能与软件线程不同)仅生成 10 个整数。以每 10 个整数方式假脱机线程的成本抵消了并行生成这些整数的好处。
如果每个线程负责,例如,每个线程负责 10,000 个整数,您可能会看到性能提升,但您可能会遇到不同的问题:
您的所有代码都受到固有串行过程的瓶颈
两个版本的代码都将生成的整数复制到主向量中。如果生成这些整数的行为本身是一个耗时的过程,那将是一回事,但在您的情况下,这可能只是生成每个整数的一小部分快速组装的问题。
因此,将每个整数复制到最终向量中的行为本质上可能并不比生成每个整数更快,这意味着正在完成的“工作”中相当大的一部分是完全串行的,这违背了多线程代码的整个目的。
修复代码
编译器非常擅长他们的工作,因此在尝试修改您的代码时,我只能勉强获得比串行代码更快的多线程代码。多次执行有不同的结果,所以我的总体评估是这种代码不适合多线程。
但这是我想出的:
#include <algorithm>
#include <vector>
#include <future>
#include<chrono>
#include<iostream>
#include<iomanip>
//#1: Constants
constexpr int BLOCK_SIZE = 500000;
constexpr int NUM_OF_BLOCKS = 20;
std::vector<int> Generate(int i) {
std::vector<int> v;
for (int j = i; j < i + BLOCK_SIZE; ++j) {
v.push_back(j);
}
return v;
}
void asynchronous_attempt() {
std::vector<std::future<void>> futures;
//#2: Preallocated Vector
std::vector<int> res(NUM_OF_BLOCKS * BLOCK_SIZE);
auto it = res.begin();
for (int i = 0; i < NUM_OF_BLOCKS * BLOCK_SIZE; i+=BLOCK_SIZE)
{
futures.push_back(std::async(
[it](int i) {
auto vec = Generate(i);
//#3 Copying done multithreaded
std::copy(vec.begin(), vec.end(), it + i);
}, i));
}
for (auto &&f : futures) {
f.get();
}
}
void serial_attempt() {
//#4 Changes here to show fair comparison
std::vector<int> res(NUM_OF_BLOCKS * BLOCK_SIZE);
auto it = res.begin();
for (int i = 0; i < NUM_OF_BLOCKS * BLOCK_SIZE; i+=BLOCK_SIZE) {
auto vec = Generate(i);
it = std::copy(vec.begin(), vec.end(), it);
}
}
int main() {
using clock = std::chrono::steady_clock;
std::cout << "Theoretical # of Threads: " << std::thread::hardware_concurrency() << std::endl;
auto begin = clock::now();
asynchronous_attempt();
auto end = clock::now();
std::cout << "Duration of Multithreaded Attempt: " << std::setw(10) << (end - begin).count() << "ns" << std::endl;
begin = clock::now();
serial_attempt();
end = clock::now();
std::cout << "Duration of Serial Attempt: " << std::setw(10) << (end - begin).count() << "ns" << std::endl;
}
这导致以下输出:
Theoretical # of Threads: 2
Duration of Multithreaded Attempt: 361149213ns
Duration of Serial Attempt: 364785676ns
鉴于这是在在线编译器上(此处),我敢打赌,多线程代码可能会在专用机器上胜出,但我认为这至少证明了性能的提高,我们至少在两者之间相当方法。
以下是我所做的更改,这些更改已在代码中标识:
- 我们显着增加了正在生成的整数的数量,以强制线程执行实际有意义的工作,而不是陷入操作系统级别的内务管理中
- 该向量具有预先分配的大小。不再频繁调整大小。
- 现在空间已经被预先分配,我们可以多线程复制而不是稍后以串行方式进行。
- 我们必须更改序列号,以便它也预先分配 + 副本,以便进行公平比较。
现在,我们已经确保所有代码确实是并行运行的,虽然它并没有对串行代码进行实质性改进,但至少不再表现出我们之前看到的退化性能损失。