aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeandro Pereira <leandro@hardinfo.org>2020-05-03 18:14:47 -0700
committerLeandro Pereira <leandro@hardinfo.org>2020-05-03 18:14:47 -0700
commitae0fac0baa8d9eee2925fd25d5ed32b6f26da6f1 (patch)
tree3f8495e68b5b7c4c5961f1d825a94999f5799b56
parentd06f0b0c4a227eb4235e341a6ab1b1466d1a09b1 (diff)
Add script to convert benchmark.conf to benchmark.json
The new server will send and retrieve information in JSON, as that was what was easier to do. Having a more verbose format makes things a whole lot easier to work with, too, so there's this bonus. This script replicates some of the work that HardInfo does to adapt the various versions of benchmark.conf, including cleaning up the CPU names and whatnot. (Might be a good idea to store the raw values in the database too just in case.) Results seem sane from a first glance, but this will need some testing. The idea is to use this script once, use it to rewrite the benchmark result loader, and then populate the server with this information. After that, this script can be thrown away.
-rwxr-xr-xtools/benchmark-conf-to-json.py252
1 files changed, 252 insertions, 0 deletions
diff --git a/tools/benchmark-conf-to-json.py b/tools/benchmark-conf-to-json.py
new file mode 100755
index 00000000..928797a1
--- /dev/null
+++ b/tools/benchmark-conf-to-json.py
@@ -0,0 +1,252 @@
+#!/usr/bin/python
+# Script to convert benchmark.conf to benchmark.json
+# Hopefully we won't have to maintain benchmark.conf files any longer after
+# we fully switch to JSON.
+
+from configparser import ConfigParser as config_parser
+from collections import defaultdict
+import json
+import sys
+import re
+
+def guess_old_style_used_threads(bench_name, num_threads):
+ if bench_name == 'CPU Fibonacci':
+ return 1
+ if bench_name == 'CPU FFT':
+ if num_threads >= 4:
+ return 4
+ if num_threads >= 2:
+ return 2
+ return 1
+ if bench_name == 'CPU N-Queens':
+ if num_threads >= 10:
+ return 10
+ if num_threads >= 5:
+ return 5
+ if num_threads >= 2:
+ return 2
+ return 1
+ return num_threads
+
+def first_truthy_or_none(*args):
+ for arg in args:
+ if arg:
+ return arg
+ return None
+
+def nice_cpu_name(name,
+ trademark_stuff = re.compile(r'\((r|tm|c)\)', re.I),
+ vendor_intel = re.compile(r'Intel '),
+ vendor_amd = re.compile(r'AMD '),
+ integrated_processor = re.compile(r'(Integrated Processor|Processor)', re.I),
+ frequency = re.compile(r'(\d+\.?\d+?\+?\s*[GH]z)')):
+ # Cleaned-up port of nice_name_x86_cpuid_model_string() from
+ # deps/sysobj_early/src/nice_name.c
+
+ name = name.strip()
+
+ name = name.replace("@", "")
+ name = trademark_stuff.sub("", name)
+
+ # Move vendor to front
+ match = first_truthy_or_none(vendor_intel.search(name), vendor_amd.search(name))
+ if match:
+ span = match.span()
+ name = name[span[0]:span[1]] + name[0:span[0]] + name[span[1]:]
+
+ # Remove vendor-specific crud
+ if name.startswith("AMD"):
+ name = name.replace("Mobile Technology", "Mobile")
+ name = name.replace("with Radeon", "")
+ name = name.replace("Radeon", "")
+ if "COMPUTE CORES" in name:
+ name, _ = name.split(',', 2)
+ if "X2" in name:
+ name = name.replace("Dual-Core", "")
+ name = name.replace("Dual Core", "")
+ if "X3" in name:
+ name = name.replace("Triple-Core", "")
+ if "X4" in name:
+ name = name.replace("Quad-Core", "")
+ elif name.startswith("Cyrix"):
+ name = name.replace("tm ", "")
+ else:
+ name = name.replace("Genuine Intel", "Intel")
+
+ name = name.replace(" CPU", "")
+ name = name.replace(" APU", "")
+ name = integrated_processor.sub("", name)
+ name = frequency.sub("", name)
+
+ for core_count in ("Dual", "Triple", "Quad", "Six",
+ "Eight", "Octal", "Twelve", "Sixteen",
+ "8", "16", "24", "32", "48", "56", "64"):
+ name = name.replace("%s-Core" % core_count, "")
+ name = name.replace("%s Core" % core_count, "")
+
+ while " " in name:
+ name = name.replace(" ", " ")
+
+ return name.strip()
+
+def cleanup_old_style_cpu_name(name):
+ for n in ('Intel', 'AMD', 'VIA', 'Cyrix'):
+ if n in name:
+ return nice_cpu_name(name)
+ return name
+
+def cpu_config_val(config,
+ mult_freq_re = re.compile(r'((?P<num_cpu>\d+)x\s+)?(?P<freq>\d+\.?\d*)')):
+ # "2x 1400.00 MHz + 2x 800.00 MHz" -> 4400.0
+ value = 0.0
+ for _, num, val in mult_freq_re.findall(config):
+ value += float(val) * (float(num) if num else 1.0)
+ return value
+
+def cpu_config_cmp(config1, config2):
+ r0 = cpu_config_val(config1)
+ r1 = cpu_config_val(config2)
+ if r0 == r1:
+ return 0
+ if r0 < r1:
+ return -1
+ return 1
+
+def cpu_config_is_close(config1, config2):
+ r0 = cpu_config_val(config1)
+ r1 = cpu_config_val(config2)
+ r1n = r1 * 0.9
+ return r0 > r1n and r0 < r1
+
+def guess_old_style_cpu_config(cpu_config, num_logical_cpus,
+ frequency_re = re.compile(r'(?P<freq>\d+\.?\d+)\s*(?P<mult>[GH])z')):
+ freq_mult = frequency_re.match(cpu_config)
+ if not freq_mult:
+ if num_logical_cpus > 1:
+ return '%dx %s' % (num_logical_cpus, cpu_config)
+ return cpu_config
+
+ freq, mult = freq_mult.groups()
+ freq = atof(freq)
+ if mult[0] == 'G':
+ freq *= 1000
+
+ candidate_config = "%dx %.2f MHz" % (num_logical_cpus, freq)
+ if cpu_config_cmp(cpu_config, candidate_config) == -1 and \
+ not cpu_config_is_close(cpu_config, candidate_config):
+ return candidate_config
+
+ return cpu_config
+
+def parse_old_style_cpu_info(cpu_config, cpu_name, bench_name,
+ old_style_cpu_name_re = re.compile('\s*(?P<numcpu>\d+)?(x?\s+)?(?P<cpuname>.*)')):
+ num_cpus = old_style_cpu_name_re.match(cpu_name)
+ assert num_cpus
+
+ groups = num_cpus.groupdict()
+ num_logical_cpus = int(groups['numcpu']) if groups['numcpu'] else 1
+ return {
+ 'NumThreads': num_logical_cpus,
+ 'NumCores': -1,
+ 'NumCpus': -1,
+ 'CpuName': cleanup_old_style_cpu_name(groups['cpuname']),
+ 'CpuConfig': guess_old_style_cpu_config(cpu_config, num_logical_cpus),
+ 'UsedThreads': guess_old_style_used_threads(bench_name, num_logical_cpus),
+ }
+
+def generate_machine_id(bench,
+ invalid_chars_re = re.compile(r'[^A-Za-z0-9\(\);]')):
+ mid = "%s;%s;%.2f" % (
+ bench['Board'] if 'Board' in bench else '(Unknown)',
+ bench['CpuName'],
+ cpu_config_val(bench['CpuConfig']),
+ )
+
+ return invalid_chars_re.sub("_", mid)
+
+def parse_new_style_bench_value(value):
+ values = value.strip().split(';')
+
+ if len(values) >= 3:
+ result = float(values[0].replace(",", "."))
+ elapsed_time = float(values[1].replace(",", "."))
+ threads_used = int(values[2])
+ else:
+ result = 0
+ elapsed_time = 0
+ threads_used = 0
+
+ benchmark_version = int(values[3]) if len(values) >= 4 else 1
+ extra = values[4] if len(values) >= 5 and not '|' in values[4] else ''
+ user_note = values[5] if len(values) >= 6 and not '|' in values[5] else ''
+
+ return {
+ 'BenchmarkResult': result,
+ 'ElapsedTime': elapsed_time,
+ 'UsedThreads': threads_used,
+ 'BenchmarkVersion': benchmark_version,
+ 'ExtraInfo': extra,
+ 'UserNote': user_note,
+ }
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print("Usage: benchmark-conf-to-json.py benchmark.conf")
+ sys.exit(1)
+
+ cfg = config_parser(strict=False)
+ cfg.optionxform = lambda opt: opt
+ cfg.read(sys.argv[1])
+
+ out = defaultdict(lambda: [])
+
+ for section in cfg.sections():
+ if section == 'param':
+ continue
+
+ for key in cfg[section]:
+ values = cfg[section][key].split('|')
+
+ if len(values) >= 10:
+ bench = {
+ 'MachineId': key,
+ 'Board': values[2],
+ 'CpuName': values[3],
+ 'CpuDesc': values[4],
+ 'CpuConfig': values[5].replace(",", "."),
+ 'MemoryInKiB': int(values[6]),
+ 'NumCpus': int(values[7]),
+ 'NumCores': int(values[8]),
+ 'NumThreads': int(values[9]),
+ }
+ bench.update(parse_new_style_bench_value(values[0]))
+ if len(values) >= 11:
+ bench['OpenGlRenderer'] = values[10]
+ if len(values) >= 12:
+ bench['GpuDesc'] = values[11]
+ if len(values) >= 13:
+ bench['MachineDataVersion'] = int(values[12])
+ if len(values) >= 14:
+ bench['PointerBits'] = int(values[13])
+ if len(values) >= 15:
+ bench['DataFromSuperUser'] = int(values[14])
+ if len(values) >= 16:
+ bench['PhysicalMemoryInMiB'] = int(values[15])
+ if len(values) >= 17:
+ bench['MemoryTypes'] = values[16]
+ elif len(values) >= 2:
+ bench = {
+ 'BenchmarkResult': float(values[0]),
+ }
+
+ bench.update(parse_old_style_cpu_info(
+ values[1].replace(",", "."),
+ key,
+ section))
+ bench['MachineId'] = generate_machine_id(bench)
+ else:
+ raise SyntaxError("unexpected value length: %d (%s)" % (len(values), values))
+
+ out[section].append(bench)
+
+ print(json.dumps(out))