Распараллеливание парсинга TBB с помощью boots::spirit::qi

В своей программе я использую Boost-Spirit-Qi для анализа больших наборов данных. Входные данные представляют собой последовательные записи. Я пытаюсь использовать TBB для повышения эффективности синтаксического анализа. Процедура параллельной обработки следующая:

typedef map<string, data_struct_t> mdata_t;
vector<string> text; 
mdata_t  data;

parallel_for(blocked_range<size_t>(0, input.size(), gs),
                     [&]  (blocked_range<size_t>& r) {
        data_struct_t cs;
        mdata_t cr;
        string s;
        for(size_t i=r.begin(); i<r.end(); i++) {
           s = text[i];         
           Parser::task1(s, cs); 
           Parser::task2(s, cs); 
           Parser::task3(s, cs);
        ....
           Parser::task8(s, cs);   
           cr.insert(std::make_pair(cs.title, cs));
        }
        data.insert(cr.begin(), cr.end());  

 }, ap);

Моя программа использует только 10% ЦП (2 ЦП, 16 ядер) и работает на 8 ядрах. Не понимаю, почему остальные 8 ядер не используются (один процессор). Буду признателен за указание мне правильного алгоритма распараллеливания этой задачи.

Спасибо за совет.

Стэн


person stansy    schedule 22.01.2015    source источник


Ответы (1)


Ваш input.size() может быть маленьким или gs слишком большим, чтобы предотвратить создание достаточного количества параллелизма. В противном случае, если вас беспокоит количество потоков, проверьте маску процесса (аффинити) вашей программы при ее запуске и то, как инициализируется TBB (например, если tbb::task_scheduler_init создается с небольшим количеством потоков).

Что касается использования ЦП, то оно ожидается, когда ваша работа связана с вводом-выводом, то есть при чтении файла. Также возможно, что время, необходимое для завершения одной параллельной итерации, сильно отличается от времени другой итерации. В этом случае небольшие итерации могут быть выполнены еще до того, как будут созданы все потоки. (Вы должны вручную дождаться, когда все потоки заработают, если хотите точно измерить ускорение)

Советы:

У вас есть ошибка с data.insert, так как std::map небезопасно для одновременной модификации. Используйте tbb::concurrent_unordered_map или просто tbb::parallel_reduce, чтобы объединить частичные результаты, собранные в cr из разных потоков.

Шаблон Parser::task1(s, cs); ... Parser::task8(s, cs); также можно распараллелить, если задачи не имеют общего глобального состояния. См. tbb::parallel_pipeline, который активирует параллелизм конвейерного типа для цепочки. этих самостоятельных задач.

person Anton    schedule 22.01.2015
comment
Большое спасибо Антон. С некоторыми в ваших комментах не могу согласиться например: imput.size=100000, gs проверял в диапазоне 1-10000, задачи не использую по ряду очевидных причин. Я согласен с вами в том, что его следует использовать для параллельного уменьшения или одновременного использования контейнеров. Знаете ссылку на сайт с примерами. - person stansy; 22.01.2015
comment
@stansy, я просто попытался охватить все возможности поведения, если дело не в диапазоне и количестве задач, которые он может создать, то либо небольшой объем работы внутри, либо какое-то ограничение на количество потоков, предоставляемых через маску или TBB API. Тем не менее, количество потоков — это отдельная проблема, связанная с использованием ЦП, о которой я говорил выше. Вы можете найти примеры в пакете TBB и на страницах справочника. - person Anton; 22.01.2015
comment
Спасибо @Антон. После нескольких тестов нашел решение этой проблемы. Вывод прост: всегда используйте итераторы, а не индексы. Если вы не используете сложные вычисления с использованием контейнеров TBB, это не значительно улучшит время вычислений. С таким же успехом можно использовать стандартные контейнеры. - person stansy; 25.01.2015