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 ...@@ -27,6 +27,7 @@ from __future__ import print_function
import re, io, numpy as np import re, io, numpy as np
from collections import OrderedDict from collections import OrderedDict
from cStringIO import StringIO
# Benchmark is a collection of benchmark lines. # Benchmark is a collection of benchmark lines.
...@@ -87,24 +88,28 @@ class Stats(object): ...@@ -87,24 +88,28 @@ class Stats(object):
# ---------------------------------------- # ----------------------------------------
# '<key>: <value>' _sp_re = re.compile(r'\s')
_label_re = re.compile(r'(?P<key>\w+):\s*')
# parse_label tries to parse line as label. # parse_label tries to parse line as label.
# #
# returns (key, value). # returns (key, value).
# if line does not match - (None, None) is returned. # if line does not match - (None, None) is returned.
def parse_label(line): def parse_label(line):
m = _label_re.match(line) colon = line.find(':')
if m is None: 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 return None, None
# FIXME key must be unicode lower # FIXME key must be unicode lower
# FIXME key must not contain upper or space # FIXME key must not contain upper or space
key = m.group('key') # XXX also support 'WARNING'
value = line[m.end():]
value = value.rstrip() # XXX or rstrip only \n ?
value = value.strip() # leading and traling \s XXX for trailing - rstrip only \n ?
return key, value return key, value
...@@ -155,7 +160,8 @@ def _parse_benchline(linev): ...@@ -155,7 +160,8 @@ def _parse_benchline(linev):
# #
# r is required to implement `.readlines()`. # 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): def load(r):
labels = OrderedDict() labels = OrderedDict()
benchv = Benchmark() # of BenchLine benchv = Benchmark() # of BenchLine
...@@ -166,7 +172,11 @@ def load(r): ...@@ -166,7 +172,11 @@ def load(r):
if key is not None: if key is not None:
labels = labels.copy() labels = labels.copy()
if value: 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: else:
labels.pop(key, None) # discard labels.pop(key, None) # discard
continue continue
...@@ -178,18 +188,92 @@ def load(r): ...@@ -178,18 +188,92 @@ def load(r):
benchv.append(bl) benchv.append(bl)
continue continue
# XXX also extract warnings? return benchv, labels
return benchv
# load_file loads benchmark data from file @ path. # load_file loads benchmark data from file @ path.
# #
# returns -> Benchmark. # returns -> Benchmark, exit_labels.
def load_file(path): def load_file(path):
with io.open(path, 'r', encoding='utf-8') as f: with io.open(path, 'r', encoding='utf-8') as f:
return load(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. # method decorator allows to define methods separate from class.
def method(cls): def method(cls):
...@@ -198,7 +282,6 @@ def method(cls): ...@@ -198,7 +282,6 @@ def method(cls):
return deco return deco
# bylabel splits Benchmark into several groups of Benchmarks with specified # bylabel splits Benchmark into several groups of Benchmarks with specified
# labels having same values across a given group. # labels having same values across a given group.
# #
...@@ -386,7 +469,7 @@ def main(): ...@@ -386,7 +469,7 @@ def main():
p.add_argument("--split", default="", help="split benchmarks by labels (default no split)") p.add_argument("--split", default="", help="split benchmarks by labels (default no split)")
args = p.parse_args() args = p.parse_args()
B = load_file(args.file) B, _ = load_file(args.file)
benchstat(sys.stdout, B, split=args.split.split(",")) benchstat(sys.stdout, B, split=args.split.split(","))
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -22,13 +22,15 @@ ...@@ -22,13 +22,15 @@
import sys, re import sys, re
from collections import OrderedDict from collections import OrderedDict
from benchlib import load_file, Unit from benchlib import xload_file, Unit
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle from matplotlib.patches import Rectangle
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset, \ from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset, \
TransformedBbox, BboxPatch, BboxConnectorPatch TransformedBbox, BboxPatch, BboxConnectorPatch
from pprint import pprint
# BenchSeries represents several runs of a benchmark with different "-<n>". # BenchSeries represents several runs of a benchmark with different "-<n>".
# #
...@@ -257,15 +259,41 @@ def plotlat1(ax, S): ...@@ -257,15 +259,41 @@ def plotlat1(ax, S):
def main(): 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'] splitby = ['dataset', 'cluster']
Bl = B.bylabel(splitby) Bl = B.bylabel(splitby)
for labkey in Bl: for labkey in Bl:
print labkey
# FIXME hack # FIXME hack
if labkey == (): # cpu benchmarks if labkey == (): # cpu benchmarks
continue continue
Bu = Bl[labkey].byunit() Bu = Bl[labkey].byunit()
fig = plt.figure(figsize=(2*7.5,10)) # XXX figsize - temp? fig = plt.figure(figsize=(2*7.5,10)) # XXX figsize - temp?
...@@ -315,14 +343,23 @@ def main(): ...@@ -315,14 +343,23 @@ def main():
plt.text("xxx not found") plt.text("xxx not found")
#fig.legend() # legend showing labels from labkey
fig.legend([r0,r0], ["aaa", "bbb"]) # 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.tight_layout()
fig.subplots_adjust( fig.subplots_adjust(
left=0.05, # no big marging on the left left=0.05, # no big marging on the left
#wspace=0.1 #wspace=0.1
) )
# date
fig.text(0.003, 0.995, date, ha='left', va='top')
plt.show() plt.show()
return # XXX temp to show only first return # XXX temp to show only first
......
...@@ -498,7 +498,7 @@ else: ...@@ -498,7 +498,7 @@ else:
local gitver=$(git -C $loc describe --long --dirty 2>/dev/null) local gitver=$(git -C $loc describe --long --dirty 2>/dev/null)
local ver local ver
test "$gitver" != "" && ver="$gitver" || ver="$pyver" 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 # proginfo <prog> ... - run `prog ...` or print that prog is missing
...@@ -532,21 +532,21 @@ xhostname() { ...@@ -532,21 +532,21 @@ xhostname() {
# show information about local system (os, hardware, versions, ...) # show information about local system (os, hardware, versions, ...)
system_info() { system_info() {
echo -n "# "; date --rfc-2822 echo -ne "date:\t"; date --rfc-2822
echo -n "# `whoami`@`hostname --fqdn 2>/dev/null || hostname` (" echo -ne "xnode:\t`whoami`@`hostname --fqdn 2>/dev/null || hostname` ("
echo -n "${myaddr6v[0]}" echo -n "${myaddr6v[0]}"
test "${#myaddr6v[@]}" -eq 1 || echo -n " (+ $((${#myaddr6v[@]} - 1))·ipv6)" test "${#myaddr6v[@]}" -eq 1 || echo -n " (+ $((${#myaddr6v[@]} - 1))·ipv6)"
echo -n " ${myaddr4v[0]}" echo -n " ${myaddr4v[0]}"
test "${#myaddr4v[@]}" -eq 1 || echo -n " (+ $((${#myaddr4v[@]} - 1))·ipv4)" test "${#myaddr4v[@]}" -eq 1 || echo -n " (+ $((${#myaddr4v[@]} - 1))·ipv4)"
echo ")" echo ")"
echo -n "# "; uname -a echo -ne "uname:\t"; uname -a # XXX key name
# cpu # 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 syscpu=/sys/devices/system/cpu
sysidle=$syscpu/cpuidle 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 test $# -le 1 && echo "$@" && return
min="" min=""
...@@ -562,14 +562,14 @@ system_info() { ...@@ -562,14 +562,14 @@ system_info() {
fi fi
max=$n max=$n
done done
echo "cpu[$min-$max]" echo "cpu/[$min-$max]"
} }
freqcpuv=() # [] of cpu freqcpuv=() # [] of cpu
freqstr="" # text about cpufreq for cpus in ^^^ freqstr="" # text about cpufreq for cpus in ^^^
freqdump() { freqdump() {
test "${#freqcpuv[@]}" = 0 && return test "${#freqcpuv[@]}" = 0 && return
echo "# `cpuvabbrev ${freqcpuv[*]}`: $freqstr" echo "`cpuvabbrev ${freqcpuv[*]}`/freq: $freqstr"
freqcpuv=() freqcpuv=()
freqstr="" freqstr=""
} }
...@@ -578,7 +578,7 @@ system_info() { ...@@ -578,7 +578,7 @@ system_info() {
idlestr="" idlestr=""
idledump() { idledump() {
test "${#idlecpuv[@]}" = 0 && return test "${#idlecpuv[@]}" = 0 && return
echo "# `cpuvabbrev ${idlecpuv[*]}`: $idlestr" echo "`cpuvabbrev ${idlecpuv[*]}`/idle: $idlestr"
idlecpuv=() idlecpuv=()
idlestr="" idlestr=""
} }
...@@ -588,7 +588,7 @@ system_info() { ...@@ -588,7 +588,7 @@ system_info() {
f="$cpu/cpufreq" f="$cpu/cpufreq"
fmin=`fkghz $f/scaling_min_freq` fmin=`fkghz $f/scaling_min_freq`
fmax=`fkghz $f/scaling_max_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 if [ "$fs" != "$freqstr" ]; then
freqdump freqdump
freqstr="$fs" freqstr="$fs"
...@@ -601,7 +601,7 @@ system_info() { ...@@ -601,7 +601,7 @@ system_info() {
latmax=0 latmax=0
while read cpu; do 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 while read state; do
# XXX add target residency? # XXX add target residency?
is+=" " is+=" "
...@@ -620,8 +620,8 @@ system_info() { ...@@ -620,8 +620,8 @@ system_info() {
< <(ls -vd $syscpu/cpu[0-9]*) < <(ls -vd $syscpu/cpu[0-9]*)
idledump idledump
test "$freqstable" = y || echo "# cpu: WARNING: frequency not fixed - benchmark timings won't be stable" test "$freqstable" = y || echo "WARNING: cpu: 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 "$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 . # disk under .
...@@ -636,14 +636,14 @@ system_info() { ...@@ -636,14 +636,14 @@ system_info() {
blkdev=$1 blkdev=$1
blkdev1=`basename $blkdev` # /dev/sda -> sda blkdev1=`basename $blkdev` # /dev/sda -> sda
# XXX lsblk: tmpfs: not a block device # 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 case "$blkdev1" in
md*) md*)
# software raid # software raid
slavev=`ls -x /sys/class/block/$blkdev1/slaves` 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 # XXX dup wrt dm-*; move recursion to common place
for s in $slavev; do for s in $slavev; do
s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda
...@@ -653,7 +653,7 @@ system_info() { ...@@ -653,7 +653,7 @@ system_info() {
dm-*) dm-*)
# device mapper # device mapper
slavev=`ls -x /sys/class/block/$blkdev1/slaves` 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 # XXX dup wrt md*; move recursion to common place
for s in $slavev; do for s in $slavev; do
s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda s=`echo $s |sed -e 's/[0-9]*$//'` # sda3 -> sda
...@@ -671,7 +671,7 @@ system_info() { ...@@ -671,7 +671,7 @@ system_info() {
find /sys/class/net -type l -not -lname '*virtual*' |sort | \ find /sys/class/net -type l -not -lname '*virtual*' |sort | \
while read nic; do while read nic; do
nicname=`basename $nic` # /sys/class/net/eth0 -> eth0 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 nicdev=`realpath $nic/device` # /sys/class/net/eth0 -> /sys/devices/pci0000:00/0000:00:1f.6
case "$nicdev" in case "$nicdev" in
...@@ -700,7 +700,7 @@ system_info() { ...@@ -700,7 +700,7 @@ system_info() {
featok=y featok=y
feat=`ethtool -k $nicname 2>/dev/null` || featok=n feat=`ethtool -k $nicname 2>/dev/null` || featok=n
if [ $featok != y ]; then if [ $featok != y ]; then
echo "# $nicname: features: ?" echo "nic/$nicname/features: ?"
else else
# feat1 name abbrev -> abbrev. value (e.g. "tx" or "!tx") # feat1 name abbrev -> abbrev. value (e.g. "tx" or "!tx")
feat1() { feat1() {
...@@ -719,7 +719,7 @@ system_info() { ...@@ -719,7 +719,7 @@ system_info() {
esac esac
} }
s="# $nicname: features:" s="nic/$nicname/features:"
# NOTE feature abbrevs are those used by `ethtool -K` to set them # NOTE feature abbrevs are those used by `ethtool -K` to set them
s+=" `feat1 rx-checksumming rx`" s+=" `feat1 rx-checksumming rx`"
s+=" `feat1 tx-checksumming tx`" s+=" `feat1 tx-checksumming tx`"
...@@ -748,7 +748,7 @@ system_info() { ...@@ -748,7 +748,7 @@ system_info() {
# show rx/tx coalescing latency # show rx/tx coalescing latency
echo -n "# $nicname: coalesce:" echo -n "nic/$nicname/coalesce:"
coalok=y coalok=y
coal=`ethtool -c $nicname 2>/dev/null` || coalok=n coal=`ethtool -c $nicname 2>/dev/null` || coalok=n
if [ $coalok != y ]; then if [ $coalok != y ]; then
...@@ -779,7 +779,7 @@ system_info() { ...@@ -779,7 +779,7 @@ system_info() {
fi fi
# show main parameters + GRO flush time # show main parameters + GRO flush time
s="# $nicname:" s="nic/$nicname/status: "
s+=" `cat $nic/operstate`" s+=" `cat $nic/operstate`"
speed=`cat $nic/speed 2>/dev/null` || speed=? # returns EINVAL for wifi speed=`cat $nic/speed 2>/dev/null` || speed=? # returns EINVAL for wifi
s+=", speed=$speed" s+=", speed=$speed"
...@@ -798,15 +798,16 @@ system_info() { ...@@ -798,15 +798,16 @@ system_info() {
# emit NIC warnings # emit NIC warnings
for warn in "${nicwarnv[@]}"; do for warn in "${nicwarnv[@]}"; do
echo "# $nicname: WARNING: $warn" echo "WARNING: nic/$nicname: $warn"
done done
done done
echo -n "# "; proginfo python --version 2>&1 # https://bugs.python.org/issue18338 printf "%-20s" "sw/python:"; proginfo python --version 2>&1 # https://bugs.python.org/issue18338
echo -n "# "; proginfo go version printf "%-20s" "sw/go:"; proginfo go version
echo -n "# "; proginfo python -c 'import sqlite3 as s; print "sqlite %s (py mod %s)" % (s.sqlite_version, s.version)' printf "%-20s" "sw/sqlite:"; proginfo python -c \
echo -n "# "; proginfo mysqld --version '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 neoppod neo
pyver zodb pyver zodb
...@@ -1101,6 +1102,7 @@ zbench_go() { ...@@ -1101,6 +1102,7 @@ zbench_go() {
# command: benchmark when client and storage are on the same computer # command: benchmark when client and storage are on the same computer
cmd_bench-local() { cmd_bench-local() {
echo -e ">>> bench-local" echo -e ">>> bench-local"
echo -e "\n*** neotest: node"
system_info system_info
echo -e "\n*** cpu:\n" echo -e "\n*** cpu:\n"
bench_cpu bench_cpu
...@@ -1195,9 +1197,9 @@ cmd_bench-cluster() { ...@@ -1195,9 +1197,9 @@ cmd_bench-cluster() {
test -z "$url" && die "Usage: neotest bench-cluster [user@]<host>:<path>" test -z "$url" && die "Usage: neotest bench-cluster [user@]<host>:<path>"
echo -e ">>> bench-cluster $url" echo -e ">>> bench-cluster $url"
echo -e "\n# server:" echo -e "\n#*** neotest: node: (server)"
system_info system_info
echo -e "\n# client:" echo -e "\n#*** neotest: node: (client)"
on $url ./neotest info-local on $url ./neotest info-local
echo -e "\n*** server cpu:" 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