Сценарий генерации данных Python со временем замедляется - PullRequest
0 голосов
/ 07 июля 2019

РЕДАКТИРОВАТЬ 1: Как указал fizzybear, похоже, что объем используемой памяти постоянно увеличивается, но я не могу сказать, почему, любые идеи будут высоко оценены.

Я запускаю скрипт, который использует библиотеку staticfg для генерации тонны управляющих потоковых диаграмм из программ на python, примерно 150 000 программ. Мой код просто перебирает местоположение файла каждой программы и генерирует соответствующий граф потока управления.

Из часто обновляемого индикатора выполнения я вижу, что когда скрипт запускается, он легко генерирует около 1000 CFG за несколько секунд, но за полчаса запуска он может сгенерировать всего 100 CFG за минуту.

В попытке ускорить процесс я реализовал многопоточность, используя многопроцессорную функцию Python map(), но этого не достаточно.

Кроме того, загрузка процессора (для всех ядер) увеличивается до 80-90% в начале сценария, но падает до 30-40% после запуска в течение нескольких минут.

Я попытался запустить его на Windows 10 и Ubuntu 18.04, и оба замедлились до почти невыносимой скорости.

Код для построения контрольной блок-схемы

from staticfg import CFGBuilder

def process_set():
    content = get_file_paths()
    iterate(build_cfg, ERROR_LOG_FILE, content)


def build_cfg(file_path):
    cfg = CFGBuilder().build_from_file(os.path.basename(file_path), os.path.join(DATA_PATH, file_path))
    cfg.build_visual(get_output_data_path(file_path), format='dot', calls=False, show=False)
    os.remove(get_output_data_path(file_path))  # Delete the other weird file created

Код для запуска здания cfg

from threading import Lock
from multiprocessing.dummy import Pool as ThreadPool
import multiprocessing

def iterate(task, error_file_path, content):
    progress_bar = ProgressBar(0, content.__len__(), prefix='Progress:', suffix='Complete')
    progress_bar.print_progress_bar()

    error_file_lock = Lock()
    increment_work_lock = Lock()
    increment_errors_lock = Lock()

    def an_iteration(file):
        try:
            task(file)
        except Exception as e:
            with increment_errors_lock:
                progress_bar.increment_errors()
            with error_file_lock:
                handle_exception(error_file_path, file, 'Error in doing thing', e)
        finally:
            with increment_work_lock:
                progress_bar.increment_work()
                progress_bar.print_progress_bar()

    pool = multiprocessing.dummy.Pool(multiprocessing.cpu_count())
    pool.map(an_iteration, content)

Код для обработки ошибок

def handle_exception(error_log_file_path, file_path, message, stacktrace):
    with open(error_log_file_path, 'a+', encoding='utf8') as f:
        f.write('\r{},{},{},{}\n'.format(str(datetime.datetime.now()), message, file_path, stacktrace))

Насколько я могу судить (?), Нет объектов, увеличивающихся в размерах и не увеличивающих время поиска, поэтому я немного растерялся, почему сценарий вообще должен замедляться. Любая помощь будет принята с благодарностью.

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

Приветствие.

Редактировать 2: Код для индикатора выполнения в случае, если это влияет на использование памяти

class ProgressBar:
    def __init__(self, iteration, total, prefix='', suffix='', decimals=1, length=100, fill='█'):
        self.iteration = iteration
        self.total = total
        self.prefix = prefix
        self.suffix = suffix
        self.decimals = decimals
        self.length = length
        self.fill = fill
        self.errors = 0

    def increment_work(self):
        self.iteration += 1

    def increment_errors(self):
        self.errors += 1

    def print_progress_bar(self):
        percent = ("{0:." + str(self.decimals) + "f}").format(100 * (self.iteration / float(self.total)))
        filled_length = int(self.length * self.iteration // self.total)
        bar = self.fill * filled_length + '-' * (self.length - filled_length)
        print('%s |%s| %s%% (%s/%s) %s, %s %s' % (self.prefix, bar, percent, self.iteration, self.total, self.suffix, str(self.errors), 'errors'), end='\r')
        # Print New Line on Complete
        if self.iteration == self.total:
            print()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...