2016-10-13 2 views
0

У меня есть следующий код, который путает меня много:C++ создание потоков большая голова

float OverlapRate(cv::Mat& model, cv::Mat& img) { 
    if ((model.rows!=img.rows)||(model.cols!=img.cols)) { 
     return 0; 
    } 

    cv::Mat bgr[3]; 
    cv::split(img, bgr); 

    int counter = 0; 
    float b_average = 0, g_average = 0, r_average = 0; 
    for (int i = 0; i < model.rows; i++) { 
     for (int j = 0; j < model.cols; j++) { 
      if((model.at<uchar>(i,j)==255)){ 
       counter++; 
       b_average += bgr[0].at<uchar>(i, j); 
       g_average += bgr[1].at<uchar>(i, j); 
       r_average += bgr[2].at<uchar>(i, j); 
      } 
     } 
    } 

    b_average = b_average/counter; 
    g_average = g_average/counter; 
    r_average = r_average/counter; 

    counter = 0; 
    float b_stde = 0, g_stde = 0, r_stde = 0; 
    for (int i = 0; i < model.rows; i++) { 
     for (int j = 0; j < model.cols; j++) { 
      if((model.at<uchar>(i,j)==255)){ 
       counter++; 
       b_stde += std::pow((bgr[0].at<uchar>(i, j) - b_average), 2); 
       g_stde += std::pow((bgr[1].at<uchar>(i, j) - g_average), 2); 
       r_stde += std::pow((bgr[2].at<uchar>(i, j) - r_average), 2);     
      } 
     } 
    } 

    b_stde = std::sqrt(b_stde/counter); 
    g_stde = std::sqrt(g_stde/counter); 
    r_stde = std::sqrt(r_stde/counter); 

    return (b_stde + g_stde + r_stde)/3; 
} 


void work(cv::Mat& model, cv::Mat& img, int index, std::map<int, float>& results){ 
    results[index] = OverlapRate(model, img); 
} 

int OCR(cv::Mat& a, std::map<int,cv::Mat>& b, const std::vector<int>& possible_values) 
{ 
     int recog_value = -1; 
     clock_t start = clock(); 

     std::thread threads[10]; 
     std::map<int, float> results; 
     for(int i=0; i<10; i++) 
     { 
      threads[i] = std::thread(work, std::ref(b[i]), std::ref(a), i, std::ref(results)); 
     } 

     for(int i=0; i<10; i++) 
      threads[i].join(); 


     float min_score = 1000; 
     int min_index = -1; 
     for(auto& it:results) 
     { 
      if (it.second < min_score) { 
       min_score = it.second; 
       min_index = it.first; 
      } 
     } 

     clock_t end = clock(); 
     clock_t t = end - start; 
     printf ("It took me %d clicks (%f seconds) .\n",t,((float)t)/CLOCKS_PER_SEC); 

     recog_value = min_index; 
} 

Что делает приведенный выше код является только простым оптическими распознаванием символов. У меня есть один оптический символ в качестве входного сигнала и сравниваю его с 0 - 9 десятью стандартными символьными моделями, чтобы получить наиболее похожий, а затем вывести признанное значение.

Когда я выполняю вышеуказанный код без использования десяти потоков, работающих одновременно, время составляет 7 мс. НО, когда я использую десять потоков, он опускается до 1 или 2 секунд для одного оптического распознавания символов.

В чем причина? Отладочная информация говорит о том, что создание потоков потребляет много времени, что составляет этот код:

threads[i] = std::thread(work, std::ref(b[i]), std::ref(a), i, std::ref(results)); 

Почему? Благодарю.

+2

Вы спрашиваете: «Почему для создания потока требуется много времени?» ? – ZivS

+3

Разрешено ли 'work' изменять' model' или 'img'? Как поточно-безопасный вы считаете «результатом», так как вы изменяете его в каждом потоке? – kfsone

+1

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

ответ

1

Выполнение нескольких потоков полезно только в двух контекстах: у вас есть несколько аппаратных ядер (так что потоки могут выполняться одновременно) ИЛИ каждый поток ожидает ввода-вывода (так что один поток может работать, пока другой поток ожидает ввода-вывода, дисковой нагрузки или сетевой передачи).

Ваш код не привязан к IO, поэтому, надеюсь, у вас есть 10 ядер для запуска кода. Если у вас нет 10 ядер, каждый поток будет конкурировать за ограниченные ресурсы, а самый дефицитный ресурс - это пространство кеша L1. Если все 10 потоков сражаются за 1 или 2 ядра и их кеш-пространство, тогда кэши будут «обманывать» и дают вам 10-100x более медленную производительность.

Попробуйте протестировать свой код 10 раз, с N = от 1 до 10 потоков и посмотреть, как он работает.

(Есть еще одна причина, по которой есть несколько потоков, когда ядра поддерживают гиперпоточность. ОС будет «притворяться», что 1 ядро ​​имеет 2 виртуальных процессора, но с этим вы не получаете 2x производительности. получить что-то между 1x и 2x. Но для того, чтобы получить этот частичный импульс, вам нужно запустить 2 потока на ядро)

+0

Да, я думаю, что это причина. Было бы лучше, если бы я использовал пул потоков? – Johnnylin

+0

@Johnnylin пул потоков не имеет значения, это всего лишь коллекция нитей в одном объекте. то, что вы, возможно, захотите изменить, это предоставить Mat по значению (для увеличения кеша локального потока) и порождать не больше, чем количество потоков в вашей системе. – UmNyobe

+0

@UmNyobe. Стоит исследовать, я уверен, что «cv :: Mat» в какой-то форме изображения, и если размер изображения особенно велик, вы можете фактически выбросить кеш, пройдя по значению. – kfsone

0

Не всегда эффективно использовать темы. Если вы используете потоки для небольшой проблемы, тогда управление потоками требует больше времени и ресурсов, чем решение проблемы. У вас должно быть достаточно работы для потоков и хорошей работы над потоками.

Если вы хотите узнать, сколько потоков вы можете использовать для решения проблемы или насколько велики должны быть проблемы, найдите Isoeffective functions (psi1, psi2, psi3) из теории параллельных компьютеров.