Темы: стоит ли для этой ситуации? - PullRequest
0 голосов
/ 29 августа 2018

Я никогда раньше не использовал темы, но думаю, что, возможно, я столкнулся с возможностью:

Я написал скрипт, который просматривает массив из ~ 500 файлов Excel и использует Parse :: Excel для извлечения значений из определенных листов в книге (в среднем два листа на книгу; одна ячейка извлекается на лист).

Запуск его сейчас, когда я просто просматриваю массив файлов один за другим и извлекаю соответствующую информацию из файла, на это уходит около 45 минут.

У меня такой вопрос: это возможность использовать потоки и получать более одного файла за раз *, или я должен просто принять 45-минутное время выполнения?

(* - если это грубое недопонимание того, что я могу сделать с темами, скажите, пожалуйста!)

Заранее спасибо за любые рекомендации, которые вы можете предложить!

Редактировать - добавить пример кода. Приведенный ниже код является подпрограммой, которая вызывается в цикле foreach для каждого местоположения файла, хранящегося в массиве:

# Init the parser
my $parser = Spreadsheet::ParseExcel->new;
my $workbook = $parser->parse($inputFile) or die("Unable to load $inputFile: $!");

# Get a list of any sheets that have 'QA' in the sheet name
foreach my $sheet ($workbook->worksheets) {
    if ($sheet->get_name =~ m/QA/) {
        push @sheetsToScan, $sheet->get_name;
    }
}
shift @sheetsToScan;

# Extract the value from the appropriate cell
foreach (@sheetsToScan) {
    my $worksheet = $workbook->worksheet($_);
    if ($_ =~ m/Production/ or $_ =~ m/Prod/) {
        $cell = $worksheet->get_cell(1, 1);
        $value = $cell ? $cell->value: undef;
        if (not defined $value) {
            $value = "Not found.";
        }
    } else {
        $cell = $worksheet->get_cell(6,1);
        $value = $cell ? $cell->value: undef;
        if (not defined $value) {
            $value = "Not found.";
        }
    }

push(@outputBuffer, $line);

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Мне кажется, что ваша задача должна получить выгоду от нескольких потоков выполнения (процессов или потоков), так как она, кажется, имеет очень грубое сочетание ввода-вывода и процессора. Я ожидал бы ускорения в несколько раз, но трудно сказать, не зная деталей. Предлагаю попробовать.

Один из способов - разбить список файлов на группы, если есть несколько ядер, которые вы можете сэкономить. Затем обработайте каждую группу в fork, который собирает ее результаты и передает их обратно в родительский узел после передачи по каналу или файлам. Есть модули, которые делают это и многое другое, например, Forks :: Super или Parallel :: ForkManager . Они также предлагают очередь, еще один подход, который вы можете использовать.

Я делаю это регулярно, когда задействовано много данных в файлах, и почти линейное ускорение увеличивается до 4 или 5 ядер (в NFS) или даже с большим количеством ядер в зависимости от деталей задания и аппаратного обеспечения.

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

Другим способом было бы создание очереди потока ( Thread :: Queue ) и накормить его именами групп. Обратите внимание, что потоки Perl не являются легкими "потоками" , как можно было бы ожидать; напротив, они тяжелые, они копируют все в каждый поток (поэтому запустите их заранее, пока в программе не было много данных), и у них есть и другие тонкости. Поэтому у вас есть небольшое количество рабочих с хорошим списком файлов для каждого, вместо множества потоков, быстро работающих с очередью.

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

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

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

Оптимальное количество файлов для передачи в поток / процесс сложно оценить, даже если все детали под рукой; пытаться. Я предполагаю, что заявленная производительность (более 5 секунд для файла) обусловлена ​​неэффективностью, которую можно устранить. Если какой-то файл действительно обрабатывает , что долго, то начните с передачи одного файла за раз в очередь.

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


Практический комментарий: как отмечено в комментарии Шон , для извлечения нескольких фрагментов данных из 500 файлов требуется 45 минут, что выглядит довольно экстремально (если только используемый модуль не является крайне неэффективным ); Я бы дал самое большее несколько минут? Поэтому сначала проверьте свой код на предмет неоправданной неэффективности.

0 голосов
/ 02 сентября 2018

То, что вы делаете, это просто меняете "for ...." на "mce_loop ....", и вы увидите повышение, хотя я предлагаю вам сначала взглянуть mceloop .

0 голосов
/ 29 августа 2018

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

  • задача вашего скрипта не поддается параллелизации - когда каждый шаг вашего алгоритма зависит от предыдущих шагов

  • задача, выполняемая вашим сценарием, быстра и легка по сравнению с издержками на создание и управление новым потоком или новым процессом

  • ваша система имеет только один процессор или ваш сценарий может использовать только один процессор

  • ваша задача ограничена ресурсом, отличным от ЦП, таким как доступ к диску, пропускная способность сети или память - если ваша задача включает обработку больших файлов, которые вы загружаете через медленное сетевое соединение, тогда ваша сеть узкое место, и обработка файла на нескольких процессорах не поможет. Аналогично, если ваша задача потребляет 70% памяти вашей системы, использование второго и третьего потоков потребует подкачки в пространство подкачки и не сэкономит время. Распараллеливание также будет менее эффективным, если ваши потоки конкурируют за некоторый синхронизированный ресурс - блокировки файлов, доступ к базе данных и т. Д.

  • вам нужно быть внимательным к другим пользователям вашей системы - если вы используете все ядра на машине, то у других пользователей будет плохой опыт

  • [добавлено, только потоки] ваш код использует любой пакет, который не потоко-безопасный . Самый чистый код Perl будет поточно-ориентированным, но пакеты, использующие XS, могут не быть

  • [добавлено], когда вы все еще активно разрабатываете свою основную задачу. Отладка в параллельном коде намного сложнее

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...