Mercurial > hg
view contrib/perf-utils/perf-revlog-write-plot.py @ 45095:8e04607023e5
procutil: ensure that procutil.std{out,err}.write() writes all bytes
Python 3 offers different kind of streams and it’s not guaranteed for all of
them that calling write() writes all bytes.
When Python is started in unbuffered mode, sys.std{out,err}.buffer are
instances of io.FileIO, whose write() can write less bytes for
platform-specific reasons (e.g. Linux has a 0x7ffff000 bytes maximum and could
write less if interrupted by a signal; when writing to Windows consoles, it’s
limited to 32767 bytes to avoid the "not enough space" error). This can lead to
silent loss of data, both when using sys.std{out,err}.buffer (which may in fact
not be a buffered stream) and when using the text streams sys.std{out,err}
(I’ve created a CPython bug report for that:
https://bugs.python.org/issue41221).
Python may fix the problem at some point. For now, we implement our own wrapper
for procutil.std{out,err} that calls the raw stream’s write() method until all
bytes have been written. We don’t use sys.std{out,err} for larger writes, so I
think it’s not worth the effort to patch them.
author | Manuel Jacob <me@manueljacob.de> |
---|---|
date | Fri, 10 Jul 2020 12:27:58 +0200 |
parents | 2372284d9457 |
children | c102b704edb5 |
line wrap: on
line source
#!/usr/bin/env python # # Copyright 2018 Paul Morelle <Paul.Morelle@octobus.net> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. # # This script use the output of `hg perfrevlogwrite -T json --details` to draw # various plot related to write performance in a revlog # # usage: perf-revlog-write-plot.py details.json from __future__ import absolute_import, print_function import json import re import numpy as np import scipy.signal from matplotlib import ( pyplot as plt, ticker as mticker, ) def plot(data, title=None): items = {} re_title = re.compile(r'^revisions #\d+ of \d+, rev (\d+)$') for item in data: m = re_title.match(item['title']) if m is None: continue rev = int(m.group(1)) items[rev] = item min_rev = min(items.keys()) max_rev = max(items.keys()) ary = np.empty((2, max_rev - min_rev + 1)) for rev, item in items.items(): ary[0][rev - min_rev] = rev ary[1][rev - min_rev] = item['wall'] fig = plt.figure() comb_plt = fig.add_subplot(211) other_plt = fig.add_subplot(212) comb_plt.plot( ary[0], np.cumsum(ary[1]), color='red', linewidth=1, label='comb' ) plots = [] p = other_plt.plot(ary[0], ary[1], color='red', linewidth=1, label='wall') plots.append(p) colors = { 10: ('green', 'xkcd:grass green'), 100: ('blue', 'xkcd:bright blue'), 1000: ('purple', 'xkcd:dark pink'), } for n, color in colors.items(): avg_n = np.convolve(ary[1], np.full(n, 1.0 / n), 'valid') p = other_plt.plot( ary[0][n - 1 :], avg_n, color=color[0], linewidth=1, label='avg time last %d' % n, ) plots.append(p) med_n = scipy.signal.medfilt(ary[1], n + 1) p = other_plt.plot( ary[0], med_n, color=color[1], linewidth=1, label='median time last %d' % n, ) plots.append(p) formatter = mticker.ScalarFormatter() formatter.set_scientific(False) formatter.set_useOffset(False) comb_plt.grid() comb_plt.xaxis.set_major_formatter(formatter) comb_plt.legend() other_plt.grid() other_plt.xaxis.set_major_formatter(formatter) leg = other_plt.legend() leg2plot = {} for legline, plot in zip(leg.get_lines(), plots): legline.set_picker(5) leg2plot[legline] = plot def onpick(event): legline = event.artist plot = leg2plot[legline] visible = not plot[0].get_visible() for l in plot: l.set_visible(visible) if visible: legline.set_alpha(1.0) else: legline.set_alpha(0.2) fig.canvas.draw() if title is not None: fig.canvas.set_window_title(title) fig.canvas.mpl_connect('pick_event', onpick) plt.show() if __name__ == '__main__': import sys if len(sys.argv) > 1: print('reading from %r' % sys.argv[1]) with open(sys.argv[1], 'r') as fp: plot(json.load(fp), title=sys.argv[1]) else: print('reading from stdin') plot(json.load(sys.stdin))