c++ - 添加线程会增加执行相同任务所需的时间
问题描述
在过去的 3 天里,我一直在为此苦苦挣扎。我在图像处理方面做了一些事情。我已经到了可以将工作流程分配给更多线程的地步,因为我有图像的“补丁”,我可以将其传递给不同的线程。不幸的是,无论使用 1 个或多个线程,处理图像所花费的整个时间都是相同的。
所以我开始挖掘,制作补丁的副本,这样每个线程都有自己的本地数据,停止将结果写入数组,但它仍然是一样的。所以我做了我能做的最简约的程序。创建线程后,它将制作 10x10 矩阵并将其行列式写入控制台。所以之间没有共享,唯一传递的是线程的索引。
但还是一样。我在 Linux 和 Windows 上都进行了测试。这些显示了计算一个行列式所需的时间,因此当使用两个线程时,如果没有另外说明,每个线程花费相同的时间:
视窗:
1 个线程 = 4479 毫秒
2 个线程 = 7500 毫秒
3 个线程 = 11300 毫秒
4 个线程 = 15800 毫秒
Linux:
1 线程 = 490 毫秒
2 个线程 = 478 毫秒
3 个线程 = 第一个:503 毫秒;其他两个:1230ms
4 个线程 = 1340 毫秒
首先很明显,Linux 计算相同事物的速度提高了 10 倍。没关系。然而windows并不是单线程性能更差,不管我添加多少它都会变得更糟。Linux 似乎只有在逻辑核心上完成工作负载时才会变慢。这就是为什么 1 和 2 都可以的原因,因为我有 2Core 和 HT,当使用 3 个线程时,它也会减慢使用 HT 的核心,但另一个没问题。但是无论如何,Windows都很糟糕。
有趣的是,在 Windows 上,如果我在一个核心上计算 4 个行列式或在每个核心上计算 1 个行列式,则需要相同的时间。
我用来获得这些结果的代码。我能够用 g++ 和 msvc 编译没问题。重要的是最后几个方法,有一些我不确定没有被使用的构造函数。
#include <iostream>
#include <cmath>
#include <thread>
#include <chrono>
#include <float.h>
class FVector
{
public:
FVector();
FVector(int length);
FVector(const FVector &vec);
FVector(FVector &&vec);
FVector &operator=(const FVector &vec);
FVector &operator=(FVector &&vec);
~FVector();
void setLength(int length);
int getLength() const;
double *getData();
const double* getConstData() const;
private:
double *data;
int length;
void allocateDataArray(int length);
void deallocateDataArray();
};
FVector::FVector() {
data = nullptr;
length = 0;
}
FVector::FVector(int length) {
data = nullptr;
this->length = length;
allocateDataArray(length);
for (int i = 0; i < length; i++) {
data[i] = 0.;
}
}
FVector::FVector(const FVector &vec) {
allocateDataArray(vec.length);
length = vec.length;
for (int i = 0; i < length; i++) {
data[i] = vec.data[i];
}
}
FVector::FVector(FVector &&vec) {
data = vec.data;
vec.data = nullptr;
length = vec.length;
}
FVector &FVector::operator=(const FVector &vec) {
deallocateDataArray();
if (data == nullptr) {
allocateDataArray(vec.length);
for (int i = 0; i < vec.length; i++) {
data[i] = vec.data[i];
}
length = vec.length;
}
return *this;
}
FVector &FVector::operator=(FVector &&vec) {
deallocateDataArray();
if (data == nullptr) {
data = vec.data;
vec.data = nullptr;
length = vec.length;
}
return *this;
}
FVector::~FVector() {
deallocateDataArray();
}
void FVector::allocateDataArray(int length) {
data = new double[length];
}
void FVector::deallocateDataArray() {
if (data != nullptr) {
delete[] data;
}
data = nullptr;
}
int FVector::getLength() const {
return length;
}
double *FVector::getData() {
return data;
}
void FVector::setLength(int length) {
deallocateDataArray();
allocateDataArray(length);
this->length = length;
}
const double* FVector::getConstData() const {
return data;
}
class FMatrix
{
public:
FMatrix();
FMatrix(int columns, int rows);
FMatrix(const FMatrix &mat);
FMatrix(FMatrix &&mat);
FMatrix& operator=(const FMatrix &mat);
FMatrix& operator=(FMatrix &&mat);
~FMatrix();
FVector *getData();
const FVector* getConstData() const;
void makeIdentity();
int determinant() const;
private:
FVector *data;
int columns;
int rows;
void deallocateDataArray();
void allocateDataArray(int count);
};
FMatrix::FMatrix() {
data = nullptr;
columns = 0;
rows = 0;
}
FMatrix::FMatrix(int columns, int rows) {
data = nullptr;
allocateDataArray(columns);
for (int i = 0; i < columns; i++) {
data[i].setLength(rows);
}
this->columns = columns;
this->rows = rows;
}
FMatrix::FMatrix(const FMatrix &mat) {
data = nullptr;
allocateDataArray(mat.columns);
for (int i = 0; i < mat.columns; i++) {
data[i].setLength(mat.data[i].getLength());
data[i] = mat.data[i];
}
columns = mat.columns;
rows = mat.rows;
}
FMatrix::FMatrix(FMatrix &&mat) {
data = mat.data;
mat.data = nullptr;
columns = mat.columns;
rows = mat.rows;
}
FMatrix &FMatrix::operator=(const FMatrix &mat) {
deallocateDataArray();
if (data == nullptr) {
allocateDataArray(mat.columns);
for (int i = 0; i < mat.columns; i++) {
data[i].setLength(mat.rows);
data[i] = mat.data[i];
}
}
columns = mat.columns;
rows = mat.rows;
return *this;
}
FMatrix &FMatrix::operator=(FMatrix &&mat) {
deallocateDataArray();
data = mat.data;
mat.data = nullptr;
columns = mat.columns;
rows = mat.rows;
return *this;
}
FMatrix::~FMatrix() {
deallocateDataArray();
}
void FMatrix::deallocateDataArray() {
if (data != nullptr) {
delete[] data;
}
data = nullptr;
}
void FMatrix::allocateDataArray(int count) {
data = new FVector[count];
}
FVector *FMatrix::getData() {
return data;
}
void FMatrix::makeIdentity() {
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
if (i == j) {
data[i].getData()[j] = 1.;
}
else {
data[i].getData()[j] = 0.;
}
}
}
}
int FMatrix::determinant() const {
int det = 0;
FMatrix subMatrix(columns - 1, rows - 1);
int subi;
if (columns == rows && rows == 1) {
return data[0].getData()[0];
}
if (columns != rows) {
//throw EXCEPTIONS::SINGULAR_MATRIX;
}
if (columns == 2)
return ((data[0].getConstData()[0] * data[1].getConstData()[1]) - (data[1].getConstData()[0] * data[0].getConstData()[1]));
else {
for (int x = 0; x < columns; x++) {
subi = 0;
for (int i = 0; i < columns; i++) {
for (int j = 1; j < columns; j++) {
if (x == i) {
continue;
}
subMatrix.data[subi].getData()[j - 1] = data[i].getConstData()[j];
}
if (x != i) {
subi++;
}
}
det += (pow(-1, x) * data[x].getConstData()[0] * subMatrix.determinant());
}
}
return det;
}
const FVector* FMatrix::getConstData() const {
return data;
}
class FCore
{
public:
FCore();
~FCore();
void process();
private:
int getMaxThreads() const;
void joinThreads(std::thread *threads, int max);
};
void parallelTest(int i) {
auto start = std::chrono::high_resolution_clock::now();
FMatrix m(10, 10);
m.makeIdentity();
std::cout << "Det: " << i << "= " << m.determinant() << std::endl;
auto finish = std::chrono::high_resolution_clock::now();
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(finish - start);
std::cout << "Time: " << microseconds.count() / 1000. << std::endl;
}
FCore::FCore()
{
}
FCore::~FCore()
{
}
void FCore::process() {
/*********************************************/
/*Set this to limit number of created threads*/
int threadCount = getMaxThreads();
/*********************************************/
/*********************************************/
std::cout << "Thread count: " << threadCount;
std::thread *threads = new std::thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = std::thread(parallelTest, i);
}
joinThreads(threads, threadCount);
delete[] threads;
getchar();
}
int FCore::getMaxThreads() const {
int count = std::thread::hardware_concurrency();
if (count == 0) {
return 1;
}
else {
return count;
}
}
void FCore::joinThreads(std::thread *threads, int max) {
for (int i = 0; i < max; i++) {
threads[i].join();
}
}
int main() {
FCore core;
core.process();
return 0;
}
显然我已经用更原始的测试做了一些测试,就像添加数字一样简单,它是一样的。所以我只是想问问你们中是否有人偶然发现过与此类似的东西。我知道我无法在 Windows 上像在 Linux 上那样享受美妙的时光,但至少扩展性可能会更好。
在 Win7/Linux 英特尔 2C+2T 和 Win10 锐龙 8C+8T 上测试。发布时间为2C+2T
解决方案
推荐阅读
- reactjs - 为什么 RxJS 间隔从最后一个值开始而不是从“0”开始?
- python - Pandas DataFrame 查询的过滤问题
- c++ - 我必须制作一个汽车经销商程序,但我不知道如何从文件中读取
- python - 如何检测点和矩形之间的碰撞方向?
- excel - 比较 2 个字段并获取第一个有效值,否则在 excel 中获取任何一个值
- go - 为什么 Go 中缺少 Bool 的 Token 类型?
- swift - 为什么我的 MKMapViewDelegate 没有反应,尽管找到了路线?
- spring-boot - 使用spring cloud gateway的Rest api的速率限制不起作用
- flutter - 布尔值未在函数中更新
- javascript - Javascript 使用 setAttribute 和下拉选择将变量从更改传递到外部函数