2016-03-21 4 views
0

Я сделал тестовый код между std :: thread и std :: async.std :: thread to std :: async делает HUGE прирост производительности. Как это возможно?

#include <iostream> 
#include <mutex> 
#include <fstream> 
#include <string> 
#include <memory> 
#include <thread> 
#include <future> 
#include <functional> 
#include <boost/noncopyable.hpp> 
#include <boost/lexical_cast.hpp> 
#include <boost/filesystem.hpp> 
#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/asio.hpp> 

namespace fs = boost::filesystem; 
namespace pt = boost::posix_time; 
namespace as = boost::asio; 
class Log : private boost::noncopyable 
{ 
public: 
    void LogPath(const fs::path& filePath) { 
     boost::system::error_code ec; 
     if(fs::exists(filePath, ec)) { 
      fs::remove(filePath); 
     } 
     this->ofStreamPtr_.reset(new fs::ofstream(filePath)); 
    }; 

    void WriteLog(std::size_t i) { 
     assert(*this->ofStreamPtr_); 
     std::lock_guard<std::mutex> lock(this->logMutex_); 
     *this->ofStreamPtr_ << "Hello, World! " << i << "\n"; 
    }; 

private: 
    std::mutex logMutex_; 
    std::unique_ptr<fs::ofstream> ofStreamPtr_; 
}; 

int main(int argc, char *argv[]) { 
    if(argc != 2) { 
     std::cout << "Wrong argument" << std::endl; 
     exit(1); 
    } 
    std::size_t iter_count = boost::lexical_cast<std::size_t>(argv[1]); 

    Log log; 
    log.LogPath("log.txt"); 

    std::function<void(std::size_t)> func = std::bind(&Log::WriteLog, &log, std::placeholders::_1); 

    auto start_time = pt::microsec_clock::local_time(); 
    ////// Version 1: use std::thread ////// 
// { 
//  std::vector<std::shared_ptr<std::thread> > threadList; 
//  threadList.reserve(iter_count); 
//  for(std::size_t i = 0; i < iter_count; i++) { 
//   threadList.push_back(
//    std::make_shared<std::thread>(func, i)); 
//  } 
// 
//  for(auto it: threadList) { 
//   it->join(); 
//  } 
// } 

// pt::time_duration duration = pt::microsec_clock::local_time() - start_time; 
// std::cout << "Version 1: " << duration << std::endl; 

    ////// Version 2: use std::async ////// 
    start_time = pt::microsec_clock::local_time(); 
    { 
     for(std::size_t i = 0; i < iter_count; i++) { 
      auto result = std::async(func, i); 
     } 
    } 

    duration = pt::microsec_clock::local_time() - start_time; 
    std::cout << "Version 2: " << duration << std::endl; 

    ////// Version 3: use boost::asio::io_service ////// 
// start_time = pt::microsec_clock::local_time(); 
// { 
//  as::io_service ioService; 
//  as::io_service::strand strand{ioService}; 
//  { 
//   for(std::size_t i = 0; i < iter_count; i++) { 
//    strand.post(std::bind(func, i)); 
//   } 
//  } 
//  ioService.run(); 
// } 

// duration = pt::microsec_clock::local_time() - start_time; 
// std::cout << "Version 3: " << duration << std::endl; 


} 

С 4-ядерного CentOS 7 коробками (GCC 4.8.5), версия 1 (с использованием зОго :: нити) составляет около 100x медленнее по сравнению с другими реализациями.

 
Iteration Version1 Version2 Version3 
100  0.0034s 0.000051s 0.000066s 
1000  0.038s  0.00029s 0.00058s 
10000  0.41s  0.0042s 0.0059s 
100000 throw  0.026s  0.061s 

Почему резьбовая версия работает так медленно? Я думал, что каждый поток не займет много времени, чтобы завершить функцию Log::WriteLog.

+0

На мой взгляд, вы стреляете в слишком много потоков (больше, чем ядра процессора), и из-за того, что они все конкурируют за время процессора и переключение контекста, это медленно. В случае async, среда выполнения эффективно и эффективно выполняет ваш код на достаточно потоках и при необходимости дает процессорное время. – Saleem

+0

Создание потока _very_ expansive. что-то большее, чем количество ядер, снизит производительность (игнорируя потоки, заблокированные блокировками/IO). поэтому рекомендуется использовать пул потоков. –

+0

То, что ваш код не удается с 100000 итераций, является достаточно большим намеком. Поток - это дорогостоящий объект операционной системы, и вы платите за затраты на их создание и срываете их снова. Если объем работы, выполняемой потоком, является таким маленьким, то вы определенно видите накладные расходы. Реализация std :: async может амортизировать эту стоимость, используя threadpool - стандартная техника. Грубым направлением является то, что поток должен работать минимум на 100 микросекунд, асинхронная функция не должна занимать больше секунды. –

ответ

2

Функция никогда не может быть вызвана. Вы не пропускании std::launch политики в версии 2, так что вы полагаетесь на the default behavior of std::async (курсив мой):

Ведет же, как async(std::launch::async | std::launch::deferred, f, args...). Другими словами, f может быть выполнен в другом потоке или он может запускаться синхронно, когда запрашивается std::future для значения.

Попробуйте повторно запущен ваш тест с этим незначительными изменениями:

auto result = std::async(std::launch::async, func, i); 

В качестве альтернативы, вы могли бы назвать result.wait() на каждый std::future во втором цикле, аналогичны тому, как вы называете join() на всех потоках в версии 1. Это заставляет оценить std::future.

Обратите внимание, что с этим эталоном существует серьезная, несвязанная проблема. func сразу же получает блокировку для полной продолжительности вызова функции, что делает невозможным параллелизм. Здесь нет преимуществ для использования потоков - я подозреваю, что это будет значительно медленнее (из-за создания потоков и блокировки накладных расходов), чем последовательная реализация.

+0

Да, вы правы. Без указания политики запуска этот код никогда не запускается ... Я изменил свой код, чтобы использовать std :: launch :: async, и он делает то же самое, что и Version 1. (0.29s для 10000 iterrations). Примечание: там не было повышение производительности с удалением '' 'std :: lock_guard''' –

+0

Я предлагаю сравнить сравнительный параллельный код с последовательной реализацией как проверку работоспособности. Вы определенно не должны удалять 'std :: lock_guard', потому что' std :: fstream' не является потокобезопасным. Кроме того, если это ответило на ваш первоначальный вопрос, пожалуйста, подумайте о принятии ответа. –

 Смежные вопросы

  • Нет связанных вопросов^_^