Commit f5fec740 authored by Kirill Smelkov's avatar Kirill Smelkov

X switch node info to labels; start adding that to plot

parent 28d00786
......@@ -27,6 +27,7 @@ from __future__ import print_function
import re, io, numpy as np
from collections import OrderedDict
from cStringIO import StringIO
# Benchmark is a collection of benchmark lines.
......@@ -87,24 +88,28 @@ class Stats(object):
# ----------------------------------------
# '<key>: <value>'
_label_re = re.compile(r'(?P<key>\w+):\s*')
_sp_re = re.compile(r'\s')
# parse_label tries to parse line as label.
#
# returns (key, value).
# if line does not match - (None, None) is returned.
def parse_label(line):
m = _label_re.match(line)
if m is None:
colon = line.find(':')
if colon == -1:
return None, None
key, value = line[:colon], line[colon+1:]
# key must not contain space
if _sp_re.search(key):
return None, None
# FIXME key must be unicode lower
# FIXME key must not contain upper or space
key = m.group('key')
value = line[m.end():]
value = value.rstrip() # XXX or rstrip only \n ?
# XXX also support 'WARNING'
value = value.strip() # leading and traling \s XXX for trailing - rstrip only \n ?
return key, value
......@@ -155,7 +160,8 @@ def _parse_benchline(linev):
#
# r is required to implement `.readlines()`.
#
# returns -> Benchmark.
# returns -> Benchmark, exit_labels.
# (exit_labels is ordered {} with labels state at end of reading)
def load(r):
labels = OrderedDict()
benchv = Benchmark() # of BenchLine
......@@ -166,7 +172,11 @@ def load(r):
if key is not None:
labels = labels.copy()
if value:
labels[key] = value
if key == 'WARNING':
# warnings accumulate, not replace previous ones
labels[key] = labels.get(key, ()) + (value,)
else:
labels[key] = value
else:
labels.pop(key, None) # discard
continue
......@@ -178,18 +188,92 @@ def load(r):
benchv.append(bl)
continue
# XXX also extract warnings?
return benchv
return benchv, labels
# load_file loads benchmark data from file @ path.
#
# returns -> Benchmark.
# returns -> Benchmark, exit_labels.
def load_file(path):
with io.open(path, 'r', encoding='utf-8') as f:
return load(f)
# xload loads benchmark data from a reader with neotest extensions handling.
#
# neotest extensions:
#
# - a line starting with `*** neotest:` denotes start of neotest extension block.
# The block consists of labels describing hardware and software on that node. XXX
# The block ends with a blank line.
# Labels in the block are not added to benchmarking lines from main stream.
# The block itself should not contain benchmark lines.
#
# returns -> Benchmark, exit_labels, []extlab.
# (extlab is ordered {} with labels from an extension block)
def xload(r):
xr = _neotestExtReader(r)
b, l = load(xr)
extv = []
for lineno, text in xr.extblockv:
bext, lext = load(StringIO(text.encode('utf-8')))
if len(bext) != 0:
raise RuntimeError("%s:%d: neotest extension block contains benchmark line" \
% (getattr(r, name, '?'), lineno))
extv.append(lext)
return b, l, extv
# _neotestExtReader is a reader that splits neotest extension data from
# benchmarking data stream.
#
# A reader reading from _neotestExtReader sees original data stream with
# extensions filtered-out. The list of extension blocks found can be accessed
# at .extblockv.
class _neotestExtReader(object):
def __init__(self, r):
self.r = r
self.extblockv = [] # of (lineno, text)
self._lineno = 0
def _readline(self):
l = self.r.readline()
if l:
self._lineno += 1
return l
def readline(self):
l = self._readline()
if not l.startswith('*** neotest:'):
return l # EOF='' so also match here
# new extension block - read up to empty line or EOF
lineno, ext = self._lineno, [l]
while 1:
l = self._readline()
if l.strip() == "":
break
ext.append(l)
self.extblockv.append((lineno, ''.join(ext)))
return l
def readlines(self):
while 1:
l = self.readline()
yield l
if not l:
break # EOF
# xload_file loads benchmark data from file @ path with neotest extensions.
#
# returns -> Benchmark, exit_labels, []extlab.
def xload_file(path):
with io.open(path, 'r', encoding='utf-8') as f:
return xload(f)
# method decorator allows to define methods separate from class.
def method(cls):
......@@ -198,7 +282,6 @@ def method(cls):
return deco
# bylabel splits Benchmark into several groups of Benchmarks with specified
# labels having same values across a given group.
#
......@@ -386,7 +469,7 @@ def main():
p.add_argument("--split", default="", help="split benchmarks by labels (default no split)")
args = p.parse_args()
B = load_file(args.file)
B, _ = load_file(args.file)
benchstat(sys.stdout, B, split=args.split.split(","))
if __name__ == '__main__':
......
......@@ -22,13 +22,15 @@
import sys, re
from collections import OrderedDict
from benchlib import load_file, Unit
from benchlib import xload_file, Unit
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset, \
TransformedBbox, BboxPatch, BboxConnectorPatch
from pprint import pprint
# BenchSeries represents several runs of a benchmark with different "-<n>".
#
......@@ -257,15 +259,41 @@ def plotlat1(ax, S):
def main():
B = load_file(sys.argv[1])
B, _, extv = xload_file(sys.argv[1])
# extv -> node {}, date
nodemap = OrderedDict()
date = None
for ext in extv:
if 'xnode' not in ext:
textv = ['%s: %s' % (k,v) for k,v in ext.items()]
raise RuntimeError('ext block without xnode:\n%s' % ('\n'.join(textv),))
xnode = ext['xnode']
# kirr@deco.navytux.spb.ru (... XXX vvv hacky, not robust
_ = xnode.split()[0] # kirr@deco.navytux.spb.ru
_ = _.split('@')[1] # deco.navytux.spb.ru
node = _.split('.')[0]
nodemap[node] = ext
if 'date' in ext:
date = ext['date']
# XXX if date = None -> warning "no date found"
if date is None:
date = "date: ?"
splitby = ['dataset', 'cluster']
Bl = B.bylabel(splitby)
for labkey in Bl:
print labkey
# FIXME hack
if labkey == (): # cpu benchmarks
continue
Bu = Bl[labkey].byunit()
fig = plt.figure(figsize=(2*7.5,10)) # XXX figsize - temp?
......@@ -315,14 +343,23 @@ def main():
plt.text("xxx not found")
#fig.legend()
fig.legend([r0,r0], ["aaa", "bbb"])
# legend showing labels from labkey
# https://matplotlib.org/tutorials/intermediate/legend_guide.html#multiple-legends-on-the-same-axes
lh = [r0] * len(labkey)
ltext = ['%s: %s' % (k,v) for k,v in labkey]
fig.legend(lh, ltext, handlelength=0, handletextpad=0, loc="upper right")
#fig.tight_layout()
fig.subplots_adjust(
left=0.05, # no big marging on the left
#wspace=0.1
)
# date
fig.text(0.003, 0.995, date, ha='left', va='top')
plt.show()
return # XXX temp to show only first
......
......@@ -498,7 +498,7 @@ else:
local gitver=$(git -C $loc describe --long --dirty 2>/dev/null)
local ver
test "$gitver" != "" && ver="$gitver" || ver="$pyver"
printf "# %-16s: %s\n" "$showas" "$ver"
printf "sw/%-16s %s\n" "${showas}:" "$ver"
}
# proginfo <prog> ... - run `prog ...` or print that prog is missing
......@@ -532,21 +532,21 @@ xhostname() {
# show information about local system (os, hardware, versions, ...)
system_info() {
echo -n "# "; date --rfc-2822
echo -n "# `whoami`@`hostname --fqdn 2>/dev/null || hostname` ("
echo -ne "date:\t"; date --rfc-2822
echo -ne "xnode:\t`whoami`@`hostname --fqdn 2>/dev/null || hostname` ("
echo -n "${myaddr6v[0]}"
test "${#myaddr6v[@]}" -eq 1 || echo -n " (+ $((${#myaddr6v[@]} - 1))·ipv6)"
echo -n " ${myaddr4v[0]}"
test "${#myaddr4v[@]}" -eq 1 || echo -n " (+ $((${#myaddr4v[@]} - 1))·ipv4)"
echo ")"
echo -n "# "; uname -a
echo -ne "uname:\t"; uname -a # XXX key name
# cpu
echo -n "# cpu: "; grep "^model name" /proc/cpuinfo |head -1 |sed -e 's/model name\s*: //'
echo -ne "cpu:\t"; grep "^model name" /proc/cpuinfo |head -1 |sed -e 's/model name\s*: //'
syscpu=/sys/devices/system/cpu
sysidle=$syscpu/cpuidle
cpuvabbrev() { # cpuvabbrev cpu0 cpu1 cpu2 ... cpuN -> cpu[0-N]
cpuvabbrev() { # cpuvabbrev cpu0 cpu1 cpu2 ... cpuN -> cpu/[0-N]
test $# -le 1 && echo "$@" && return
min=""
......@@ -562,14 +562,14 @@ system_info() {
fi
max=$n
done
echo "cpu[$min-$max]"
echo "cpu/[$min-$max]"
}
freqcpuv=() # [] of cpu
freqstr="" # text about cpufreq for cpus in ^^^
freqdump() {
test "${#freqcpuv[@]}" = 0 && return
echo "# `cpuvabbrev ${freqcpuv[*]}`: $freqstr"
echo "`cpuvabbrev ${freqcpuv[*]}`/freq: $freqstr"
freqcpuv=()
freqstr=""
}
......@@ -578,7 +578,7 @@ system_info() {
idlestr=""
idledump() {
test "${#idlecpuv[@]}" = 0 && return
echo "# `cpuvabbrev ${idlecpuv[*]}`: $idlestr"
echo "`cpuvabbrev ${idlecpuv[*]}`/idle: $idlestr"
idlecpuv=()
idlestr=""
}
......@@ -588,7 +588,7 @@ system_info() {
f="$cpu/cpufreq"
fmin=`fkghz $f/scaling_min_freq`
fmax=`fkghz $f/scaling_max_freq`
fs="freq: `cat $f/scaling_driver`/`cat $f/scaling_governor` [$fmin - $fmax]"
fs="`cat $f/scaling_driver`/`cat $f/scaling_governor` [$fmin - $fmax]"
if [ "$fs" != "$freqstr" ]; then
freqdump
freqstr="$fs"
......@@ -601,7 +601,7 @@ system_info() {
latmax=0
while read cpu; do
is="idle: `cat $sysidle/current_driver`/`cat $sysidle/current_governor_ro`:"
is="`cat $sysidle/current_driver`/`cat $sysidle/current_governor_ro`:"
while read state; do
# XXX add target residency?
is+=" "
......@@ -620,8 +620,8 @@ system_info() {
< <(ls -vd $syscpu/cpu[0-9]*)
idledump
test "$freqstable" = y || echo "# cpu: WARNING: frequency not fixed - benchmark timings won't be stable"
test "$latmax" -le 10 || echo "# cpu: WARNING: C-state exit-latency is max ${latmax}μs - up to that can add to networked and IPC request-reply latency"
test "$freqstable" = y || echo "WARNING: cpu: frequency not fixed - benchmark timings won't be stable"
test "$latmax" -le 10 || echo "WARNING: cpu: C-state exit-latency is max ${latmax}μs - up to that can add to networked and IPC request-reply latency"
# disk under .
......@@ -636,14 +636,14 @@ system_info() {
blkdev=$1
blkdev1=`basename $blkdev` # /dev/sda -> sda
# XXX lsblk: tmpfs: not a block device
echo "# $blkdev1: `lsblk -dn -o MODEL $blkdev` rev `lsblk -dn -o REV,SIZE $blkdev`"
printf "disk/%s: %s\n" "$blkdev1" "`lsblk -dn -o MODEL $blkdev` rev `lsblk -dn -o REV,SIZE $blkdev`"
}
case "$blkdev1" in
md*)
# software raid
slavev=`ls -x /sys/class/block/$blkdev1/slaves`
echo "# $blkdev1 (`cat /sys/class/block/$blkdev1/md/level`) -> $slavev"
printf "disk/%s:\t%s\n" "$blkdev1" "(`cat /sys/class/block/$blkdev1/md/level`) -> $slavev"
# XXX dup wrt dm-*; move recursion to common place
for s in $slavev; do
s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda
......@@ -653,7 +653,7 @@ system_info() {
dm-*)
# device mapper
slavev=`ls -x /sys/class/block/$blkdev1/slaves`
echo "# $blkdev1 (`cat /sys/class/block/$blkdev1/dm/name`) -> $slavev"
printf "disk/%s:\t%s\n" "$blkdev1" "(`cat /sys/class/block/$blkdev1/dm/name`) -> $slavev"
# XXX dup wrt md*; move recursion to common place
for s in $slavev; do
s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda
......@@ -671,7 +671,7 @@ system_info() {
find /sys/class/net -type l -not -lname '*virtual*' |sort | \
while read nic; do
nicname=`basename $nic` # /sys/class/net/eth0 -> eth0
echo -n "# $nicname: "
echo -n "nic/$nicname: "
nicdev=`realpath $nic/device` # /sys/class/net/eth0 -> /sys/devices/pci0000:00/0000:00:1f.6
case "$nicdev" in
......@@ -700,7 +700,7 @@ system_info() {
featok=y
feat=`ethtool -k $nicname 2>/dev/null` || featok=n
if [ $featok != y ]; then
echo "# $nicname: features: ?"
echo "nic/$nicname/features: ?"
else
# feat1 name abbrev -> abbrev. value (e.g. "tx" or "!tx")
feat1() {
......@@ -719,7 +719,7 @@ system_info() {
esac
}
s="# $nicname: features:"
s="nic/$nicname/features:"
# NOTE feature abbrevs are those used by `ethtool -K` to set them
s+=" `feat1 rx-checksumming rx`"
s+=" `feat1 tx-checksumming tx`"
......@@ -748,7 +748,7 @@ system_info() {
# show rx/tx coalescing latency
echo -n "# $nicname: coalesce:"
echo -n "nic/$nicname/coalesce:"
coalok=y
coal=`ethtool -c $nicname 2>/dev/null` || coalok=n
if [ $coalok != y ]; then
......@@ -779,7 +779,7 @@ system_info() {
fi
# show main parameters + GRO flush time
s="# $nicname:"
s="nic/$nicname/status: "
s+=" `cat $nic/operstate`"
speed=`cat $nic/speed 2>/dev/null` || speed=? # returns EINVAL for wifi
s+=", speed=$speed"
......@@ -798,15 +798,16 @@ system_info() {
# emit NIC warnings
for warn in "${nicwarnv[@]}"; do
echo "# $nicname: WARNING: $warn"
echo "WARNING: nic/$nicname: $warn"
done
done
echo -n "# "; proginfo python --version 2>&1 # https://bugs.python.org/issue18338
echo -n "# "; proginfo go version
echo -n "# "; proginfo python -c 'import sqlite3 as s; print "sqlite %s (py mod %s)" % (s.sqlite_version, s.version)'
echo -n "# "; proginfo mysqld --version
printf "%-20s" "sw/python:"; proginfo python --version 2>&1 # https://bugs.python.org/issue18338
printf "%-20s" "sw/go:"; proginfo go version
printf "%-20s" "sw/sqlite:"; proginfo python -c \
'import sqlite3 as s; print "sqlite %s (py mod %s)" % (s.sqlite_version, s.version)'
printf "%-20s" "sw/mysqld:"; proginfo mysqld --version
pyver neoppod neo
pyver zodb
......@@ -1101,6 +1102,7 @@ zbench_go() {
# command: benchmark when client and storage are on the same computer
cmd_bench-local() {
echo -e ">>> bench-local"
echo -e "\n*** neotest: node"
system_info
echo -e "\n*** cpu:\n"
bench_cpu
......@@ -1195,9 +1197,9 @@ cmd_bench-cluster() {
test -z "$url" && die "Usage: neotest bench-cluster [user@]<host>:<path>"
echo -e ">>> bench-cluster $url"
echo -e "\n# server:"
echo -e "\n#*** neotest: node: (server)"
system_info
echo -e "\n# client:"
echo -e "\n#*** neotest: node: (client)"
on $url ./neotest info-local
echo -e "\n*** server cpu:"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment