Добавление потоков увеличивает время, необходимое для выполнения той же задачи - PullRequest
0 голосов
/ 16 декабря 2018

Я боролся с этим последние 3 дня.Я делаю некоторые вещи в обработке изображений.Я пришел к тому, что смог распределить рабочий процесс по нескольким потокам, так как у меня были «заплатки» изображения, которые я мог передавать различным потокам.К сожалению, все время, затрачиваемое на обработку изображения, было одинаковым, независимо от использования 1 или более потоков.

Поэтому я начал копать, делать копии патчей, чтобы каждый поток имел свои локальные данные, прекратил запись результатак массиву, но это было все то же самое.Поэтому я сделал самую минималистичную программу, какую только мог иметь.После того, как поток был создан, он сделал бы матрицу 10х10 и записал бы ее определитель в консоль.Так что между ними ничего не было, единственное, что было передано, это индекс потока.

Но все было по-прежнему.Я сделал тесты как на Linux, так и на Windows.Они показывают время, необходимое для вычисления одного определителя, поэтому при использовании двух потоков каждый из них занимает одинаковое количество времени, если не указано иное:

Windows:

1 Thread = 4479ms

2 потока = 7500 мс

3 потока = 11300 мс

4 потока = 15800 мс

Linux:

1 поток = 490 мс

2 потока = 478 мс

3 потока = первый: 503 мс;Два других: 1230мс

4 Потока = 1340мс

Первое, что очевидно, - Linux вычисляет то же самое в 10 раз быстрее.Не берите в голову.Тем не менее, Windows не так, что производительность одного потока хуже, она ухудшается независимо от того, сколько я добавляю.Кажется, что Linux замедляется только тогда, когда нагрузка выполняется на логическом ядре.Вот почему 1 и 2 в порядке, так как у меня 2Core с HT, а при использовании 3 потоков это замедляет работу ядра, которое также использует HT, но с другим все в порядке.Однако окна отстой, несмотря ни на что.

Забавно, что на окнах это занимает + - столько же времени, если я вычисляю 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 intel 2C + 2T и Win10 ryzen 8C + 8T.Время опубликовано с 2C + 2T

...