我运行一个创建大型纯文本日志文件的程序。在那里有很多 我希望提取的数字块用于进一步分析。 不幸的是,那里的结构并不多。一开始就有 一个块,其中包含有关问题大小和数量的一些信息 使用的节点:
Lattice initialized:
problem size = 16 16 16 32
layout size = 8 16 16 32
logical machine size = 1 1 2 4
subgrid size = 16 16 8 8
total number of nodes = 8
total volume = 131072
subgrid volume = 16384
要获得这些数字,我只是遍历文件中的每一行
然后看看我是否可以在那里使用正则表达式模式total number of nodes = (\d+)
。这很有效。
程序进行模拟,需要解决多个线性系统 单个更新的等式。每个都有任意数量的更新 日志文件。它们中的每一个都是由这样的一条线引入的:
Doing Update: 29 warm_up_p = 1
到目前为止,我刚刚提取了整体的性能数据(下面更多) 文件,忽略它们所属的更新。我现在想改变它 在更新的上下文中具有性能,并查看它是否在减速 下来。
使用了不同的求解器,每个求解器都有不同的输出:
invcg:
QDP:FlopCount:invcg2 Performance/CPU: t=2.577605(s) Flops=19473629184 => 7554.93149027877 Mflops/cpu.
QDP:FlopCount:invcg2 Total performance: 7554.93149027877 Mflops = 7.55493149027877 Gflops = 0.00755493149027877 Tflops
CG_SOLVER: 37 iterations. Rsd = 2.06699506385389e-09 Relative Rsd = 5.77136063168358e-13
CG_SOLVER_TIME: 2.72206 sec
minvcg:
MInvCG2: 36 iterations
QDP:FlopCount:minvcg Performance/CPU: t=2.998556(s) Flops=21837643776 => 7282.72000789713 Mflops/cpu.
QDP:FlopCount:minvcg Total performance: 7282.72000789713 Mflops = 7.28272000789713 Gflops = 0.00728272000789713 Tflops
QPhiX Clover CG:
QPHIX_CLOVER_CG_SOLVER: 29 iters, rsd_sq_final=8.70199859880196e-11
QPHIX_CLOVER_CG_SOLVER: || r || / || b || = 8.70199859880196e-11
QPHIX_CLOVER_CG_SOLVER: Solver Time=0.0646071434020996 (sec) Performance=239.823066739962 GFLOPS
QPHIX_MDAGM_SOLVER: total time: 0.079926 (sec)
QPhiX Clover Multi-Shift:
QPHIX_CLOVER_MULTI_SHIFT_CG_MDAGM_SOLVER: Iters=30 Solver Time=0.0672321319580078 (sec) Performance=253.535273917039 GFLOPS
QPHIX_CLOVER_MULTI_SHIFT_CG_MDAGM_SOLVER: total time: 0.225874 (sec)
QPhiX Clover BICGSTAB:
QPHIX_CLOVER_BICGSTAB_SOLVER: 8 iters, rsd_sq_final=6.82060929874834e-09 ||r||/||b|| (acc) = 9.48324202036213e-08
QPHIX_CLOVER_BICGSTAB_SOLVER: Solver Time=0.114965915679932 (sec) Performance=35.4751782550446 GFLOPS
QPHIX_CLOVER_BICGSTAB_SOLVER: total_iters=17 || r || / || b || = 8.2259136270501e-08
Gflop / s的表现总是在一条线上。所以我能够使用 像这样的正则表达式来提取数据:
QDP:FlopCount:(\S+) Total performance: ([\d.]+) Mflops = ([\d.]+) Gflops = ([\d.]+) Tflops
我也希望在上下文中有解算器中的迭代次数 更新号码。最后,我想分析像这些每个解算器一样的东西 算法:
解算器的多个行块可以用多行解析 我认为正则表达式。但是因为可以有多个求解器块 对于每个更新号,我需要一些具有状态的解析器。我从来没用过 正确的解析库,但我觉得这是使用它的重点。
编写Python(或C ++,如果库)是一个明智的起点 是更好的程序从这个日志文件中提取数据?
与此同时,我尝试了几次听过的pyparsing。这是我的尝试,实际上有点工作:
import argparse
import pprint
import textwrap
from pyparsing import Word, Optional, OneOrMore, Group, ParseException, Suppress, SkipTo, ZeroOrMore, Combine
caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lowers = caps.lower()
digits = "0123456789"
def print_results(results):
lines = []
#lines.append(str(type(results)))
if isinstance(results, str):
lines.append(results)
else:
for key, val in sorted(results.items()):
lines.append(key)
lines.append(textwrap.indent(print_results(val), ' '))
return '\n'.join(lines)
def main(options):
pp = pprint.PrettyPrinter()
with open(options.logfile) as f:
content = f.read()
all_letters = ''.join(sorted(set(content)))
g_integer = Word(digits)
g_float = Word(digits + '+-.eE')
g_subgrid_volume = Suppress('subgrid volume =') + g_integer('subgrid_volume')
g_invcg = Combine(Suppress('QDP:FlopCount:invcg2 Performance/CPU: t=')
+ Suppress(g_float)
+ Suppress('(s) Flops=')
+ Suppress(g_float)
+ Suppress(' => ')
+ Suppress(g_float)
+ Suppress(' Mflops/cpu.\nQDP:FlopCount:invcg2 Total performance: ')
+ Suppress(g_float)
+ Suppress(' Mflops = ')
+ g_float('gflops')
+ Suppress(' Gflops = ')
+ Suppress(g_float)
+ Suppress(' Tflops\nCG_SOLVER: ')
+ g_integer('iterations')
+ Suppress(' iterations. Rsd = ')
+ Suppress(g_float)
+ Suppress(' Relative Rsd = ')
+ Suppress(g_float)
+ Suppress('\nCG_SOLVER_TIME: ')
+ Suppress(g_float)
+ Suppress(' sec')
)
g_minvcg = Combine(Suppress('MInvCG2: ')
+ g_integer('iterations')
+ Suppress(' iterations\nQDP:FlopCount:minvcg Performance/CPU: t=')
+ Suppress(g_float)
+ Suppress('(s) Flops=')
+ Suppress(g_float)
+ Suppress(' => ')
+ Suppress(g_float)
+ Suppress(' Mflops/cpu.\nQDP:FlopCount:minvcg Total performance: ')
+ Suppress(g_float)
+ Suppress(' Mflops = ')
+ g_float('gflops')
+ Suppress(' Gflops = ')
+ Suppress(g_float)
+ Suppress(' Tflops')
)
g_qphix_clover_cg = Combine(
'QPHIX_CLOVER_CG_SOLVER: '
+ g_integer('iterations')
+ ' iters, rsd_sq_final='
+ Suppress(g_float)
+ '\nQPHIX_CLOVER_CG_SOLVER: || r || / || b || = '
+ Suppress(g_float)
+ '\nQPHIX_CLOVER_CG_SOLVER: Solver Time='
+ Suppress(g_float)
+ ' (sec) Performance='
+ g_float('gflops')
+ ' GFLOPS\nQPHIX_MDAGM_SOLVER: total time: '
+ Suppress(g_float)
+ ' (sec)'
)
g_update = Suppress('Doing Update:') + g_integer('update_no')
g_before_update = SkipTo(g_update)
g_solver_block = (g_invcg('invcg') | g_minvcg('minvcg') | g_qphix_clover_cg('qphix_clover_cg'))
g_solver_blocks = Suppress(SkipTo(g_solver_block)) + g_solver_block
g_update_block = Suppress(g_before_update) + Group(g_update + OneOrMore(g_solver_blocks))('update_block')
g_logfile = Suppress(SkipTo(g_subgrid_volume)) + g_subgrid_volume + OneOrMore(g_update_block)
results = g_logfile.parseString(content)
print(print_results(results))
对于一个小文件只需几秒钟,并提供所需的输出:
subgrid_volume
16384
update_block
qphix_clover_cg
gflops
375.885935327638
iterations
20
update_no
1
典型文件有274480
行,需要261秒才能解析。不太酷。这不是一个真正的问题,因为生成数据的过程会运行三到六个小时。
答案 0 :(得分:1)
除非您处理大量数据并且性能是一个很大的问题,否则我的感觉是没有理由对此过于复杂。通过更新行拆分日志文件
update_lines = {}
with open(log_path, 'r') as fh:
update = None
for line in fh.readlines():
if update is None:
continue
match = re.match('^Doing Update: (\d+) warm_up_p = (\d+)', line)
if match:
update = match.group(0)
update_lines[update] = []
continue
update_lines[update].append(line)
然后,通过一系列解析特定解算器的函数为每个更新提供行列表
for update, lines in update_lines.items():
invcg_info = parse_invcg(lines)
minvcg_info = parse_minvcg(lines)
# etc, etc.
如果您受到文件/速度大小的限制,但太有限,则可以将解析链接到单个函数中,以避免一遍又一遍地遍历相同的行。我的两分钱,但我觉得任何基于EBNF的解析都可能有点过分。