编辑1: 正如fizzybear指出的那样,看来我的内存使用量正在稳步增长,但是我不能说为什么,任何想法都会受到赞赏。
我正在运行一个脚本,该脚本使用staticfg
库从python程序(大约150,000个程序)生成大量控制流图。我的代码只是循环遍历每个程序的文件位置,并生成相应的控制流程图。
从经常更新的进度栏中,我可以看到,当脚本开始运行时,它可以在几秒钟内轻松生成约1000个CFG,但是运行半小时,一分钟之内几乎无法生成100个CFG。
为了加快速度,我使用python的多处理map()
函数实现了多线程,但这还不够用。
此外,(在所有内核中)CPU利用率在脚本开始时高达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()