diff options
author | Sayed Adel <seiko@imavr.com> | 2021-11-23 03:51:21 +0200 |
---|---|---|
committer | Sayed Adel <seiko@imavr.com> | 2021-12-08 22:18:07 +0200 |
commit | 563051aaebbb80da3d453cacf3e1f9782d3077fb (patch) | |
tree | d7066b82eb8158e74d2efbd8f730927fcfebb588 /doc/source/reference/simd/gen_features.py | |
parent | 6ae1a58e508a1f843ec3736488c67ac0bb793c16 (diff) | |
download | numpy-563051aaebbb80da3d453cacf3e1f9782d3077fb.tar.gz |
DOC, SIMD: Improve the auto-generated tables of CPU features
Diffstat (limited to 'doc/source/reference/simd/gen_features.py')
-rw-r--r-- | doc/source/reference/simd/gen_features.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/doc/source/reference/simd/gen_features.py b/doc/source/reference/simd/gen_features.py new file mode 100644 index 000000000..d74d54016 --- /dev/null +++ b/doc/source/reference/simd/gen_features.py @@ -0,0 +1,194 @@ +""" +Generate CPU features tables from CCompilerOpt +""" +from os import sys, path +from numpy.distutils.ccompiler_opt import CCompilerOpt + +class FakeCCompilerOpt(CCompilerOpt): + # disable caching no need for it + conf_nocache = True + + def __init__(self, arch, cc, *args, **kwargs): + self.fake_info = (arch, cc, '') + CCompilerOpt.__init__(self, None, **kwargs) + + def dist_compile(self, sources, flags, **kwargs): + return sources + + def dist_info(self): + return self.fake_info + + @staticmethod + def dist_log(*args, stderr=False): + # avoid printing + pass + + def feature_test(self, name, force_flags=None, macros=[]): + # To speed up + return True + +class Features: + def __init__(self, arch, cc): + self.copt = FakeCCompilerOpt(arch, cc, cpu_baseline="max") + + def names(self): + return self.copt.cpu_baseline_names() + + def serialize(self, features_names): + result = [] + for f in self.copt.feature_sorted(features_names): + gather = self.copt.feature_supported.get(f, {}).get("group", []) + implies = self.copt.feature_sorted(self.copt.feature_implies(f)) + result.append((f, implies, gather)) + return result + + def table(self, **kwargs): + return self.gen_table(self.serialize(self.names()), **kwargs) + + def table_diff(self, vs, **kwargs): + fnames = set(self.names()) + fnames_vs = set(vs.names()) + common = fnames.intersection(fnames_vs) + extra = fnames.difference(fnames_vs) + notavl = fnames_vs.difference(fnames) + iextra = {} + inotavl = {} + idiff = set() + for f in common: + implies = self.copt.feature_implies(f) + implies_vs = vs.copt.feature_implies(f) + e = implies.difference(implies_vs) + i = implies_vs.difference(implies) + if not i and not e: + continue + if e: + iextra[f] = e + if i: + inotavl[f] = e + idiff.add(f) + + def fbold(f): + if f in extra: + return f':enabled:`{f}`' + if f in notavl: + return f':disabled:`{f}`' + return f + + def fbold_implies(f, i): + if i in iextra.get(f, {}): + return f':enabled:`{i}`' + if f in notavl or i in inotavl.get(f, {}): + return f':disabled:`{i}`' + return i + + diff_all = self.serialize(idiff.union(extra)) + diff_all += vs.serialize(notavl) + content = self.gen_table( + diff_all, fstyle=fbold, fstyle_implies=fbold_implies, **kwargs + ) + return content + + def gen_table(self, serialized_features, fstyle=None, fstyle_implies=None, + **kwargs): + + if fstyle is None: + fstyle = lambda ft: f'``{ft}``' + if fstyle_implies is None: + fstyle_implies = lambda origin, ft: fstyle(ft) + + rows = [] + have_gather = False + for f, implies, gather in serialized_features: + if gather: + have_gather = True + name = fstyle(f) + implies = ' '.join([fstyle_implies(f, i) for i in implies]) + gather = ' '.join([fstyle_implies(f, i) for i in gather]) + rows.append((name, implies, gather)) + if not rows: + return '' + fields = ["Name", "Implies", "Gathers"] + if not have_gather: + del fields[2] + rows = [(name, implies) for name, implies, _ in rows] + return self.gen_rst_table(fields, rows, **kwargs) + + def gen_rst_table(self, field_names, rows, tab_size=4): + assert(not rows or len(field_names) == len(rows[0])) + rows.append(field_names) + fld_len = len(field_names) + cls_len = [max(len(c[i]) for c in rows) for i in range(fld_len)] + del rows[-1] + cformat = ' '.join('{:<%d}' % i for i in cls_len) + border = cformat.format(*['='*i for i in cls_len]) + + rows = [cformat.format(*row) for row in rows] + # header + rows = [border, cformat.format(*field_names), border] + rows + # footer + rows += [border] + # add left margin + rows = [(' ' * tab_size) + r for r in rows] + return '\n'.join(rows) + +def wrapper_section(title, content, tab_size=4): + tab = ' '*tab_size + if content: + return ( + f"{title}\n{'~'*len(title)}" + f"\n.. table::\n{tab}:align: left\n\n" + f"{content}\n\n" + ) + return '' + +def wrapper_tab(title, table, tab_size=4): + tab = ' '*tab_size + if table: + ('\n' + tab).join(( + '.. tab:: ' + title, + tab + '.. table::', + tab + 'align: left', + table + '\n\n' + )) + return '' + + +if __name__ == '__main__': + + pretty_names = { + "PPC64": "IBM/POWER big-endian", + "PPC64LE": "IBM/POWER little-endian", + "ARMHF": "ARMv7/A32", + "AARCH64": "ARMv8/A64", + "ICC": "Intel Compiler", + # "ICCW": "Intel Compiler msvc-like", + "MSVC": "Microsoft Visual C/C++" + } + gen_path = path.join( + path.dirname(path.realpath(__file__)), "generated_tables" + ) + with open(path.join(gen_path, 'cpu_features.inc'), 'wt') as fd: + fd.write(f'.. generated via {__file__}\n\n') + for arch in ( + ("x86", "PPC64", "PPC64LE", "ARMHF", "AARCH64") + ): + title = "On " + pretty_names.get(arch, arch) + table = Features(arch, 'gcc').table() + fd.write(wrapper_section(title, table)) + + with open(path.join(gen_path, 'compilers-diff.inc'), 'wt') as fd: + fd.write(f'.. generated via {__file__}\n\n') + for arch, cc_names in ( + ("x86", ("clang", "ICC", "MSVC")), + ("PPC64", ("clang",)), + ("PPC64LE", ("clang",)), + ("ARMHF", ("clang",)), + ("AARCH64", ("clang",)) + ): + arch_pname = pretty_names.get(arch, arch) + for cc in cc_names: + title = f"On {arch_pname}::{pretty_names.get(cc, cc)}" + table = Features(arch, cc).table_diff(Features(arch, "gcc")) + fd.write(wrapper_section(title, table)) + + |