Форум программистов, компьютерный форум, киберфорум
Python для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
10 / 6 / 5
Регистрация: 04.04.2017
Сообщений: 14

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

07.12.2019, 19:39. Показов 671. Ответов 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
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Программа принимает математическое выражение в виде строки и выдаёт его производную в виде строки и вычисляет значение производной при заданном х Логарифм записывается как: (x-2)log(x^2+2) -. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru