10 / 6 / 5
Регистрация: 04.04.2017
Сообщений: 14

Устранение узкого места в многопроцессной программе

07.12.2019, 19:39. Показов 705. Ответов 2
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток, столкнулся с узким местом при разработке много процессной программы на питоне.
Программа подсчитывает количество вхождений ключевой фразы в текстовом файле, оба параметра (путь к файлу и ключевая фраза) задаются в командной строке.
Свой личный опыт программирования на других языках говорит о том, что узкое место связано с разделением данных (объект lines) между процессами - а именно то, что объект lines поддерживаем доступ к данным лишь для одного процесса в одно и тоже время.
Следовательно была предпринята попытка создать разрезы данных (дополнительные ссылки, в коде отмечено тройным восклицательным знаком после комментария) для каждого процесса, но результат не впечатлил.
Исходный код и результаты тестов для одного и двух процессов приведены ниже по тексту.
Прошу вашего мнения по методу оптимизации данной программы...

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
''' Counting specified key sequence in the specified file
and printing result to the standard output with elapsed time '''
 
import sys
import argparse
from multiprocessing import Manager, Process
 
from timeit import default_timer as timer
 
def worker(lines_slices, keyseq, results):
    counter = 0
 
    # подсчет количества совподений в каждой строке
    for line in lines_slices:
        counter += line.count(keyseq)
 
    # сохранение результата подсчета в очереде
    results.put(counter)
 
def main(argv_slice):
    res = 0
 
    # разбор аргументов командной строки
    parser = argparse.ArgumentParser(
        description='Counting key sequence in the file')
 
    parser.add_argument('-f', '--fpath', type=str, required=True,
                        help='File path to reading')
    parser.add_argument('-k', '--keyseq', type=str, required=True,
                        help='Key sequence to counting')
 
    parsed_argv = parser.parse_args(argv_slice)
 
    fpath = parsed_argv.fpath
    keyseq = parsed_argv.keyseq
 
    with open(fpath) as fin:
        start = timer()
 
        manager = Manager()
 
        # считывание всего содержимого исходного файла
        lines = fin.readlines()
 
        # создание очереди для хранения результатов
        results = manager.Queue()
 
        processes_list = [] # список процессов
 
        lines_slices = [] # список ссылок на данные
 
        processes_count = 1
 
        process_part = len(lines) / processes_count
 
        # цикл инициализации и создания потоков выполнения
        for process_index in range(0, processes_count):
            from_index = process_index * int(process_part)
 
            if process_index != processes_count - 1:
                to_index = from_index + int(process_part)
            else:
                to_index = len(lines)
 
            # создание дополнительной ссылки на данные для каждого потока !!!
            lines_slices.append(lines[from_index:to_index])
 
            # сохранение дескриптора потока в списке потоков
            processes_list.append(Process(target=worker,
                                          args=(lines_slices[process_index],
                                                keyseq, results)))
 
            processes_list[process_index].start()
 
        # ожидание завершения потоков выполнения
        for process_index in range(0, processes_count):
            processes_list[process_index].join()
 
        custom_counter = 0
 
        # расчет окончательного результата подсчета
        for process_index in range(0, processes_count):
            custom_counter += results.get()
 
        elapsed = timer() - start # расчет затраченного времени на вычисление
 
    print('Counter: {}'.format(custom_counter))
    print('Elapsed: {}'.format(elapsed))
    
    return res
 
if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
Тест программы с одним процессом:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f ".\Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 11.7325519
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f ".\Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 10.1927612
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f ".\Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.8788877
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f ".\Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.7096962
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f ".\Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.2697762
 
C:\Users\DPC\Desktop>
Тест программы с двумя процессами:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 8.733194699999999
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 8.6498893
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 8.703362700000001
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 8.5597862
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 8.717497
 
C:\Users\DPC\Desktop>
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
07.12.2019, 19:39
Ответы с готовыми решениями:

Устранение узкого места в многопоточной программе
Доброго времени суток, столкнулся с узким местом при разработке много поточной программы на питоне. Программа подсчитывает количество...

Поиск "узкого" места и варианты решения
Есть сервер (i3 WS2003 4 Gb RAID5 1Tb 1x1Gbit/s Lan), на нём в общей папке, весом в 18 Гб, лежат 15154 файла. Каждый день с ними работают...

Устранение ошибок в программе
написала программу. выдает очень много ошибок. #include "stdafx.h" #include "stdio.h" #define N20 int main() { int...

2
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5973 / 3735 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
07.12.2019, 20:30
Воркеры чуточку не так пишутся. Вы создали очередь, но не используете её. Главный поток должен класть задания в очередь, а воркеры забирать (и крутиться в вечном цикле, ожидая задания из очереди).

Если написать воркеры так, то можно нарезать входной файл на одинаковые кусочки (10-20-сколько угодно) и выдавать задания воркерам.

У вас, скорей всего, много данных, поэтому прирост производительности от параллелизма будет "съедаться" на пересылку данных процессам.

Добавлено через 1 минуту
Уточнение: очередей должно быть две: входная и выходная.
1
10 / 6 / 5
Регистрация: 04.04.2017
Сообщений: 14
08.12.2019, 23:19  [ТС]
Сделал примерно так, как сказал Рыжий Лис, но вместо объекта Process используется объект Pool:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
''' Counting specified key sequence in the specified file
and printing result to the standard output with elapsed time '''
 
import sys
import argparse
from multiprocessing import Manager, Pool
 
from timeit import default_timer as timer
 
keyseq = str()
results = None
 
def worker_init(first, second):
    global keyseq
    global results
 
    keyseq = first
    results = second
 
def worker(lines_slices):
    global keyseq
    global results
    
    counter = 0
 
    # подсчет количества совподений в каждой строке
    for line in lines_slices:
        counter += line.count(keyseq)
 
    # сохранение результата подсчета в очереде
    results.put(counter)
 
def main(argv_slice):
    res = 0
 
    # разбор аргументов командной строки
    parser = argparse.ArgumentParser(
        description='Counting key sequence in the file')
 
    parser.add_argument('-f', '--fpath', type=str, required=True,
                        help='File path to reading')
    parser.add_argument('-k', '--keyseq', type=str, required=True,
                        help='Key sequence to counting')
 
    parsed_argv = parser.parse_args(argv_slice)
 
    fpath = parsed_argv.fpath
    keyseq = parsed_argv.keyseq
 
    with open(fpath) as fin:
        start = timer()
 
        manager = Manager()
 
        # считывание всего содержимого исходного файла
        lines = fin.readlines()
 
        # создание очереди для хранения результатов
        results = manager.Queue()
 
        pool_size = 1
 
        pool = Pool(processes=pool_size, initializer=worker_init,
                    initargs=[keyseq, results])
 
        pool_size *= 1 # теперь pool_size определяет количество порций данных
 
        lines_slices = [] # список ссылок на данные
 
        pool_part = len(lines) / pool_size
 
        # цикл разбиения даннных на равные порции
        for pool_index in range(0, pool_size):
            from_index = pool_index * int(pool_part)
 
            if pool_index != pool_size - 1:
                to_index = from_index + int(pool_part)
            else:
                to_index = len(lines)
 
            lines_slices.append(lines[from_index:to_index])
 
        pool.map(worker, lines_slices)
 
        pool.close()
 
        pool.join();
 
        custom_counter = 0
 
        # расчет окончательного результата подсчета
        while not results.empty():
            custom_counter += results.get()
 
        elapsed = timer() - start # расчет затраченного времени на вычисление
 
    print('Counter: {}'.format(custom_counter))
    print('Elapsed: {}'.format(elapsed))
    
    return res
 
if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
Тест программы с одним процессом и не разделенными данными:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.741935000000002
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.9012215
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.848994
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.721278000000002
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 9.761334900000001
 
C:\Users\DPC\Desktop>
Тест программы с пятью процессами и пятидесятью порциями данных:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 6.8526908
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 6.853741800000001
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 6.8378956
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 6.7401488
 
C:\Users\DPC\Desktop>python ./keyseqfinder.py -f "Всякие записи.txt" -k "Абишев Данияр"
Counter: 396144
Elapsed: 7.0934037
 
C:\Users\DPC\Desktop>
Непонятно было как определить оптимальное количество процессов в пуле - поэтому пришлось экспериментировать. Выяснил, что быстрее всего программа работает при пяти процессах в пуле и с пятидесятью порциями данных.

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

Скорее всего я где-то ошибся при использовании процессов и пуллов процессов - потому-что результат увеличения производительности совсем не велик...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
08.12.2019, 23:19
Помогаю со студенческими работами здесь

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

fstream в многопроцессной среде
Не нашел в гугле ответа на следующие вопросы: 1. Что происходит когда ifstream открывает, затем читает существующий, но уже открытый на...

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

Как закрыть фрейм из другого места в программе?
Я умею создавать фремы с кнопками, по нажатии на крестик или кнопку с действием dispose() окно гасится. Да. Но вот я создаю фрейм 01,...

Выбор места для хранения текста в программе
Добрый вечер. Мне в программе надо выводить на экран небольшой текст на родном языке (50 строк по 20 символов). Есть несколько способов это...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Опции темы

Новые блоги и статьи
Отчёт о спецтехнике находящейся в ремонте
Maks 20.04.2026
Отчёт из решения ниже размещен в конфигурации КА2. Задача: отобразить спецтехнику, которая на данный момент находится в ремонте. Есть нетиповой документ "Заявка на ремонт спецтехники" который. . .
Памятка для бота и "визитка" для читателей "Semantic Universe Layer (Слой семантической вселенной)"
Hrethgir 19.04.2026
Сгенерировано для краткого описания по случаю сборки и компиляции скелета серверного приложения. И пусть после этого скажут, что статьи сгенерированные AI - туфта и не интересно. И это не реклама -. . .
Запрет удаления строк ТЧ документа при определенном условии
Maks 19.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "Аккумуляторы", разработанного в конфигурации КА2. У данного документа есть ТЧ, в которой в зависимости от прав доступа. . .
Модель заражения группы наркоманов
alhaos 17.04.2026
Условия задачи сформулированы тут Суть: - Группа наркоманов из 10 человек. - Только один инфицирован ВИЧ. - Колются одной иглой. - Колются раз в день. - Колются последовательно через. . .
Мысли в слух. Про "навсегда".
kumehtar 16.04.2026
Подумалось тут, что наверное очень глупо использовать во всяких своих установках понятие "навсегда". Это очень сильное понятие, и я только начинаю понимать край его смысла, не смотря на то что давно. . .
My Business CRM
MaGz GoLd 16.04.2026
Всем привет, недавно возникла потребность создать CRM, для личных нужд. Собственно программа предоставляет из себя базу данных клиентов, в которой можно фиксировать звонки, стадии сделки, а также. . .
Знаешь почему 90% людей редко бывают счастливыми?
kumehtar 14.04.2026
Потому что они ждут. Ждут выходных, ждут отпуска, ждут удачного момента. . . а удачный момент так и не приходит.
Фиксация колонок в отчете СКД
Maks 14.04.2026
Фиксация колонок в СКД отчета типа Таблица. Задача: зафиксировать три левых колонки в отчете. Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка) / / . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru