Commit d5dcab3b authored by Jan Kiene's avatar Jan Kiene
Browse files

Merge branch 'ci/add-reference-lines-to-complexity-plots' into 'main'

[CI] Complexity measurement refactoring + preparation for adding reference lines and bars in graphs

See merge request !1784
parents 8009f2af ea733d20
Loading
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -13,7 +13,9 @@ COLS = [

def main(args):
    linewise_logfiles = [args.wmops_logfile, args.ram_logfile, args.rom_logfile]
    changes_found_linewise = any([check_linewise_logfile(f, c) for f, c in zip(linewise_logfiles, COLS)])
    changes_found_linewise = any(
        [check_linewise_logfile(f, c) for f, c in zip(linewise_logfiles, COLS)]
    )

    if changes_found_linewise:
        print("Global max of WMOPS, RAM or ROM changed")
+449 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
"""
(C) 2022-2024 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository. All Rights Reserved.

This software is protected by copyright law and by international treaties.
The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository retain full ownership rights in their respective contributions in
the software. This notice grants no license of any kind, including but not limited to patent
license, nor is any license granted by implication, estoppel or otherwise.

Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
contributions.

This software is provided "AS IS", without any express or implied warranties. The software is in the
development stage. It is intended exclusively for experts who have experience with such software and
solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
and fitness for a particular purpose are hereby disclaimed and excluded.

Any dispute, controversy or claim arising under or in relation to providing this software shall be
submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
the United Nations Convention on Contracts on the International Sales of Goods.
"""

import argparse
from typing import Optional


MAX_VALUES = 40

REF_WMOPS_EVS = 88
REF_WMOPS_3EVS = 3 * REF_WMOPS_EVS
REF_WMOPS_6EVS = 6 * REF_WMOPS_EVS
REF_WMOPS_10EVS = 10 * REF_WMOPS_EVS

REF_COLORS = ["#000000", "#666666", "#AAAAAA"]

RUNS_KEYS_COMMON = ["fullDate", "shortDate", "revision", "logFile"]
RUNS_KEYS_WMOPS = RUNS_KEYS_COMMON + [
    "worstCaseEnc",
    "worstCaseDec",
    "worstCaseCodec",
    "worstCaseEncRs",
    "worstCaseDecRs",
    "worstCaseCodecRs",
    "fixpointScalingFac",
]
RUNS_KEYS_WMOPS_PER_OP = [
    "operatingPoint",
    "mode",
]
RUNS_KEYS_ROM = RUNS_KEYS_COMMON + [
    "PromEnc",
    "PromDec",
    "PromCom",
    "PromRend",
    "TromEnc",
    "TromDec",
    "TromCom",
    "TromRend",
]
RUNS_KEYS_RAM = RUNS_KEYS_COMMON + [
    "maxTotalRamEnc",
    "maxTotalRamDec",
    "maxStackEnc",
    "maxStackDec",
    "maxHeapEnc",
    "maxHeapDec",
]

RUNS_KEYS = {
    "wmops": RUNS_KEYS_WMOPS,
    "rom": RUNS_KEYS_ROM,
    "ram": RUNS_KEYS_RAM,
    "wmops_per_op": RUNS_KEYS_WMOPS_PER_OP,
}
RUNS_LINE_IDX = {
    "wmops": [2, 1, 0, 4, 6, 8, 12, 14, 16, 10, 18],
    "rom": [2, 1, 0, 4, 6, 8, 10, 12, 14, 16, 18, 20],
    "ram": [2, 1, 0, 4, 6, 9, 11, 14, 16, 18],
    "wmops_per_op": [0, 4],
}

DISPLAY_IDS = {
    "wmops": [
        "3xEVS",
        "6xEVS",
        "10xEVS",
        "worst case codec",
        "worst case enc/dec",
        "worst case enc",
        "worst case dec",
    ],
    "rom": [
        "requirementRom",
        "TotalRomCodecScore",
        "maxPROMEncScore",
        "maxPROMDecScore",
        "maxPROMComScore",
        "maxPROMRendScore",
        "maxTROMEncScore",
        "maxTROMDecScore",
        "maxTROMComScore",
        "maxTROMRendScore",
    ],
    "ram": [
        "requirementRam",
        "maxTotalRamCodecScore",
        "maxTotalRamEncScore",
        "maxTotalRamDecScore",
        "maxStackCodecScore",
        "maxStackEncScore",
        "maxStackDecScore",
        "maxHeapCodecScore",
        "maxHeapEncScore",
        "maxHeapDecScore",
    ],
    "wmops_per_op": [
        "worstCaseEnc",
        "worstCaseDec",
    ],
}
# the -1's are for reference lines
DISPLAY_LINE_IDX = {
    "wmops": [-1, -1, -1, 9, 3, 5, 7],
    "rom": [-1, 3, 5, 7, 9, 11, 13, 15, 17, 19],
    "ram": [-1, 3, 5, 7, 8, 10, 12, 13, 15, 17],
    "wmops_per_op": [1, 2],
}
DISPLAY_LABELS = {
    "wmops_per_op": ["Encoder", "Decoder"],
}
DISPLAY_ELEM_TEMPLATE = '{{ lines: {{ show: true }}, points: {{ show: true, fillColor: "#ffffff" }}, borderWidth: 1.5, borderColor: "#BEBEBE", markingsLineWidth: .75, hoverable: true, clickable: false, shadowSize: 0, color: "{color}", id: "{id}", data: [ {data} ] }}'

REF_COLOR_FOR_COMP_BARS = dict(
    zip(DISPLAY_LABELS["wmops_per_op"], [REF_COLORS[0], REF_COLORS[2]])
)
LINE_COLORS = {
    "wmops": [
        REF_COLORS[0],
        REF_COLORS[1],
        REF_COLORS[2],
        "#0080FF",
        "#FF8000",
        "#CF4B4B",
        "#008040",
    ],
    "rom": [
        REF_COLORS[0],
        "#FF0000",
        "#FF8000",
        "#FFFF00",
        "#800080",
        "#0000FF",
        "#0080C0",
        "#004000",
        "#008000",
        "#00FF00",
    ],
    "ram": [
        REF_COLORS[0],
        "#FF0000",
        "#FF8000",
        "#FFFF00",
        "#004000",
        "#008000",
        "#00FF00",
        "#800080",
        "#0000FF",
        "#0080C0",
    ],
    "wmops_per_op": [
        "#CF4B4B",
        "#008040",
    ],
}

JS_FILE_TEMPLATE = """var {var_name} = {{
    {elem_name}: {{
        description: "{description}",
        direction: -1,
        ticks: [
            {ticks}
            ],
        runs: [
            {runs}
            ],
        displays: [
            {displays}
            ]
        }}
}};
"""

FILE_DATA = {
    "wmops": {
        "var_name": "Graphs_WMOPS",
        "elem_name": "wmops_worstcase",
        "description": "Worst Case WMOPS",
        "filename": "graphs_wmops_flc.js",
        "references": {
            "3xEVS": REF_WMOPS_3EVS,
            "6xEVS": REF_WMOPS_6EVS,
            "10xEVS": REF_WMOPS_10EVS,
        },
    },
    "rom": {
        "var_name": "Graphs_ROM",
        "elem_name": "rom_worstcase",
        "description": "ROM",
        "filename": "graphs_rom_flc.js",
        "references": {
            "requirementRom": 0,
        },
    },
    "ram": {
        "var_name": "Graphs_RAM",
        "elem_name": "ram_worstcase",
        "description": "Worst Case RAM",
        "filename": "graphs_ram_flc.js",
        "references": {
            "requirementRam": 0,
        },
    },
    "wmops_per_op": {
        "var_name": "Graphs_WMOPS_perOP",
        "elem_name": "wmops_worstcase_per_op",
        "description": "Worst Case WMOPS per OP",
        "filename": "graphs_wmops_flc_perOP.js",
        "references": {},
    },
}


def main(
    wmops_log, wmops_per_op_log, rom_log, ram_log, wmops_per_op_log_for_comparison
):
    FILE_DATA["wmops"]["log_file"] = wmops_log
    FILE_DATA["wmops_per_op"]["log_file"] = wmops_per_op_log
    FILE_DATA["rom"]["log_file"] = rom_log
    FILE_DATA["ram"]["log_file"] = ram_log

    for x, data in FILE_DATA.items():
        with open(data["log_file"]) as f:
            log_lines = f.readlines()

        split_char = " "
        ticks = []
        if x == "wmops_per_op":
            split_char = ";"
            log_lines, ticks = pre_proc_log_lines(
                log_lines, wmops_per_op_log_for_comparison
            )

        else:
            log_lines = log_lines[-MAX_VALUES:]

        runs = [
            create_runs_string(line.strip().split(split_char), x) for line in log_lines
        ]
        displays = create_display_strings(
            log_lines,
            data["references"],
            split_char,
            x,
            has_comparison=x == "wmops_per_op"
            and wmops_per_op_log_for_comparison is not None,
        )

        runs = ",\n".join(runs)
        displays = ",\n".join(displays)
        ticks = ",\n".join(ticks)

        js_string = JS_FILE_TEMPLATE.format(
            var_name=data["var_name"],
            elem_name=data["elem_name"],
            description=data["description"],
            runs=runs,
            displays=displays,
            ticks=ticks,
        )
        with open(data["filename"], "w") as f:
            print(js_string, file=f)


def pre_proc_log_lines(
    log_lines: list[str], wmops_per_op_log_for_comparison: Optional[str]
):
    if wmops_per_op_log_for_comparison is not None:
        with open(wmops_per_op_log_for_comparison) as f:
            log_lines_comp = f.readlines()

        log_lines_combined = []
        MISSING_LINE = "{};0;0;0"
        for line in log_lines:
            line_split = line.split(";")
            conf = line_split[0]

            # add - BASOP suffix to existing line
            new_line_split = line_split
            new_line_split[0] = conf + " - BASOP"
            new_line_basop = ";".join(new_line_split)
            log_lines_combined.append(new_line_basop)

            # search for same operating point in given comparison log
            ref_conf = conf + " - FLT REF"
            new_line_ref = MISSING_LINE.format(ref_conf)
            for line_comp in log_lines_comp:
                line_comp_split = line_comp.split(";")
                conf_comp = line_comp_split[0]
                if conf == conf_comp:
                    new_line_ref = ";".join([ref_conf] + line_comp_split[1:])
                    break
            log_lines_combined.append(new_line_ref)

        log_lines = log_lines_combined

    # some preprocessing is needed so that the later functions work as are
    # 1. need to make sure that modes are ordered by bandwidth
    # 2. need to add the bandwidth indicator as a column
    wb_lines, swb_lines, fb_lines = [], [], []
    for line in log_lines:
        if " WB " in line:
            wb_lines.append(line + ";WB")
        elif " SWB " in line:
            swb_lines.append(line + ";SWB")
        elif " FB " in line:
            fb_lines.append(line + ";FB")
    log_lines = wb_lines + swb_lines + fb_lines

    # generate tick positions and x axis labels from the number of lines
    in_between_offset_size = 1
    wb_label_pos = len(wb_lines) / 2
    swb_label_pos = len(wb_lines) + in_between_offset_size + len(swb_lines) / 2
    fb_label_pos = (
        len(wb_lines)
        + in_between_offset_size
        + len(swb_lines)
        + in_between_offset_size
        + len(fb_lines) / 2
    )

    ticks = [
        f"['{wb_label_pos}', 'WB']",
        f"['{swb_label_pos}', 'SWB']",
        f"['{fb_label_pos}', 'FB']",
    ]

    return log_lines, ticks


def create_runs_string(line: list[str], which: str) -> str:
    keys = RUNS_KEYS[which]

    vals = [line[i] for i in RUNS_LINE_IDX[which]]
    run = str(dict(zip(keys, vals)))

    return run


def create_display_strings(
    log_lines, references, split_char, which, has_comparison=False
):
    display_ids = DISPLAY_IDS[which]
    display_line_idx = DISPLAY_LINE_IDX[which]
    line_colors = LINE_COLORS[which]
    display_labels = DISPLAY_LABELS.get(which, [""] * len(display_ids))

    displays = list()
    for id, idx, color, label in zip(
        display_ids, display_line_idx, line_colors, display_labels
    ):
        data = list()
        for i, line in enumerate(log_lines):
            value = line.split(split_char)[idx]
            if id in references:
                value = references[id]
            data.append(f"[{i}, {value}]")

        if which == "wmops_per_op" and has_comparison:
            # de-interleave data
            data_basop = data[::2]
            data_flt_ref = data[1::2]

            display_basop = DISPLAY_ELEM_TEMPLATE.format(
                color=color, id=id + " - BASOP", data=", ".join(data_basop)
            )
            idx_data = display_basop.index("data:")
            label_string = f"label: '{label} - BASOP', "
            display_basop = (
                display_basop[:idx_data] + label_string + display_basop[idx_data:]
            )

            ref_color = REF_COLOR_FOR_COMP_BARS[label]

            display_flt_ref = DISPLAY_ELEM_TEMPLATE.format(
                color=ref_color, id=id + " - FLT REF", data=", ".join(data_flt_ref)
            )
            idx_data = display_flt_ref.index("data:")
            label_string = f"label: '{label} - FLT REF', "
            display_flt_ref = (
                display_flt_ref[:idx_data] + label_string + display_flt_ref[idx_data:]
            )

            display = display_basop + ", \n" + display_flt_ref
        else:
            display = DISPLAY_ELEM_TEMPLATE.format(
                color=color, id=id, data=", ".join(data)
            )

        if which == "wmops_per_op":
            display = display.replace("show: true", "show: false")

            if not has_comparison:
                idx_data = display.index("data:")
                label_string = f"label: '{label}', "
                display = display[:idx_data] + label_string + display[idx_data:]

        displays.append(display)

    return displays


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Generate Javascript code for the complexity webpage"
    )
    parser.add_argument("wmops_log")
    parser.add_argument("wmops_per_op_log")
    parser.add_argument("rom_log")
    parser.add_argument("ram_log")
    parser.add_argument("--wmops_per_op_log_for_comparison", default=None)

    args = parser.parse_args()

    main(
        args.wmops_log,
        args.wmops_per_op_log,
        args.rom_log,
        args.ram_log,
        args.wmops_per_op_log_for_comparison,
    )
+0 −504

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −535

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −500

File deleted.

Preview size limit exceeded, changes collapsed.

Loading