diff options
author | Craig Tiller <ctiller@google.com> | 2017-03-14 07:59:19 -0700 |
---|---|---|
committer | Craig Tiller <ctiller@google.com> | 2017-03-14 07:59:19 -0700 |
commit | 82d72e79dfb1f1e59844c7b6d5826a726b84e631 (patch) | |
tree | 01eeeba16f88f7e875254add08ff45196f55460a /tools/profiling | |
parent | b18c8ba0b6bbc2317dd92353288f2b1c0ff353d5 (diff) | |
parent | 264a2108788fde232dfec976a511ed732864ddb0 (diff) |
Merge github.com:grpc/grpc into nolock
Diffstat (limited to 'tools/profiling')
-rwxr-xr-x | tools/profiling/latency_profile/run_latency_profile.sh | 5 | ||||
-rwxr-xr-x | tools/profiling/microbenchmarks/bm2bq.py | 160 | ||||
-rwxr-xr-x | tools/profiling/microbenchmarks/bm_diff.py | 118 | ||||
-rw-r--r-- | tools/profiling/microbenchmarks/bm_json.py | 184 |
4 files changed, 323 insertions, 144 deletions
diff --git a/tools/profiling/latency_profile/run_latency_profile.sh b/tools/profiling/latency_profile/run_latency_profile.sh index e9baee0957..41423fc3c1 100755 --- a/tools/profiling/latency_profile/run_latency_profile.sh +++ b/tools/profiling/latency_profile/run_latency_profile.sh @@ -44,4 +44,9 @@ else PYTHON=python2.7 fi +make CONFIG=opt memory_profile_test memory_profile_client memory_profile_server +bins/opt/memory_profile_test +bq load microbenchmarks.memory memory_usage.csv + $PYTHON tools/run_tests/run_microbenchmark.py --collect summary perf latency --bigquery_upload + diff --git a/tools/profiling/microbenchmarks/bm2bq.py b/tools/profiling/microbenchmarks/bm2bq.py index a83b3be89c..ffb11f57d8 100755 --- a/tools/profiling/microbenchmarks/bm2bq.py +++ b/tools/profiling/microbenchmarks/bm2bq.py @@ -36,7 +36,7 @@ import sys import json import csv -import os +import bm_json columns = [ ('jenkins_build', 'integer'), @@ -73,6 +73,14 @@ columns = [ ('framing_bytes_per_iteration', 'float'), ] +SANITIZE = { + 'integer': int, + 'float': float, + 'boolean': bool, + 'string': str, + 'timestamp': str, +} + if sys.argv[1] == '--schema': print ',\n'.join('%s:%s' % (k, t.upper()) for k, t in columns) sys.exit(0) @@ -88,147 +96,11 @@ else: writer = csv.DictWriter(sys.stdout, [c for c,t in columns]) -bm_specs = { - 'BM_UnaryPingPong': { - 'tpl': ['fixture', 'client_mutator', 'server_mutator'], - 'dyn': ['request_size', 'response_size'], - }, - 'BM_PumpStreamClientToServer': { - 'tpl': ['fixture'], - 'dyn': ['request_size'], - }, - 'BM_PumpStreamServerToClient': { - 'tpl': ['fixture'], - 'dyn': ['request_size'], - }, - 'BM_StreamingPingPong': { - 'tpl': ['fixture', 'client_mutator', 'server_mutator'], - 'dyn': ['request_size', 'request_count'], - }, - 'BM_StreamingPingPongMsgs': { - 'tpl': ['fixture', 'client_mutator', 'server_mutator'], - 'dyn': ['request_size'], - }, - 'BM_PumpStreamServerToClient_Trickle': { - 'tpl': [], - 'dyn': ['request_size', 'bandwidth_kilobits'], - }, - 'BM_ErrorStringOnNewError': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_ErrorStringRepeatedly': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_ErrorGetStatus': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_ErrorGetStatusCode': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_ErrorHttpError': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_HasClearGrpcStatus': { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_IsolatedFilter' : { - 'tpl': ['fixture', 'client_mutator'], - 'dyn': [], - }, - 'BM_HpackEncoderEncodeHeader' : { - 'tpl': ['fixture'], - 'dyn': ['end_of_stream', 'request_size'], - }, - 'BM_HpackParserParseHeader' : { - 'tpl': ['fixture'], - 'dyn': [], - }, - 'BM_CallCreateDestroy' : { - 'tpl': ['fixture'], - 'dyn': [], - }, -} - -def numericalize(s): - if not s: return '' - if s[-1] == 'k': - return int(s[:-1]) * 1024 - if s[-1] == 'M': - return int(s[:-1]) * 1024 * 1024 - if 0 <= (ord(s[-1]) - ord('0')) <= 9: - return int(s) - assert 'not a number: %s' % s - -def parse_name(name): - if '<' not in name and '/' not in name and name not in bm_specs: - return {'name': name} - rest = name - out = {} - tpl_args = [] - dyn_args = [] - if '<' in rest: - tpl_bit = rest[rest.find('<') + 1 : rest.rfind('>')] - arg = '' - nesting = 0 - for c in tpl_bit: - if c == '<': - nesting += 1 - arg += c - elif c == '>': - nesting -= 1 - arg += c - elif c == ',': - if nesting == 0: - tpl_args.append(arg.strip()) - arg = '' - else: - arg += c - else: - arg += c - tpl_args.append(arg.strip()) - rest = rest[:rest.find('<')] + rest[rest.rfind('>') + 1:] - if '/' in rest: - s = rest.split('/') - rest = s[0] - dyn_args = s[1:] - name = rest - assert name in bm_specs, 'bm_specs needs to be expanded for %s' % name - assert len(dyn_args) == len(bm_specs[name]['dyn']) - assert len(tpl_args) == len(bm_specs[name]['tpl']) - out['name'] = name - out.update(dict((k, numericalize(v)) for k, v in zip(bm_specs[name]['dyn'], dyn_args))) - out.update(dict(zip(bm_specs[name]['tpl'], tpl_args))) - return out +for row in bm_json.expand_json(js, js2): + sane_row = {} + for name, sql_type in columns: + if name in row: + if row[name] == '': continue + sane_row[name] = SANITIZE[sql_type](row[name]) + writer.writerow(sane_row) -for bm in js['benchmarks']: - context = js['context'] - if 'label' in bm: - labels_list = [s.split(':') for s in bm['label'].strip().split(' ') if len(s) and s[0] != '#'] - for el in labels_list: - el[0] = el[0].replace('/iter', '_per_iteration') - labels = dict(labels_list) - else: - labels = {} - row = { - 'jenkins_build': os.environ.get('BUILD_NUMBER', ''), - 'jenkins_job': os.environ.get('JOB_NAME', ''), - } - row.update(context) - row.update(bm) - row.update(parse_name(row['name'])) - row.update(labels) - if 'label' in row: - del row['label'] - if js2: - for bm2 in js2['benchmarks']: - if bm['name'] == bm2['name']: - row['cpu_time'] = bm2['cpu_time'] - row['real_time'] = bm2['real_time'] - row['iterations'] = bm2['iterations'] - writer.writerow(row) diff --git a/tools/profiling/microbenchmarks/bm_diff.py b/tools/profiling/microbenchmarks/bm_diff.py new file mode 100755 index 0000000000..14ea24f65e --- /dev/null +++ b/tools/profiling/microbenchmarks/bm_diff.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python2.7 +# Copyright 2017, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys +import json +import bm_json +import tabulate +import argparse + +def changed_ratio(n, o): + if float(o) <= .0001: o = 0 + if float(n) <= .0001: n = 0 + if o == 0 and n == 0: return 0 + if o == 0: return 100 + return (float(n)-float(o))/float(o) + +def min_change(pct): + return lambda n, o: abs(changed_ratio(n,o)) > pct/100.0 + +_INTERESTING = { + 'cpu_time': min_change(10), + 'real_time': min_change(10), + 'locks_per_iteration': min_change(5), + 'allocs_per_iteration': min_change(5), + 'writes_per_iteration': min_change(5), + 'atm_cas_per_iteration': min_change(1), + 'atm_add_per_iteration': min_change(5), +} + +argp = argparse.ArgumentParser(description='Perform diff on microbenchmarks') +argp.add_argument('-t', '--track', + choices=sorted(_INTERESTING.keys()), + nargs='+', + default=sorted(_INTERESTING.keys()), + help='Which metrics to track') +argp.add_argument('files', metavar='bm_file.json', type=str, nargs=4, + help='files to diff. ') +args = argp.parse_args() + +with open(args.files[0]) as f: + js_new_ctr = json.loads(f.read()) +with open(args.files[1]) as f: + js_new_opt = json.loads(f.read()) +with open(args.files[2]) as f: + js_old_ctr = json.loads(f.read()) +with open(args.files[3]) as f: + js_old_opt = json.loads(f.read()) + +new = {} +old = {} + +for row in bm_json.expand_json(js_new_ctr, js_new_opt): + new[row['cpp_name']] = row +for row in bm_json.expand_json(js_old_ctr, js_old_opt): + old[row['cpp_name']] = row + +changed = [] +for fld in args.track: + chk = _INTERESTING[fld] + for bm in new.keys(): + if bm not in old: continue + n = new[bm] + o = old[bm] + if fld not in n or fld not in o: continue + if chk(n[fld], o[fld]): + changed.append((fld, chk)) + break + +headers = ['Benchmark'] + [c[0] for c in changed] + ['Details'] +rows = [] +for bm in sorted(new.keys()): + if bm not in old: continue + row = [bm] + any_changed = False + n = new[bm] + o = old[bm] + details = '' + for fld in args.track: + chk = _INTERESTING[fld] + if fld not in n or fld not in o: continue + if chk(n[fld], o[fld]): + row.append(changed_ratio(n[fld], o[fld])) + if details: details += ', ' + details += '%s:%r-->%r' % (fld, float(o[fld]), float(n[fld])) + any_changed = True + else: + row.append('') + if any_changed: + row.append(details) + rows.append(row) +print tabulate.tabulate(rows, headers=headers, floatfmt='+.2f') diff --git a/tools/profiling/microbenchmarks/bm_json.py b/tools/profiling/microbenchmarks/bm_json.py new file mode 100644 index 0000000000..4695f829f4 --- /dev/null +++ b/tools/profiling/microbenchmarks/bm_json.py @@ -0,0 +1,184 @@ +# Copyright 2017, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os + +_BM_SPECS = { + 'BM_UnaryPingPong': { + 'tpl': ['fixture', 'client_mutator', 'server_mutator'], + 'dyn': ['request_size', 'response_size'], + }, + 'BM_PumpStreamClientToServer': { + 'tpl': ['fixture'], + 'dyn': ['request_size'], + }, + 'BM_PumpStreamServerToClient': { + 'tpl': ['fixture'], + 'dyn': ['request_size'], + }, + 'BM_StreamingPingPong': { + 'tpl': ['fixture', 'client_mutator', 'server_mutator'], + 'dyn': ['request_size', 'request_count'], + }, + 'BM_StreamingPingPongMsgs': { + 'tpl': ['fixture', 'client_mutator', 'server_mutator'], + 'dyn': ['request_size'], + }, + 'BM_PumpStreamServerToClient_Trickle': { + 'tpl': [], + 'dyn': ['request_size', 'bandwidth_kilobits'], + }, + 'BM_ErrorStringOnNewError': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_ErrorStringRepeatedly': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_ErrorGetStatus': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_ErrorGetStatusCode': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_ErrorHttpError': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_HasClearGrpcStatus': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_IsolatedFilter': { + 'tpl': ['fixture', 'client_mutator'], + 'dyn': [], + }, + 'BM_HpackEncoderEncodeHeader': { + 'tpl': ['fixture'], + 'dyn': ['end_of_stream', 'request_size'], + }, + 'BM_HpackParserParseHeader': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_CallCreateDestroy': { + 'tpl': ['fixture'], + 'dyn': [], + }, + 'BM_Zalloc': { + 'tpl': [], + 'dyn': ['request_size'], + }, + 'BM_PollEmptyPollset_SpeedOfLight': { + 'tpl': [], + 'dyn': ['request_size', 'request_count'], + } +} + +def numericalize(s): + if not s: return '' + if s[-1] == 'k': + return float(s[:-1]) * 1024 + if s[-1] == 'M': + return float(s[:-1]) * 1024 * 1024 + if 0 <= (ord(s[-1]) - ord('0')) <= 9: + return float(s) + assert 'not a number: %s' % s + +def parse_name(name): + cpp_name = name + if '<' not in name and '/' not in name and name not in _BM_SPECS: + return {'name': name, 'cpp_name': name} + rest = name + out = {} + tpl_args = [] + dyn_args = [] + if '<' in rest: + tpl_bit = rest[rest.find('<') + 1 : rest.rfind('>')] + arg = '' + nesting = 0 + for c in tpl_bit: + if c == '<': + nesting += 1 + arg += c + elif c == '>': + nesting -= 1 + arg += c + elif c == ',': + if nesting == 0: + tpl_args.append(arg.strip()) + arg = '' + else: + arg += c + else: + arg += c + tpl_args.append(arg.strip()) + rest = rest[:rest.find('<')] + rest[rest.rfind('>') + 1:] + if '/' in rest: + s = rest.split('/') + rest = s[0] + dyn_args = s[1:] + name = rest + assert name in _BM_SPECS, '_BM_SPECS needs to be expanded for %s' % name + assert len(dyn_args) == len(_BM_SPECS[name]['dyn']) + assert len(tpl_args) == len(_BM_SPECS[name]['tpl']) + out['name'] = name + out['cpp_name'] = cpp_name + out.update(dict((k, numericalize(v)) for k, v in zip(_BM_SPECS[name]['dyn'], dyn_args))) + out.update(dict(zip(_BM_SPECS[name]['tpl'], tpl_args))) + return out + +def expand_json(js, js2 = None): + for bm in js['benchmarks']: + context = js['context'] + if 'label' in bm: + labels_list = [s.split(':') for s in bm['label'].strip().split(' ') if len(s) and s[0] != '#'] + for el in labels_list: + el[0] = el[0].replace('/iter', '_per_iteration') + labels = dict(labels_list) + else: + labels = {} + row = { + 'jenkins_build': os.environ.get('BUILD_NUMBER', ''), + 'jenkins_job': os.environ.get('JOB_NAME', ''), + } + row.update(context) + row.update(bm) + row.update(parse_name(row['name'])) + row.update(labels) + if js2: + for bm2 in js2['benchmarks']: + if bm['name'] == bm2['name']: + row['cpu_time'] = bm2['cpu_time'] + row['real_time'] = bm2['real_time'] + row['iterations'] = bm2['iterations'] + yield row |