|
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright 2017 gRPC authors.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
# Utilities for manipulating JSON data that represents microbenchmark results.
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
# template arguments and dynamic arguments of individual benchmark types
|
|
|
|
# Example benchmark name: "BM_UnaryPingPong<TCP, NoOpMutator, NoOpMutator>/0/0"
|
|
|
|
_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_PumpUnbalancedUnary_Trickle": {
|
|
|
|
"tpl": [],
|
|
|
|
"dyn": ["cli_req_size", "svr_req_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"],
|
|
|
|
},
|
|
|
|
"BM_StreamCreateSendInitialMetadataDestroy": {
|
|
|
|
"tpl": ["fixture"],
|
|
|
|
"dyn": [],
|
|
|
|
},
|
|
|
|
"BM_TransportStreamSend": {
|
|
|
|
"tpl": [],
|
|
|
|
"dyn": ["request_size"],
|
|
|
|
},
|
|
|
|
"BM_TransportStreamRecv": {
|
|
|
|
"tpl": [],
|
|
|
|
"dyn": ["request_size"],
|
|
|
|
},
|
|
|
|
"BM_StreamingPingPongWithCoalescingApi": {
|
|
|
|
"tpl": ["fixture", "client_mutator", "server_mutator"],
|
|
|
|
"dyn": ["request_size", "request_count", "end_of_stream"],
|
|
|
|
},
|
|
|
|
"BM_Base16SomeStuff": {
|
|
|
|
"tpl": [],
|
|
|
|
"dyn": ["request_size"],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def numericalize(s):
|
|
|
|
"""Convert abbreviations like '100M' or '10k' to a number."""
|
|
|
|
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):
|
|
|
|
if not js:
|
|
|
|
raise StopIteration()
|
|
|
|
for bm in js["benchmarks"]:
|
|
|
|
if bm["name"].endswith("_stddev") or bm["name"].endswith("_mean"):
|
|
|
|
continue
|
|
|
|
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 = {}
|
|
|
|
# TODO(jtattermusch): grabbing kokoro env values shouldn't be buried
|
|
|
|
# deep in the JSON conversion logic.
|
|
|
|
# Link the data to a kokoro job run by adding
|
|
|
|
# well known kokoro env variables as metadata for each row
|
|
|
|
row = {
|
|
|
|
"jenkins_build": os.environ.get("KOKORO_BUILD_NUMBER", ""),
|
|
|
|
"jenkins_job": os.environ.get("KOKORO_JOB_NAME", ""),
|
|
|
|
}
|
|
|
|
row.update(context)
|
|
|
|
row.update(bm)
|
|
|
|
row.update(parse_name(row["name"]))
|
|
|
|
row.update(labels)
|
|
|
|
yield row
|