소스 검색

Add basic expr 8

Youngbin Kim 4 달 전
부모
커밋
a22ac8ed0a

+ 35 - 0
imc/exprs/tvlsi2025/8_size_tradeoff/draw_graph_expr_8.ipynb

@@ -0,0 +1,35 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b9399988",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import sys\n",
+    "from importlib import reload\n",
+    "\n",
+    "import draw_graph_expr_8"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "69240b1b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "reload(draw_graph_expr_8)\n",
+    "fig = draw_graph_expr_8.draw_graph()"
+   ]
+  }
+ ],
+ "metadata": {
+  "language_info": {
+   "name": "python"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}

+ 93 - 0
imc/exprs/tvlsi2025/8_size_tradeoff/draw_graph_expr_8.py

@@ -0,0 +1,93 @@
+import pickle
+import os
+import pandas as pd
+import seaborn as sns
+import matplotlib.pyplot as plt
+import matplotlib
+
+import plot_utils
+
+def draw_graph():
+    benchmarks = [
+        "vBasicMath",
+        "vCrc",
+        "vFFT",
+        "vSha",
+        "vStringSearch",
+        "vMatMul",
+        "vConv2d",
+        "vAes",
+    ]
+    duplication_ratios = list(range(10, 101, 10))
+    config_name = "adaptive"
+
+    all_dfs = []
+
+    for benchmark in benchmarks:
+        for duplication_ratio in duplication_ratios:
+            try:
+                df = pd.read_pickle(f"output/{benchmark}/duplication_ratio_{duplication_ratio}.pickle")
+                all_dfs.append(df)
+            except FileNotFoundError:
+                continue
+    
+    all_df = pd.concat(all_dfs, ignore_index=True)
+    print(all_df)
+
+    rc = {
+        "lines.linewidth": 1.5,
+        "axes.titlepad": 10,
+        "ytick.major.pad": -4,
+        "ytick.labelsize": 20,
+        "legend.fontsize": 23,
+        "axes.labelsize": 25,
+    }
+    plot_utils.set_theme_seaborn(kind="bar", rc_custom=rc)
+
+    fig_size = (18, 6)
+    n_rows = 2
+    n_cols = 4
+    hspace = 0.05
+    fig = plt.figure(figsize=fig_size)
+    axes = fig.subplots(n_rows, n_cols, sharex=True, gridspec_kw={"hspace": hspace})
+
+    for i, benchmark in enumerate(benchmarks):
+        ax = axes.reshape(-1)[i]
+        mask = (all_df["benchmark"] == benchmark)
+        df = all_df[mask]
+        if df.empty:
+            continue
+        sns.barplot(data=df, x="duplication_ratio", y="func", ax=ax)
+        ax.set_title(benchmark)
+        ax.set_xlabel("Duplication Ratio (%)")
+        ax.set_ylabel("Func Size (bytes)")
+        ax.grid(True)
+
+        ax.set_title(benchmark)
+        ax.set_xlabel("")
+        ax.set_ylabel("")
+        if i / 4 >= 1:
+            ax.set_xlabel("Execution #")
+        if i in [0, 4]:
+            ax.set_ylabel("Func Size (kB)")
+
+        ax.yaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(lambda x, pos: f"{x/1000:.1f}"))
+        ax.yaxis.grid(visible=True, which="both")
+        # ax.yaxis.set_major_locator(matplotlib.ticker.MaxNLocator(3))
+        ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator(3))
+
+        ax.xaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator(2))
+        ax.xaxis.grid(visible=True, which="both")
+
+
+        if i == 0:
+            ax.legend(
+                ncol=1, loc="upper left", bbox_to_anchor=(0.33, 0.995), labelspacing=0.3,
+                handlelength=1,
+            )
+            # ax.get_legend().remove()
+        else:
+            if ax.get_legend() is not None:
+                ax.get_legend().remove()
+    
+    return fig

+ 198 - 0
imc/exprs/tvlsi2025/8_size_tradeoff/run_expr_8.py

@@ -0,0 +1,198 @@
+import tempfile
+import time
+import pickle
+import pandas as pd
+import os
+import subprocess
+import pprint
+
+from imc_utils.pps_e36311a import PPS_E36311A
+from imc_utils.build_config.cortex_m33 import BuildConfigM33
+from imc_utils.build_config.test_env import TestEnv
+from imc_utils.serial_watch import SerialWatcher
+
+WORKSPACE_ROOT = "/home/ybkim/workspace/imc/imc_freertos_app_m33"
+NVM_RESET_BIN = f"{WORKSPACE_ROOT}/imc/utils/nvm_reset.elf"
+OPENOCD_SCRIPT = f"{WORKSPACE_ROOT}/imc_freertos_app_m33.cfg"
+
+
+def get_build_config(benchmark, config_name):
+    config = get_default_build_config()
+    config.bench_name = benchmark
+    bench_repeat_count = config.bench_repeat_count_small[benchmark]
+    config.bench_repeat_count = bench_repeat_count
+
+    if config_name == "original":
+        config.insert_compiler_checkpoints = False
+
+    if config_name == "pass_count":
+        config.use_checkpoint_pass_counter = True
+        config.checkpoint_pass_count = config.pass_count_10ms[benchmark]
+
+    if config_name == "adaptive":
+        config.use_checkpoint_voltage_check = True
+        config.split_loop = True
+        config.enable_adaptive_loop_pass_count = True
+        config.max_loop_ids = 30
+    
+    if config_name == "unroll":
+        config.custom_unroll = True
+
+    return config
+
+
+def get_flash_size(binary):
+    flash_sections = [
+        ".isr_vector",
+        ".text",
+        ".rodata",
+        ".ARM.extab",
+        ".ARM",
+        ".preinit_array",
+        ".init_array",
+        ".fini_array",
+        ".data"
+    ]
+    total = 0
+    text = 0
+    inst = f"objdump -h {binary}".split()
+    output = subprocess.run(inst, capture_output=True)
+    lines = output.stdout.decode().split("\n")
+    for line in lines:
+        tokens = line.strip().split()
+        if len(tokens) < 1:
+            continue
+        if tokens[0].isdigit():
+            section, size_in_hex = tokens[1], tokens[2]
+            size = int(size_in_hex, base=16)
+            if section in flash_sections:
+                print(section, size_in_hex, size)
+                total += size
+                if section == ".text":
+                    text = size
+    return total, text
+
+
+def get_func_size(binary, bench_name):
+    function_names = {
+        "vBasicMath": ["vBasicMath"],
+        "vCrc": ["updateCRC32", "crc32file", "crc32buf", "vCrc"],
+        "vFFT": ["vFFT"],
+        "vSha": [
+            "sha_init",
+            "sha_update",
+            "byte_reverse",
+            "sha_transform",
+            "sha_final",
+            "sha_stream",
+            "sha_print",
+            "vSha",
+        ],
+        "vStringSearch": ["vStringSearch"],
+        "vMatMul": ["vMatMul"],
+        "vConv2d": ["vConv2d"],
+        "vAes": [
+            "AES_init_ctx_iv",
+            "KeyExpansion",
+            "AES_CBC_encrypt_buffer",
+            "XorWithIv",
+            "Cipher",
+            "vAes",
+            "AddRoundKey",
+            "SubBytes",
+            "ShiftRows",
+            "MixColumns",
+            "xtime",
+        ],
+    }
+    funcs = function_names[bench_name]
+    inst = f"nm -S -t d {binary}".split()
+    output = subprocess.run(inst, capture_output=True)
+    lines = output.stdout.decode().split("\n")
+    total_size = 0
+    for line in lines:
+        if len(line) == 0:
+            continue
+        tokens = line.strip().split()
+        if tokens[-1] in funcs:
+            size = int(tokens[1])
+            total_size += size
+    return total_size
+
+
+def main():
+    config = get_default_build_config()
+
+    benchmarks = [
+        "vBasicMath",
+        "vCrc",
+        "vFFT",
+        "vSha",
+        "vStringSearch",
+        "vMatMul",
+        "vConv2d",
+        "vAes",
+    ]
+    # benchmarks = [
+    #     "vFFT"
+    # ]
+
+    duplication_ratios = list(range(10, 101, 10))
+
+    for benchmark in benchmarks:
+        for duplication_ratio in duplication_ratios:
+            all_records = []
+            config_name = "adaptive"
+            config = get_build_config(benchmark, config_name)
+            config.duplication_ratio = duplication_ratio
+
+            env = TestEnv(WORKSPACE_ROOT, NVM_RESET_BIN, OPENOCD_SCRIPT)
+
+            with tempfile.TemporaryDirectory() as build_dir:
+                binary = env.build_binary(config, build_dir)
+                size, text_size = get_flash_size(binary)
+                func_size = get_func_size(binary, benchmark)
+            
+            record = {
+                "benchmark": benchmark,
+                # "config": config_name,
+                "duplication_ratio": duplication_ratio,
+                "flash": size,
+                "text": text_size,
+                "func": func_size,
+            }
+
+            all_records.append(record)
+            df = pd.DataFrame(all_records)
+            print(df)
+            save_records(benchmark, duplication_ratio, df)
+
+
+def get_default_build_config():
+    config = BuildConfigM33()
+    config.insert_compiler_checkpoints = True
+    config.enable_extension = True
+    config.use_checkpoint_pass_counter = False
+    config.use_checkpoint_voltage_check = False
+    config.bench_infinite_loop = True
+    config.print_recovery_message = True
+    config.split_loop = False
+    config.enable_static_loop_pass_count = False
+    config.enable_adaptive_loop_pass_count = False
+    config.print_stats = True
+    config.custom_unroll = False
+    return config
+
+
+def save_records(bench_name, duplication_ratio, df):
+    if not os.path.exists("output"):
+        os.makedirs("output")
+    if not os.path.exists(f"output/{bench_name}"):
+        os.makedirs(f"output/{bench_name}")
+    with open(f"output/{bench_name}/duplication_ratio_{duplication_ratio}.pickle", "wb") as f:
+        pickle.dump(df, f)
+
+
+if __name__ == "__main__":
+    main()
+