2013-03-28 3 views
2

Во-первых, я очень мало знаю о многопоточности, и у меня возникают проблемы с поиском наилучшего способа оптимизации этого кода, но многопоточность кажется пустым, на котором я должен быть.C++ Многопоточность вложенных для циклов

double 
applyFilter(struct Filter *filter, cs1300bmp *input, cs1300bmp *output) 
{ 
    long long cycStart, cycStop; 

    cycStart = rdtscll(); 

    output -> width = input -> width; 
    output -> height = input -> height; 

    int temp1 = output -> width; 
    int temp2 = output -> height; 

    int width=temp1-1; 
    int height=temp2 -1; 
    int getDivisorVar= filter -> getDivisor(); 
    int t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; 

    int keep0= filter -> get(0,0); 
    int keep1= filter -> get(1,0); 
    int keep2= filter -> get(2,0); 
    int keep3= filter -> get(0,1); 
    int keep4= filter -> get(1,1); 
    int keep5= filter -> get(2,1); 
    int keep6= filter -> get(0,2); 
    int keep7= filter -> get(1,2); 
    int keep8= filter -> get(2,2); 


    //Declare variables before the loop 
    int plane, row, col;  

    for (plane=0; plane < 3; plane++) { 
     for(row=1; row < height ; row++) { 
      for (col=1; col < width; col++) { 

       t0 = (input -> color[plane][row - 1][col - 1]) * keep0; 
       t1 = (input -> color[plane][row][col - 1]) * keep1; 
       t2 = (input -> color[plane][row + 1][col - 1]) * keep2; 
       t3 = (input -> color[plane][row - 1][col]) * keep3; 
       t4 = (input -> color[plane][row][col]) * keep4; 
       t5 = (input -> color[plane][row + 1][col]) * keep5; 
       t6 = (input -> color[plane][row - 1][col + 1]) * keep6; 
       t7 = (input -> color[plane][row][col + 1]) * keep7; 
       t8 = (input -> color[plane][row + 1][col + 1]) * keep8; 

       // NEW LINE HERE 

       t9 = t0 + t1 + t2 + t3 + t4 + t5 + t6 + t7 + t8; 
       t9 = t9/getDivisorVar; 

       if (t9 < 0) { 
        t9 = 0; 
       } 

       if (t9 > 255) { 
        t9 = 255; 
       } 

       output -> color[plane][row][col] = t9; 
      } .... 

Весь этот код, скорее всего, не требуется, но он обеспечивает некоторый контекст. Так как первый из трех циклов «для» только идет от 0-2, я надеялся, что я мог бы пропустить нижние два цикла «для», чтобы все работали одновременно для другого значения «плоскости». Возможно ли это? И если да, то действительно ли это сделает мою программу быстрее?

+0

Это то, о чем я думал, ненадолго глядя на многопоточность, но я думал, что это возможно, если t0-t9 были как-то локальны для потока? Потому что все остальные переменные не зависят от петель. – Scalahansolo

+0

Убедитесь, что все используемые в потоке поточно-локальные, поэтому вам не нужно беспокоиться о том, что потоки наступают друг на друга, за исключением входных и выходных массивов. Для этого просто программно убедитесь, что вы никогда не будете читать/писать те же ячейки из двух разных потоков, так что вам не нужна синхронизация. –

+0

Я только что попробовал это, используя 4 плоскости и изображение 2560x1600. Для одного потока потребовалось 109 мс, 4 потока - 47 мс. Но поскольку этот процесс очень прост, накладные расходы на создание и ожидание потоков были на самом деле достаточно значительными, но это еще больше, чем вдвое меньше времени, необходимого для расчетов. Более сложные циклы определенно выиграют от потоковой обработки. Как я уже сказал, в этом примере не нужна никакая синхронизация (кроме ожидания завершения потоков). –

ответ

3

Я также хотел бы изучить OpenMP. Это отличная библиотека, которая позволяет прорисовывать ОЧЕНЬ простым способом, используя прагмы. OpenMP скомпилирован на многих платформах, вам просто нужно убедиться, что ваш поддерживает его!

У меня есть набор кода, который имел 8 уровней для циклов, и он очень хорошо надел его.

+0

Как только я наконец получил OpenMP, чтобы работать так, как я хотел в своем коде, он работал намного быстрее! Спасибо за предложение. – Scalahansolo

1

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

Это, безусловно, ускорит ваш код на многоядерной машине.

Возможно, вы захотите взглянуть на std::thread (если вы в порядке с C++ 11) для реализации потоковой перекрестной платформы (поскольку вы не указали свою целевую платформу). Или лучше с threading support library

Вы также можете подумать об обнаружении количества ядер и запустить соответствующее количество потоков, как в threadcount = min (plane, corees), и предоставить каждой рабочей функции доступ к набору данных одиночной плоскости.

+0

Извините, что не указали. Это будет работать только на машине Ubuntu, и я заранее знаю, с каким количеством ядер я должен работать, поскольку это домашнее задание, поэтому мы можем разработать наш код именно вокруг тех машин, на которые мы будем оцениваться. – Scalahansolo

0

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

Что вы могли бы подумать, если это двухмерная графическая операция, попробуйте использовать OpenGL или аналогичную, так как она будет использовать аппаратное обеспечение вашей системы, и в нее встроен параллелизм.

+0

Единственное, что должен сделать этот код, - применить матричный фильтр 3x3 к 2d-изображению и выбросить измененное изображение, так что OpenGL стоит задуматься над этим? – Scalahansolo

+0

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

0

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

Также высокая вероятность того, что внешняя петля for с 3 прохода будет развернута с помощью complier и будет выполнена параллельно.

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

+0

Я думаю, что в этом конкретном примере он может легко уйти без синхронизации. –

+0

Я не согласен с тем, что он будет выполняться параллельно для цикла 3 pass for. В последнее время у меня был аналогичный набор кода, а также очень небольшое количество циклов (меньше, чем количество поддерживаемых потоков в системе), и он просто не скомпилировался с многопоточной версией, я должен был сделать это самостоятельно. – trumpetlicks

+0

@ W.B. Ему понадобится примитив синхронизации, который гарантирует, что все потоки завершат обработку данных, и результат может быть использован. – inkooboo

0

Для такой ситуации вы можете сделать хуже, чем использовать компилятор, который автоматически превращается для циклов в потоки.

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

Единственное, у вас есть компилятор, который делает это? Если это так, то это самый простой способ получить преимущества потоков для простого, почти открытого параллелизма.

Я знаю, что это компилятор Sun's C (я думаю, что они были самыми ранними для этого. Это может быть только версия Solaris их компилятора). Я думаю, что компилятор Intel тоже может. У меня есть сомнения в GCC (хотя я был бы очень рад, если бы исправил этот момент), и я не слишком уверен в компиляторе Microsoft.

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