Index

lognestmonster / 64e29ab

A general-purpose single-header C logging library and parser for event-based logs. (Incomplete)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
20222 Jul 2020 20:27616a224Bring repository up to dateJosh Stockin187G

Blob @ lognestmonster / tests / performance.py

application/x-python4311 bytesdownload raw
1#!/usr/bin/env python3
2"""Compiles tests/header_only.c to test size, tests/header_unit*.c to test speed"""
3
4import sys
5import os
6import subprocess
7import re
8from statistics import median
9
10PROJECT_PATH = os.path.abspath(
11 os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
12)
13BIN_PATH = os.path.join(PROJECT_PATH, "bin")
14INCLUDE_PATH = os.path.join(PROJECT_PATH, "src/c")
15HEADER_ONLY = os.path.join(PROJECT_PATH, "tests/header_only.c")
16HEADER_PERFORMANCE = os.path.join(PROJECT_PATH, "tests/header_memory.c")
17
18CC = "gcc"
19CFLAGS = ["-std=c11", "-pedantic", "-Wall", "-Wextra", "-Werror"]
20COUTFLAG = "-o"
21COPTIMIZATIONS = ["-O0", "-O1", "-O2", "-O3", "-Os"]
22CINCLUDES = ["-I", INCLUDE_PATH]
23
24TABLE_HEADER_1 = "Optimization Level"
25TABLE_HEADER_2 = "Header Binary Size (in bytes)"
26TABLE_HEADER_3 = "Memory Test Runtime (in µs)"
27
28RE_TRIAL_TIME = r"time elapsed \(us\): (\d*)"
29TRIAL_PASSES = 200
30
31
32def cc_compile(out_file: str, in_files: list, optimization_level: str = "-O0"):
33 """Uses gcc subprocess to compile at a set optimization level"""
34 process_command = (
35 [CC]
36 + CFLAGS
37 + CINCLUDES
38 + [COUTFLAG, out_file]
39 + in_files
40 + [optimization_level]
41 )
42 process = subprocess.run(process_command, stdout=sys.stdout, stderr=sys.stderr)
43 if process.returncode:
44 print("[cc_compile] error in compilation")
45 return 0
46 else:
47 print(f"[cc_compile] finished compiling to {out_file}")
48 return 1
49
50
51def execute_file(executable: str):
52 """Executes the input file and returns the stdout"""
53 process = subprocess.run([executable], capture_output=True, text=True)
54 return process.stdout
55
56
57def get_size(file_name: str):
58 """Uses os.stat to get the file size in bytes of a specified file"""
59 return os.stat(file_name).st_size
60
61
62if __name__ == "__main__":
63 try:
64 os.mkdir(BIN_PATH)
65 except FileExistsError:
66 pass
67 else:
68 print("[main] created bin/")
69 print("[main] compiling...")
70 print(f"[main/compile] using compiler {CC}")
71 for optimization in COPTIMIZATIONS:
72 header_only_out = os.path.join(BIN_PATH, f"c-header-only{optimization}")
73 if not cc_compile(header_only_out, [HEADER_ONLY], optimization):
74 sys.exit()
75 header_unit_out = os.path.join(BIN_PATH, f"c-performance{optimization}")
76 if not cc_compile(header_unit_out, [HEADER_PERFORMANCE], optimization):
77 sys.exit()
78 print("[main] getting filesize data of header_only binaries")
79 EXECUTABLE_SIZES = {}
80 for optimization in COPTIMIZATIONS:
81 header_only_out = os.path.join(BIN_PATH, f"c-header-only{optimization}")
82 EXECUTABLE_SIZES[optimization] = get_size(header_only_out)
83 print("[main] beginning time trials")
84 EXECUTABLE_RUNTIMES = {}
85 import matplotlib.pyplot as plt
86 plt.xlabel("trial")
87 plt.ylabel("time (µs)")
88 for optimization in COPTIMIZATIONS:
89 print(f"[main/trials] running trial on {optimization}")
90 header_unit_out = os.path.join(BIN_PATH, f"c-performance{optimization}")
91 trial_runtimes = []
92 for trial_num in range(TRIAL_PASSES):
93 trial_output = execute_file(header_unit_out)
94 trial_time = re.search(RE_TRIAL_TIME, trial_output).group(1)
95 trial_runtimes.append(int(trial_time))
96 plt.plot(trial_runtimes)
97 trial_runtimes.sort()
98 trial_median = int(median(trial_runtimes))
99 trial_mid = int(TRIAL_PASSES/2)
100 trial_iqr = int(median(trial_runtimes[trial_mid:TRIAL_PASSES]) - median(trial_runtimes[0:trial_mid]))
101 trial_moe = int(trial_iqr/2)
102 EXECUTABLE_RUNTIMES[optimization] = f"{trial_median} ± {trial_moe}"
103 plt.legend(COPTIMIZATIONS)
104 print("[main] finished trials:")
105 print(f"{TABLE_HEADER_1} | {TABLE_HEADER_2} | {TABLE_HEADER_3}")
106 print(
107 f"{len(TABLE_HEADER_1)*'-'} | {len(TABLE_HEADER_2)*'-'} | {len(TABLE_HEADER_3)*'-'}"
108 )
109 for optimization in COPTIMIZATIONS:
110 executable_size = EXECUTABLE_SIZES[optimization]
111 executable_runtime = EXECUTABLE_RUNTIMES[optimization]
112 print(
113 f"{optimization.ljust(len(TABLE_HEADER_1))} | "
114 + f"{str(executable_size).ljust(len(TABLE_HEADER_2))} | {executable_runtime}"
115 )
116 plt.show()
117