Многопоточность - PullRequest
       17

Многопоточность

2 голосов
/ 02 июня 2011

Я создал очень простое приложение, чтобы понять, как работает boost :: thread.Я нашел результат этого теста удивительным.4 потока выполнения заканчивают вычисления в 2 раза быстрее, чем 1 поток.Я ожидал 4-кратного повышения.Другой вопрос, почему 8 потоков не принесли никакого повышения производительности?

Я использую boost 1.46.1 и VS2008.Полный исходный код ниже.Программа была запущена на компьютере Core i5 750.

#include <iostream>
#include <vector>
#include <cmath>

#include <boost/thread.hpp>
#include <boost/timer.hpp>

typedef unsigned int uint;


struct Vector {
    float x, y, z;

    Vector() : x(0.f), y(0.f), z(0.f) {}

    float len() {
        return sqrtf(x*x + y*y + z*z);
    }

};


float norm(int a) {
    return float((a % 10) + 1) / 10.f;
}


void genVectors(std::vector<Vector>& examples) {
    srand(GetTickCount());

    for (uint i = 0; i < examples.size(); ++i) {
        examples[i].x = norm(rand());
        examples[i].y = norm(rand());
        examples[i].z = norm(rand());
    }

}

typedef std::vector<Vector> Data;
typedef Data::iterator DataIter;

typedef std::vector<float> Result;
typedef Result::iterator ResultIter;


struct Worker {
    Data   data;
    Result result;

    Worker(DataIter& dataStart,
           const DataIter& dataEnd,
           ResultIter& resultStart,
           const ResultIter& resultEnd) : data(dataStart, dataEnd), result(resultStart, resultEnd) {
        assert(data.size() == result.size());
    }

    void operator()() {
        DataIter di = data.begin();
        ResultIter ri = result.begin();

        const DataIter dend = data.end();

        for (; di != dend; ++di, ++ri) {
            *ri = di->len();
        }
    }
};


int main(int argc, char **argv) {
    const uint numThreads = 4;
    const uint seqLen = 13107200;

    std::vector<Vector> a;
    a.resize(seqLen);

    genVectors(a);  

    std::vector<float> singleThreadResult(a.size());
    assert(a.size() == singleThreadResult.size());

    boost::timer singleThreadTimer;
    for (uint i = 0; i < a.size(); ++i) {
        singleThreadResult[i] = a[i].len();
    }
    double singleThreadTime = singleThreadTimer.elapsed();

    std::vector<float> multiThreadResult(a.size());

    Worker* workers[numThreads];
    for (uint i = 0; i < numThreads; ++i) {
        uint chunkSize = seqLen / numThreads;
        assert(numThreads * chunkSize == seqLen);

        workers[i] = new Worker(a.begin() + i*chunkSize,
                                a.begin() + (i+1)*chunkSize,
                                multiThreadResult.begin() + i*chunkSize,
                                multiThreadResult.begin() + (i+1)*chunkSize);
    }

    boost::timer multiThreadTimer;
    boost::thread_group threads;
    for (uint i = 0; i < numThreads; ++i) {
        threads.create_thread(boost::ref(*workers[i]));
    }
    threads.join_all();
    double multiThreadTime = multiThreadTimer.elapsed();

    using namespace std;
    cout << "Single thread time: " << singleThreadTime << endl;
    cout << numThreads << " threads time: " << multiThreadTime << endl;

    return 0;
}

Ответы [ 5 ]

2 голосов
/ 02 июня 2011

Согласно веб-сайту Intel, процессор Core i5 750 имеет 4 ядра и поддерживает 4 потока, поэтому от 8 потоков не следует ожидать большей производительности, чем от 4. Добавляя больше потоков в свой поток программное обеспечение, чем у вас есть процессоры (или Hyperthreads), вы просто добавляете больше издержек переключения контекста.

Что касается того, почему 4 потока не быстрее, чем 2, я бы предположил, что это связано с размером рабочего набора данных. Набор данных намного больше, чем кэш-память 8 МБ, поэтому у вашего тестового приложения, вероятно, ограничена пропускная способность памяти.

Чтобы проверить это, попробуйте сравнительный анализ с набором данных, который помещается в кэш.

1 голос
/ 02 июня 2011

У вас может быть 4 ядра на вашем компьютере Core i5 750, но у вас все еще есть одна шина данных. Все используемые данные (13107200 * 3 * sizeof (float) = 157 МБ) должны проходить через эту шину данных. И затем есть результирующий вектор «просто» 13107200 * sizeof (float) = 52 МБ, который занимает тот же ресурс. Все это сильно загружает кэш, и 4 ядра тратят много времени на ожидание доступности памяти для чтения или записи.

0 голосов
/ 02 июня 2011

Когда вы используете системные потоки, нет никакой гарантии, что каждый поток будет работать на отдельном ядре. Вы не можете назначить ядро ​​для потока - это задача ОС. А учитывая, что в вашем приложении есть 4 потока, ОС может действительно запускать их на одном ядре в зависимости от общей загрузки процессора и других факторов.

0 голосов
/ 02 июня 2011

для такого сценария, я бы предпочел OpenMP #pragma parallel for или просто использовать gcc -fopenmp -D_GLIBCXX_PARALLEL и (вероятно) получить автоматическое распараллеливание ...

0 голосов
/ 02 июня 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...