Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/graph-ar

bench/summarize.py

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

"""Summarize run.sh JSON lines into Markdown tables (median of the runs)."""

import json
import statistics
import sys
from collections import defaultdict


def load(paths):
    rows, errors = [], []
    for p in paths:
        for line in open(p):
            r = json.loads(line)
            (errors if "error" in r else rows).append(r)
    return rows, errors


def med(xs):
    return statistics.median(xs) if xs else float("nan")


def fmt_s(x):
    if x != x:
        return "-"
    return f"{x * 1000:.1f} ms" if x < 1 else f"{x:.2f} s"


def main(paths):
    rows, errors = load(paths)
    w = defaultdict(lambda: {"secs": [], "rss": [], "bytes": None, "n": None})
    reads = defaultdict(lambda: {"secs": [], "rss": [], "checks": set()})
    for r in rows:
        x = r["row"]
        op = x["op"]
        if op.startswith("write"):
            k = (r["label"], r["dataset"], r["format"], op, x["impl"])
            w[k]["secs"].append(x["secs"])
            w[k]["rss"].append(r["rss_kb"] / 1024)
            w[k]["bytes"] = x["check"].get("bytes")
            w[k]["n"] = x["check"].get("vertices", x["check"].get("edges"))
        else:
            k = (r["label"], r["dataset"], r["format"], op, r["graph"], x["impl"])
            reads[k]["secs"].append(x["secs"])
            reads[k]["rss"].append(r["rss_kb"] / 1024)
            reads[k]["checks"].add(json.dumps(x["check"], sort_keys=True))

    print("### Writes\n")
    print("| label | dataset | format | operation | implementation | rows | median time | peak RSS | bytes on disk |")
    print("|---|---|---|---|---|---:|---:|---:|---:|")
    for k in sorted(w):
        v = w[k]
        b = f"{v['bytes'] / 1e6:.1f} MB" if v["bytes"] else "-"
        print(f"| {k[0]} | {k[1]} | {k[2]} | {k[3]} | {k[4]} | {v['n']} | {fmt_s(med(v['secs']))} | {med(v['rss']):.0f} MB | {b} |")

    print("\n### Reads\n")
    print("| label | dataset | format | operation | files written by | native | C++ | C++ / native | native RSS | C++ RSS |")
    print("|---|---|---|---|---|---:|---:|---:|---:|---:|")
    keys = sorted({k[:5] for k in reads})
    for k in keys:
        n = reads.get(k + ("graphar-native",))
        c = reads.get(k + ("graphar-cpp",))
        ns = med(n["secs"]) if n else float("nan")
        cs = med(c["secs"]) if c else float("nan")
        ratio = f"{cs / ns:.1f}x" if n and c and ns > 0 else "-"
        nr = f"{med(n['rss']):.0f} MB" if n else "-"
        cr = f"{med(c['rss']):.0f} MB" if c else "-"
        print(f"| {k[0]} | {k[1]} | {k[2]} | {k[3]} | {k[4]} | {fmt_s(ns)} | {fmt_s(cs)} | {ratio} | {nr} | {cr} |")

    print("\n### Same answer from every reader and writer\n")
    print("| label | dataset | format | operation | readers x writers | checks agree |")
    print("|---|---|---|---|---:|---|")
    agree = defaultdict(lambda: [set(), 0])
    for k, v in reads.items():
        a = agree[k[:4]]
        a[0] |= v["checks"]
        a[1] += 1
    for k in sorted(agree):
        checks, count = agree[k]
        print(f"| {k[0]} | {k[1]} | {k[2]} | {k[3]} | {count} | {'yes' if len(checks) == 1 else 'NO: ' + ' / '.join(sorted(checks))} |")

    if errors:
        print("\n### Errors\n")
        for e in errors:
            print(f"- {e['dataset']} {e['format']} {e['graph']}: `{e.get('cmd', '')}`: {e['error'].strip()[:200]}")


if __name__ == "__main__":
    main(sys.argv[1:])