generate_kernels.py (253 lines of code) (raw):

#!/usr/bin/env python from __future__ import absolute_import from __future__ import division from __future__ import print_function import re import time import os.path import subprocess import shutil # helpful for kernel development debug = 0 gen_kernels = [ [ "xgemm_blocksparse_32x32x32_xprop", "fprop", "A32", "B32", "C32" ], [ "xgemm_blocksparse_32x32x32_xprop", "fprop", "A10", "B10", "C10" ], [ "xgemm_blocksparse_32x32x32_xprop", "fprop", "A10", "B32", "C10" ], [ "xgemm_blocksparse_32x32x32_xprop", "fprop", "A7", "B7", "C7" ], [ "xgemm_blocksparse_32x32x32_xprop", "bprop", "A32", "B32", "C32" ], [ "xgemm_blocksparse_32x32x32_xprop", "bprop", "A10", "B10", "C10" ], [ "xgemm_blocksparse_32x32x32_xprop", "bprop", "A32", "B10", "C32" ], [ "xgemm_blocksparse_32x32x32_xprop", "bprop", "A7", "B7", "C7" ], [ "xgemm_blocksparse_32x32x32_xprop", "bprop", "A32", "B7", "C32" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A32", "B32", "C32" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A10", "B10", "C10" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A10", "B32", "C10" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A10", "B32", "C32" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A7", "B7", "C7" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A7", "B32", "C7" ], [ "xgemm_blocksparse_32x32x8_updat", "updat", "A7", "B32", "C32" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F32", "I32", "O32" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F16", "I16", "O16" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F16", "I32", "O32" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F32", "I32", "O32", "overlapK" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F16", "I16", "O16", "overlapK" ], [ "xconv_blocksparse_32x32x16_fprop", "fprop", "F16", "I32", "O32", "overlapK" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F32", "I32", "O32" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F16", "I16", "O16" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F16", "I32", "O32" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F32", "I32", "O32", "overlapC" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F16", "I16", "O16", "overlapC" ], [ "xconv_blocksparse_32x32x16_bprop", "bprop", "F16", "I32", "O32", "overlapC" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E32", "I32", "O32" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E16", "I16", "O16" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E32", "I16", "O16" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E16", "I32", "O16" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E32", "I16", "O32" ], [ "xconv_blocksparse_32x32x32_updat", "updat", "E16", "I32", "O32" ], ] kernel_specs = dict( xgemm_blocksparse_32x32x32_xprop=dict(basename="gemm_blocksparse_32x32x32", params="xprop_matmul", threads=128, share="(32*33)*4 + 4"), xgemm_blocksparse_32x32x32_updat=dict(basename="gemm_blocksparse_32x32x32", params="updat_matmul", threads=128, share="(32*32)*4 + 64"), xgemm_blocksparse_32x32x8_updat =dict(basename="gemm_blocksparse_32x32x8", params="updat_matmul", threads= 32, share="(32* 8)*4 + 64"), xconv_blocksparse_32x32x32_fprop=dict(basename="conv_blocksparse_32x32x32", params="xprop_conv", threads=128, share="(33+32)*32*2" ), xconv_blocksparse_32x32x16_fprop=dict(basename="conv_blocksparse_32x32x16", params="xprop_conv", threads= 64, share="(17+16)*32*2" ), xconv_blocksparse_32x32x16_bprop=dict(basename="conv_blocksparse_32x32x16", params="xprop_conv", threads= 64, share="(16+16)*32*2 + 64" ), xconv_blocksparse_32x32x32_updat=dict(basename="conv_blocksparse_32x32x32", params="updat_conv", threads=128, share="32*33*4 + 4" ), ) _params = { "xprop_matmul": [ "unsigned* param_Layout", "float* param_C", "float* param_A", "float* param_B", "float param_alpha", "float param_beta", "unsigned param_cda", "unsigned param_cdc", "unsigned param_m", ], "updat_matmul": [ "plist8 param_A", "plist8 param_B", "unsigned* param_Layout", "float* param_C", "float param_alpha", "float param_beta", "unsigned param_cda", "unsigned param_cdb", "unsigned param_k", "unsigned param_count", ], "xprop_conv": [ "unsigned* param_Block", "unsigned* param_LutMPQ", "unsigned* param_LutCK", "float* param_O", "float* param_F", "float* param_I", "float param_alpha", "unsigned param_TRS", "unsigned param_magic_TRS", "unsigned param_shift_TRS", "unsigned param_CDHW", "unsigned param_KMPQ", ], "updat_conv": [ "unsigned* param_Block", "unsigned* param_LutMPQ", "unsigned* param_LutCK", "float* param_O", "float* param_E", "float* param_I", "float param_alpha", "unsigned param_TRS", "unsigned param_magic_TRS", "unsigned param_shift_TRS", "unsigned param_CDHW", "unsigned param_KMPQ", "unsigned param_N", "unsigned param_sizeF", ], } def _get_cache_dir(subdir=None): cache_dir = os.path.expanduser("~/.cache/blocksparse") if subdir: subdir = subdir if isinstance(subdir, list) else [subdir] cache_dir = os.path.join(cache_dir, *subdir) if not os.path.exists(cache_dir): os.makedirs(cache_dir) return cache_dir base_dir = os.path.dirname(__file__) maxas_dir = os.path.join(base_dir, "vendor", "maxas") sass_dir = os.path.join(base_dir, "src", "sass") _space_re = re.compile(r"\s+") _share_template = r""" .shared .align 4 .b32 share[{0}]; """ _kernel_template = r""" .version {6} .target {0} .address_size 64 // args: {5} .visible .entry {1}( {2} ) {{ {4} ret; }} """ #.reqntid {3} def get_ptx_file(kernel_spec, args_spec, kernel_name, arch, ptx_ver): ptx_dir = _get_cache_dir([arch, 'ptx']) thread_spec = kernel_spec["threads"] param_spec = _params[kernel_spec["params"]] kernel_params = [] for p in param_spec: ptype, pname = _space_re.split(p) if ptype == "plist8": kernel_params.append(" .param .align 8 .b64 %s[8]" % pname) else: if ptype[-1] == '*': ptype = '.u64' elif ptype == 'float': ptype = '.f32' else: ptype = '.u32' kernel_params.append(" .param %s %s" % (ptype, pname)) kernel_params = ",\n".join(kernel_params) if "share" in kernel_spec: share = _share_template.format(eval(kernel_spec["share"])) else: share = "" kernel_text = _kernel_template.format(arch, kernel_name, kernel_params, thread_spec, share, args_spec, ptx_ver) kernel_ptx = os.path.join(ptx_dir, kernel_name + ".ptx") current_text = "" if os.path.exists(kernel_ptx): f = open(kernel_ptx, "r") current_text = f.read() f.close() # only write out the kernel if text has changed. if kernel_text != current_text: f = open(kernel_ptx, "w") f.write(kernel_text) f.close() return kernel_ptx include_re = re.compile(r'^<INCLUDE\s+file="([^"]+)"\s*/>') def extract_includes(name, includes=None): if not includes: includes = list() sass_file = os.path.join(sass_dir, name) includes.append((sass_file, os.path.getmtime(sass_file))) for line in open(sass_file, "r"): match = include_re.search(line) if match: extract_includes(match.group(1), includes) return includes def run_command(cmdlist): cmd = " ".join(cmdlist) proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() if proc.returncode: raise RuntimeError("Error(%d):\n%s\n%s" % (proc.returncode, cmd, err)) #if debug: print(cmd) if out: print(out) if err: print(err) def get_kernel(kernel): major, minor = 5, 0 arch = "sm_%d%d" % (major, minor) libprefix = "PERL5LIB=%s" % maxas_dir maxas_i = [libprefix, os.path.join(maxas_dir, "maxas.pl") + " -i -w"] maxas_p = [libprefix, os.path.join(maxas_dir, "maxas.pl") + " -p"] sass_name = kernel[0] kernel_spec = kernel_specs[sass_name] kernel_name = kernel_spec["basename"] args_spec = str(kernel[1:]) for opt in kernel[1:]: maxas_i.append("-D%s 1" % opt) maxas_p.append("-D%s 1" % opt) kernel_name += "_" + opt maxas_i.insert(2, "-k " + kernel_name) sass_name += ".sass" cubin_name = kernel_name + ".cubin" cubin_dir = _get_cache_dir([arch, 'cubin']) ptx_version = "4.2" if major < 6 else "5.0" ptx_file = get_ptx_file(kernel_spec, args_spec, kernel_name, arch, ptx_version) sass_file = os.path.join(sass_dir, sass_name) cubin_file = os.path.join(cubin_dir, cubin_name) if not os.path.exists(sass_file): raise RuntimeError("Missing: %s for kernel: %s" % (sass_name, kernel_name)) ptx_mtime = os.path.getmtime(ptx_file) cubin_mtime = os.path.getmtime(cubin_file) if os.path.exists(cubin_file) else 0 build_cubin = False if ptx_mtime > cubin_mtime: build_cubin = True includes = extract_includes(sass_name) for include, include_mtime in includes: if include_mtime > cubin_mtime: build_cubin = True break if build_cubin: # build the cubin and run maxas in the same command # we don't want the chance of a generated cubin not processed by maxas (in case user hits ^C in between these steps) run_command([ "ptxas -v -arch", arch, "-o", cubin_file, ptx_file, ";" ] + maxas_i + [sass_file, cubin_file]) cubin_mtime = time.time() # output preprocessed and disassembled versions in debug mode if debug: pre_dir = _get_cache_dir([arch, 'pre']) dump_dir = _get_cache_dir([arch, 'dump']) pre_file = os.path.join(pre_dir, kernel_name + "_pre.sass") dump_file = os.path.join(dump_dir, kernel_name + "_dump.sass") pre_mtime = os.path.getmtime(pre_file) if os.path.exists(pre_file) else 0 dump_mtime = os.path.getmtime(dump_file) if os.path.exists(dump_file) else 0 for include, include_mtime in includes: if include_mtime > pre_mtime: run_command(maxas_p + [sass_file, pre_file]) break # if cubin_mtime > dump_mtime: # run_command(["nvdisasm -c", cubin_file, ">", dump_file]) return kernel_name, cubin_file def main(): header_file = os.path.join(base_dir, "build", "blocksparse_kernels.h") with open(header_file, "w") as output_file: kernel_map = "\n\nstd::map<std::string, std::pair<const uint8_t*, size_t>> kernel_map_ = {" for kernel in gen_kernels: kernel_name, cubin_file = get_kernel(kernel) kernel_text = "\n\nconst uint8_t %s[] = {" % kernel_name with open(cubin_file, 'rb') as input_file: count = 0 byte = input_file.read(1) use_hex = 'hex' in dir(byte) while byte: if count % 32 == 0: kernel_text += "\n " count += 1 if use_hex: kernel_text += "0x" + byte.hex() + "," else: kernel_text += "0x" + byte.encode("hex") + "," byte = input_file.read(1) kernel_text += "\n};" kernel_map += "\n { \"%s\", { %s, %d } }," % (kernel_name, kernel_name, count) output_file.write(kernel_text) kernel_map += "\n};" output_file.write(kernel_map) if __name__ == '__main__': main()