1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3#
4# Copyright (C) xFusion Digital Technologies Co., Ltd., 2023
5#
6# Author: Wang Jinchao <wangjinchao@xfusion.com>
7#
8"""
9A tool for comparing tcrypt speed test logs.
10
11Please note that for such a comparison, stability depends
12on whether we allow frequency to float or pin the frequency.
13
14Both support tests for operations within one second and
15cycles of operation.
16For example, use it in the bash script below.
17
18```bash
19#!/bin/bash
20
21# log file prefix
22seq_num=0
23
24# When sec=0, it will perform cycle tests;
25# otherwise, it indicates the duration of a single test
26sec=0
27num_mb=8
28mode=211
29
30# base speed test
31lsmod | grep pcrypt && modprobe -r pcrypt
32dmesg -C
33modprobe tcrypt alg="pcrypt(rfc4106(gcm(aes)))" type=3
34modprobe tcrypt mode=${mode} sec=${sec} num_mb=${num_mb}
35dmesg > ${seq_num}_base_dmesg.log
36
37# new speed test
38lsmod | grep pcrypt && modprobe -r pcrypt
39dmesg -C
40modprobe tcrypt alg="pcrypt(rfc4106(gcm(aes)))" type=3
41modprobe tcrypt mode=${mode} sec=${sec} num_mb=${num_mb}
42dmesg > ${seq_num}_new_dmesg.log
43lsmod | grep pcrypt && modprobe -r pcrypt
44
45tools/crypto/tcrypt/tcrypt_speed_compare.py \
46    ${seq_num}_base_dmesg.log \
47    ${seq_num}_new_dmesg.log  \
48        >${seq_num}_compare.log
49grep 'average' -A2 -B0 --group-separator="" ${seq_num}_compare.log
50```
51"""
52
53import sys
54import re
55
56
57def parse_title(line):
58    pattern = r'tcrypt: testing speed of (.*?) (encryption|decryption)'
59    match = re.search(pattern, line)
60    if match:
61        alg = match.group(1)
62        op = match.group(2)
63        return alg, op
64    else:
65        return "", ""
66
67
68def parse_item(line):
69    pattern_operations = r'\((\d+) bit key, (\d+) byte blocks\): (\d+) operations'
70    pattern_cycles = r'\((\d+) bit key, (\d+) byte blocks\): 1 operation in (\d+) cycles'
71    match = re.search(pattern_operations, line)
72    if match:
73        res = {
74            "bit_key": int(match.group(1)),
75            "byte_blocks": int(match.group(2)),
76            "operations": int(match.group(3)),
77        }
78        return res
79
80    match = re.search(pattern_cycles, line)
81    if match:
82        res = {
83            "bit_key": int(match.group(1)),
84            "byte_blocks": int(match.group(2)),
85            "cycles": int(match.group(3)),
86        }
87        return res
88
89    return None
90
91
92def parse(filepath):
93    result = {}
94    alg, op = "", ""
95    with open(filepath, 'r') as file:
96        for line in file:
97            if not line:
98                continue
99            _alg, _op = parse_title(line)
100            if _alg:
101                alg, op = _alg, _op
102                if alg not in result:
103                    result[alg] = {}
104                if op not in result[alg]:
105                    result[alg][op] = []
106                continue
107            parsed_result = parse_item(line)
108            if parsed_result:
109                result[alg][op].append(parsed_result)
110    return result
111
112
113def merge(base, new):
114    merged = {}
115    for alg in base.keys():
116        merged[alg] = {}
117        for op in base[alg].keys():
118            if op not in merged[alg]:
119                merged[alg][op] = []
120            for index in range(len(base[alg][op])):
121                merged_item = {
122                    "bit_key": base[alg][op][index]["bit_key"],
123                    "byte_blocks": base[alg][op][index]["byte_blocks"],
124                }
125                if "operations" in base[alg][op][index].keys():
126                    merged_item["base_ops"] = base[alg][op][index]["operations"]
127                    merged_item["new_ops"] = new[alg][op][index]["operations"]
128                else:
129                    merged_item["base_cycles"] = base[alg][op][index]["cycles"]
130                    merged_item["new_cycles"] = new[alg][op][index]["cycles"]
131
132                merged[alg][op].append(merged_item)
133    return merged
134
135
136def format(merged):
137    for alg in merged.keys():
138        for op in merged[alg].keys():
139            base_sum = 0
140            new_sum = 0
141            differ_sum = 0
142            differ_cnt = 0
143            print()
144            hlen = 80
145            print("="*hlen)
146            print(f"{alg}")
147            print(f"{' '*(len(alg)//3) + op}")
148            print("-"*hlen)
149            key = ""
150            if "base_ops" in merged[alg][op][0]:
151                key = "ops"
152                print(f"bit key | byte blocks | base ops    | new ops     | differ(%)")
153            else:
154                key = "cycles"
155                print(f"bit key | byte blocks | base cycles | new cycles  | differ(%)")
156            for index in range(len(merged[alg][op])):
157                item = merged[alg][op][index]
158                base_cnt = item[f"base_{key}"]
159                new_cnt = item[f"new_{key}"]
160                base_sum += base_cnt
161                new_sum += new_cnt
162                differ = round((new_cnt - base_cnt)*100/base_cnt, 2)
163                differ_sum += differ
164                differ_cnt += 1
165                bit_key = item["bit_key"]
166                byte_blocks = item["byte_blocks"]
167                print(
168                    f"{bit_key:<7} | {byte_blocks:<11} | {base_cnt:<11} | {new_cnt:<11} | {differ:<8}")
169            average_speed_up = "{:.2f}".format(differ_sum/differ_cnt)
170            ops_total_speed_up = "{:.2f}".format(
171                (base_sum - new_sum) * 100 / base_sum)
172            print('-'*hlen)
173            print(f"average differ(%s)    | total_differ(%)")
174            print('-'*hlen)
175            print(f"{average_speed_up:<21} | {ops_total_speed_up:<10}")
176            print('='*hlen)
177
178
179def main(base_log, new_log):
180    base = parse(base_log)
181    new = parse(new_log)
182    merged = merge(base, new)
183    format(merged)
184
185
186if __name__ == "__main__":
187    if len(sys.argv) != 3:
188        print(f"usage: {sys.argv[0]} base_log new_log")
189        exit(-1)
190    main(sys.argv[1], sys.argv[2])
191