РЕДАКТИРОВАТЬ 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()