Почему мое параллельное управление задачами так медленно? - PullRequest
0 голосов
/ 10 февраля 2019

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

Краткий фон:

IЯ работаю над игрой, которая использует поиск пути.После добавления дополнительных сущностей возникла необходимость парализовать процесс.

Я хочу, чтобы это было максимально читабельным, поэтому я создал класс ParallelTask ​​, содержащий поток , std :: function (которая должна выполняться протектором), мьютекс для защиты некоторых операций записи и bool завершено , для которого установлено значениеистина, как только поток завершил выполнение.

Я новичок в многопоточности, поэтому я не знаю, хороший ли это подход для начала, но, тем не менее, я запутался, почему так долго выполняется.

Я написал код ниже, чтобы изолировать проблему.

int main()
{

    std::map<int, std::unique_ptr<ParallelTask>> parallelTaskDictionary;

    auto start = std::chrono::system_clock::now();

    for (size_t i = 0; i < 10; i++)
    {
         parallelTaskDictionary.emplace(i, std::make_unique<ParallelTask>());
         parallelTaskDictionary[i]->Execute();
    }

    auto end = std::chrono::system_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << elapsed.count() << std::endl;

    parallelTaskDictionary.clear();

    return 0;
}


class ParallelTask
{
public:

    ParallelTask();
    // Join the treads
    ~ParallelTask();

public:
    inline std::vector<int> GetPath() const { return path; }
    void Execute();

private:
    std::thread thread;
    mutable std::mutex mutex;

    std::function<void()> threadFunction;
    bool completed;

    std::vector<int> path;
};


ParallelTask::ParallelTask()
{
    threadFunction = [this]() {
        {
            std::lock_guard<std::mutex> lock(mutex);
            this->completed = true;
        }
    };
}

ParallelTask::~ParallelTask()
{
    if (thread.joinable())
    thread.join();
}

void ParallelTask::Execute()
{
    this->completed = false;

    // Launch the thread
    this->thread = std::thread(threadFunction);
}

Запуск этого кода дает мне от 25 до 26 миллисекунд времени выполнения.Поскольку это предназначено для использования в игре, это, конечно, недопустимо.

Как упоминалось ранее, я не понимаю, почему, тем более что сама функция ThreadFunction делает это буквально.Если вам интересно, я даже снял блокировку мьютекса, и это дало мне буквально тот же результат, так что здесь должно быть что-то еще.(Из моего исследования создание потока не должно занимать более пары микросекунд, но, может быть, я просто ошибаюсь с этим ^^)

PS: О да, и пока мы на этом, я все еще недействительно понимаю, кто должен владеть мьютексом.(Есть один глобальный или один на объект ...) ???

1 Ответ

0 голосов
/ 10 февраля 2019

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

#include <map>
#include <iostream>
#include <memory>
#include <chrono>
#include <vector>
#include <thread>
#include <mutex>
#include <functional>

class ParallelTask
{
public:

    ParallelTask();
    // Join the treads
    ~ParallelTask();

public:
    inline std::vector<int> GetPath() const { return path; }
    void Execute();

private:
    std::thread thread;
    mutable std::mutex mutex;

    std::function<void()> threadFunction;
    bool completed;

    std::vector<int> path;
};


ParallelTask::ParallelTask()
{
    threadFunction = [this]() {
        {
            auto start = std::chrono::system_clock::now();
            std::lock_guard<std::mutex> lock(mutex);
            this->completed = true;
            auto end = std::chrono::system_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
            std::cout << "elapsed time" << elapsed.count() << std::endl;
        }
    };
}

ParallelTask::~ParallelTask()
{
    if (thread.joinable())
    thread.join();
}

void ParallelTask::Execute()
{
    this->completed = false;

    // Launch the thread
    this->thread = std::thread(threadFunction);
}


int main()
{

    std::map<int, std::unique_ptr<ParallelTask>> parallelTaskDictionary;


    for (size_t i = 0; i < 10; i++)
    {
         parallelTaskDictionary.emplace(i, std::make_unique<ParallelTask>());
         parallelTaskDictionary[i]->Execute();
    }

    parallelTaskDictionary.clear();

    return 0;
}

, который дает вывод:

elapsed time1
elapsed time0
elapsed time0
elapsed time0
elapsed time0
elapsed time0elapsed time
0
elapsed time0
elapsed time0
elapsed time0

Поскольку мы исключаем время, необходимое для раскручивания потока.

И просто как проверка работоспособности, если вы действительно хотите увидеть эффектреальной работы, вы можете добавить

        using namespace std::chrono_literals;
        std::this_thread::sleep_for(2s);

к вашему threadFunction, чтобы он выглядел следующим образом

ParallelTask::ParallelTask()
{
    threadFunction = [this]() {
        {
            auto start = std::chrono::system_clock::now();
            std::lock_guard<std::mutex> lock(mutex);
            this->completed = true;
            using namespace std::chrono_literals;
            std::this_thread::sleep_for(2s);
            auto end = std::chrono::system_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
            std::cout << "elapsed time" << elapsed.count() << std::endl;
        }
    };
}

, и на выходе будет

elapsed time2000061
elapsed timeelapsed time2000103
elapsed timeelapsed time20000222000061
elapsed time2000050
2000072
elapsed time2000061
elapsed time200012
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...