Signed-off-by: Zeng Heng <zengheng4(a)huawei.com>
---
 bw_dynamic.py     | 184 ++++++++++++++++++++++++++++++++++++++++++++++
 calc_cpu_usage.py |  27 +++++++
 draw_bw.py        | 105 ++++++++++++++++++++++++++
 draw_cpu.py       |  34 +++++++++
 draw_llc.py       |  66 +++++++++++++++++
 pidcontroller.py  |  35 +++++++++
 6 files changed, 451 insertions(+)
 create mode 100644 bw_dynamic.py
 create mode 100644 calc_cpu_usage.py
 create mode 100644 draw_bw.py
 create mode 100644 draw_cpu.py
 create mode 100644 draw_llc.py
 create mode 100644 pidcontroller.py
diff --git a/bw_dynamic.py b/bw_dynamic.py
new file mode 100644
index 000000000000..51509928aef0
--- /dev/null
+++ b/bw_dynamic.py
@@ -0,0 +1,184 @@
+import os, time, signal, json, sys
+from pidcontroller import PIDController
+from calc_cpu_usage import read_cpu_times, calculate_cpu_utilization
+
+numa_bw_limit = 80000
+lc_grp = "."
+be_grp = "p1"
+
+bw_list = []
+llc_list = []
+set_list = []
+cpu_list = []
+
+def read_bw(grp):
+    resctrl_mon_data_dir = '/sys/fs/resctrl/%s/mon_data/' % grp
+    mon_data_dirs = os.listdir(resctrl_mon_data_dir)
+    mon_MB_dirs = [dir for dir in mon_data_dirs if dir.startswith('mon_MB_')]
+
+    vals = []
+    for mb_dir in mon_MB_dirs:
+        with open(resctrl_mon_data_dir + mb_dir + '/mbm_total_bytes', 'r') as f:
+            line = f.readline()
+        vals.append(int(line.strip()))
+
+    return vals
+
+def increase_bw(i, percent):
+    print("increase NUMA%d %d%%" % (i, percent))
+
+    resctrl_par_dir = '/sys/fs/resctrl/%s/schemata' % be_grp
+    with open(resctrl_par_dir, 'r') as f:
+        lines = f.readlines()
+
+    for line in lines:
+        if "MB:" in line:
+            config = line.split(":")
+            config = config[1].split(";")
+            origin_p = int(config[i].split("=")[1])
+
+    new_p = (origin_p + percent)
+    if new_p > 100:
+        new_p = 100
+    elif new_p < 8:
+        new_p = 8
+
+    # if percent < 0:
+    #     cnt = "MBHDL:%d=1\n" % i
+    #     with open(resctrl_par_dir, 'w+') as f:
+    #         f.write(cnt)
+
+    cnt = "MB:%d=%d" % (i, new_p)
+    print("%s<=%d" % (cnt, origin_p))
+    if new_p == origin_p:
+        return origin_p
+
+    with open(resctrl_par_dir, 'w+') as f:
+        f.write("%s\n" % cnt)
+
+    return origin_p
+
+def gain_numa_bw(pid_ctl, adjust_enable, target_percent):
+    lc_val = read_bw(lc_grp)
+    be_val = read_bw(be_grp)
+
+    numa_bw_state = []
+    numa_set = []
+
+    for i in range(len(lc_val)):
+        bw_state = {"total_bw": {}, "lc_bw": {}, "be_bw": {}}
+
+        bw_sum = lc_val[i] + be_val[i]
+
+        bw_state["total_bw"] = bw_sum
+        bw_state["lc_bw"] = lc_val[i]
+        bw_state["be_bw"] = be_val[i]
+        numa_bw_state.append(bw_state)
+
+        set_state = {"setting": {}, "delta": {}}
+
+        if adjust_enable == True:
+            if bw_state["lc_bw"] < (0.04 * numa_bw_limit):
+                # enlarge BE load
+                diff = 5
+            elif bw_state["lc_bw"] > (target_percent / 100  * numa_bw_limit):
+                # shutdown BE load
+                diff = -100
+            else:
+                diff = pid_ctl[i].update(bw_sum * 100 // numa_bw_limit , 1)
+        else:
+            diff = 0
+
+        curr = increase_bw(i, diff)
+        set_state['setting'] = curr
+        set_state['delta'] = diff
+
+        numa_set.append(set_state)
+
+    bw_list.append(numa_bw_state)
+    set_list.append(numa_set)
+    return
+
+def read_llc(grp):
+    resctrl_mon_data_dir = '/sys/fs/resctrl/%s/mon_data/' % grp
+    mon_data_dirs = os.listdir(resctrl_mon_data_dir)
+    mon_MB_dirs = [dir for dir in mon_data_dirs if dir.startswith('mon_L3_')]
+
+    vals = []
+    for mb_dir in mon_MB_dirs:
+        with open(resctrl_mon_data_dir + mb_dir + '/llc_occupancy', 'r') as f:
+            line = f.readline()
+        vals.append(int(line.strip()))
+
+    return vals
+
+def gain_numa_llc():
+    lc_llc = read_llc(lc_grp)
+    be_llc = read_llc(be_grp)
+
+    numa_llc_state = []
+
+    for i in range(len(lc_llc)):
+        llc_state = {"total_llc": {}, "lc_llc": {}, "be_llc": {}}
+
+        total_llc = lc_llc[i] + be_llc[i]
+        llc_state["total_llc"] = total_llc
+
+        llc_state["lc_llc"] = lc_llc[i]
+        llc_state["be_llc"] = be_llc[i]
+        numa_llc_state.append(llc_state)
+
+    llc_list.append(numa_llc_state)
+    return
+
+def gain_cpu(time1, time2):
+    usage = calculate_cpu_utilization(time1, time2)
+    print(f"CPU usage: {usage:.2f}%")
+    cpu_list.append(usage)
+    return
+
+def save_file(sig, frame):
+    with open(f"ctrlgrp_bw.data", 'w') as fl:
+        json.dump(bw_list, fl)
+
+    with open(f"schemata_set.data", 'w') as fl:
+        json.dump(set_list, fl)
+
+    with open(f"ctrlgrp_llc.data", 'w') as fl:
+        json.dump(llc_list, fl)
+
+    with open(f"cpu_usage.data", 'w') as fl:
+        json.dump(cpu_list, fl)
+
+    exit(0)
+
+if __name__ == "__main__":
+    signal.signal(signal.SIGINT, save_file)
+
+    enable = True
+    target_percent = 60
+
+    num_args = len(sys.argv) - 1
+    if num_args == 1 and sys.argv[1].isdigit():
+        target_percent = int(sys.argv[1])
+    else:
+        enable = False
+
+    pid_ctl = []
+    pid_ctl.append(PIDController(kp=1.0, ki=0.02, kd=0.05, set_point=target_percent))
+    pid_ctl.append(PIDController(kp=1.0, ki=0.02, kd=0.05, set_point=target_percent))
+    pid_ctl.append(PIDController(kp=1.0, ki=0.02, kd=0.05, set_point=target_percent))
+    pid_ctl.append(PIDController(kp=1.0, ki=0.02, kd=0.05, set_point=target_percent))
+
+    time1 = read_cpu_times()
+
+    while True:
+        gain_numa_bw(pid_ctl, enable, target_percent)
+        gain_numa_llc()
+
+        time2 = read_cpu_times()
+        gain_cpu(time1, time2)
+        time1 = time2
+
+        print("..........................")
+        time.sleep(1)
diff --git a/calc_cpu_usage.py b/calc_cpu_usage.py
new file mode 100644
index 000000000000..98fef7a2c1f5
--- /dev/null
+++ b/calc_cpu_usage.py
@@ -0,0 +1,27 @@
+import time
+
+def read_cpu_times():
+    with open('/proc/stat', 'r') as f:
+        line = f.readline()
+    parts = line.split()
+    return list(map(int, parts[1:]))
+
+def calculate_cpu_utilization(times1, times2):
+    total1 = sum(times1)
+    idle1 = times1[3]
+    total2 = sum(times2)
+    idle2 = times2[3]
+    total_diff = total2 - total1
+    idle_diff = idle2 - idle1
+    return (total_diff - idle_diff) / total_diff * 100
+
+# def gain_cpu(time1, time2):
+#     usage = calculate_cpu_utilization(time1, time2)
+#     print(f"CPU usage: {usage:.2f}%")
+#     return time2
+
+# time1 = read_cpu_times()
+# while True:
+#     time.sleep(1)
+#     time2 = read_cpu_times()
+#     time1 = gain_cpu(time1, time2)
diff --git a/draw_bw.py b/draw_bw.py
new file mode 100644
index 000000000000..17f28842cbcc
--- /dev/null
+++ b/draw_bw.py
@@ -0,0 +1,105 @@
+import json
+import matplotlib.pyplot as plt
+
+target_percent = 60 / 100
+bw_limit = target_percent * 80000
+
+def get_all_bw(data_list, idx):
+    total = []
+    lc = []
+    be = []
+
+    for data in data_list:
+        total.append(data[idx]['total_bw'])
+        lc.append(data[idx]['lc_bw'])
+        be.append(data[idx]['be_bw'])
+
+    return total, lc, be
+
+def get_all_set(set_list, idx):
+    setting = []
+    delta = []
+
+    for data in set_list:
+        setting.append(data[idx]['setting'])
+        delta.append(data[idx]['delta'])
+
+    return setting, delta
+
+def draw_data(bw_list, set_list, cpu_list):
+    fig = plt.figure()
+    x_arr = list(range(len(bw_list)))
+
+    # numa_cnt = len(bw_list[0])
+    # only draw numa1
+    numa_cnt_display = 1
+    numa_th = 1
+    for cnt in range(numa_cnt_display):
+        ax1 = fig.add_subplot(numa_cnt_display * 2 + 1, 1, cnt * 2 + 1)
+        # total, lc, be = get_all_bw(bw_list, cnt)
+        total, lc, be = get_all_bw(bw_list, numa_th)
+        ref = [bw_limit] * len(bw_list)
+
+        ax1.plot(x_arr, total, label='total bw')
+        ax1.plot(x_arr, lc, label='lc bw')
+        ax1.plot(x_arr, be, label='be bw')
+        ax1.plot(x_arr, ref, label='reference')
+        ax1.legend()
+
+        ax2 = fig.add_subplot(numa_cnt_display * 2 + 1, 1, cnt * 2 + 2)
+        # setting, delta = get_all_set(set_list, cnt)
+        setting, delta = get_all_set(set_list, numa_th)
+
+        ref = [0] * len(bw_list)
+        ax2.plot(x_arr, setting, label='setting')
+        ax2.plot(x_arr, delta, label='delta')
+        ax2.plot(x_arr, ref, label='reference')
+        ax2.legend()
+
+        # ax3 = fig.add_subplot(5, 1, 3)
+        # total, lc, be = get_all_bw(bw_list, 1)
+
+        # ax3.plot(x_arr, total, label='total bw')
+        # ax3.plot(x_arr, lc, label='lc bw')
+        # ax3.plot(x_arr, be, label='be bw')
+        # ax3.plot(x_arr, ref, label='reference')
+        # ax3.legend()
+
+        # ax4 = fig.add_subplot(5, 1, 4)
+        # setting, delta = get_all_set(set_list, 1)
+
+        # ax4.plot(x_arr, setting, label='setting')
+        # ax4.plot(x_arr, delta, label='delta')
+        # ax4.legend()
+
+    ax5 = fig.add_subplot(numa_cnt_display * 2 + 1, 1, numa_cnt_display * 2 + 1)
+
+    ax5.plot(x_arr, cpu_list, label='CPU usage')
+    ax5.legend()
+
+    plt.tight_layout()
+    plt.show()
+
+    return
+
+def get_data(file):
+    try:
+        with open(file, 'r') as f:
+            data_list = json.load(f)
+    except FileNotFoundError:
+        return None
+
+    return data_list
+
+if __name__ == '__main__':
+    filename = 'ctrlgrp_bw.data'
+    bw_list = get_data(filename)
+    filename = 'schemata_set.data'
+    set_list = get_data(filename)
+    filename = 'cpu_usage.data'
+    cpu_list = get_data(filename)
+
+    if bw_list and set_list:
+        draw_data(bw_list, set_list, cpu_list)
+    else:
+        print('FileNotFound')
diff --git a/draw_cpu.py b/draw_cpu.py
new file mode 100644
index 000000000000..02e5b0e48376
--- /dev/null
+++ b/draw_cpu.py
@@ -0,0 +1,34 @@
+import json
+import matplotlib.pyplot as plt
+
+def draw_data(cpu_list):
+    fig = plt.figure()
+    x_arr = list(range(len(cpu_list)))
+
+    ax1 = fig.add_subplot(1, 1, 1)
+
+    ax1.plot(x_arr, cpu_list, label='CPU usage')
+    ax1.legend()
+
+    plt.tight_layout()
+    plt.show()
+
+    return
+
+def get_data(file):
+    try:
+        with open(file, 'r') as f:
+            cpu_list = json.load(f)
+    except FileNotFoundError:
+        return None
+
+    return cpu_list
+
+if __name__ == '__main__':
+    filename = 'cpu_usage.data'
+    cpu_list = get_data(filename)
+
+    if cpu_list:
+        draw_data(cpu_list)
+    else:
+        print('FileNotFound')
diff --git a/draw_llc.py b/draw_llc.py
new file mode 100644
index 000000000000..7538eac3925d
--- /dev/null
+++ b/draw_llc.py
@@ -0,0 +1,66 @@
+import json
+import matplotlib.pyplot as plt
+
+def get_all_llc(data_list, idx):
+    total = []
+    lc = []
+    be = []
+
+    for data in data_list:
+        total.append(data[idx]['total_llc'] / 1000)
+        lc.append(data[idx]['lc_llc'] / 1000)
+        be.append(data[idx]['be_llc'] / 1000)
+
+    return total, lc, be
+
+def draw_data(llc_list, cpu_list):
+    fig = plt.figure()
+    x_arr = list(range(len(llc_list)))
+
+    # only draw numa1
+    numa_cnt_display = 1
+    numa_th = 0
+    for cnt in range(numa_cnt_display):
+        ax1 = fig.add_subplot(numa_cnt_display + 1, 1, cnt + 1)
+        total, lc, be = get_all_llc(llc_list, numa_th)
+
+        ax1.plot(x_arr, total, label='total llc')
+        ax1.plot(x_arr, lc, label='lc llc')
+        ax1.plot(x_arr, be, label='be llc')
+
+        ax1.set_ylabel(f"L3 Cache (kB)")
+        ax1.set_xlabel(f"Time (s)")
+        ax1.legend()
+
+    ax2 = fig.add_subplot(numa_cnt_display + 1, 1, numa_cnt_display + 1)
+    ax2.plot(x_arr, cpu_list, label='CPU usage')
+
+    ax2.set_ylabel(f"CPU Load (%)")
+    ax2.set_xlabel(f"Time (s)")
+
+    ax2.legend()
+
+    plt.tight_layout()
+    plt.show()
+
+    return
+
+def get_data(file):
+    try:
+        with open(file, 'r') as f:
+            data_list = json.load(f)
+    except FileNotFoundError:
+        return None
+
+    return data_list
+
+if __name__ == '__main__':
+    filename = 'ctrlgrp_llc.data'
+    llc_list = get_data(filename)
+    filename = 'cpu_usage.data'
+    cpu_list = get_data(filename)
+
+    if llc_list:
+        draw_data(llc_list, cpu_list)
+    else:
+        print('FileNotFound')
diff --git a/pidcontroller.py b/pidcontroller.py
new file mode 100644
index 000000000000..4d03d2f28aa1
--- /dev/null
+++ b/pidcontroller.py
@@ -0,0 +1,35 @@
+class PIDController:
+    def __init__(self, kp, ki, kd, set_point):
+        self.kp = kp  # 比例增益
+        self.ki = ki  # 积分增益
+        self.kd = kd  # 微分增益
+        self.set_point = set_point  # 设定值
+        self.last_error = 0  # 上一次的误差
+        self.integral = 0  # 积分项
+
+        self.max_output = 5        # 缓升
+        self.min_output = -100     # 快降
+
+    def update(self, current_value, dt):
+        """
+        更新PID控制器的输出
+        :param current_value: 当前系统的输出值
+        :param dt: 时间间隔
+        :return: 控制器的输出
+        """
+        error = self.set_point - current_value  # 计算当前误差
+        self.integral += error * dt  # 更新积分项
+        derivative = (error - self.last_error) / dt  # 计算微分项
+
+        # 计算PID控制器的输出
+        output = (self.kp * error) + (self.ki * self.integral) + (self.kd * derivative)
+
+        self.last_error = error  # 更新上一次的误差
+
+        # 限制输出范围
+        if output > self.max_output:
+            output = self.max_output
+        elif output < self.min_output:
+            output = self.min_output
+
+        return output
--
2.25.1