# # \file generator.py # # \brief Generates the CUTLASS Library's instances # import enum import os.path import shutil from library import * from gemm_operation import * from conv2d_operation import * ################################################################################################### class EmitOperationKindLibrary: def __init__(self, generated_path, kind, args): self.generated_path = generated_path self.kind = kind self.args = args self.emitters = { OperationKind.Gemm: EmitGemmConfigurationLibrary, OperationKind.Conv2d: EmitConv2dConfigurationLibrary, } self.configurations = [] self.header_template = """ /* Generated by manifest.py - Do not edit. */ #include "cutlass/cutlass.h" #include "cutlass/library/library.h" #include "cutlass/library/manifest.h" namespace cutlass { namespace library { /////////////////////////////////////////////////////////////////////////////////////////////////// """ self.entry_template = """ // // Entry point to construct operations // void initialize_all_${operation_name}_operations(Manifest &manifest) { """ self.configuration_prototype_template = ( "void initialize_${configuration_name}(Manifest &manifest);\n" ) self.configuration_template = " initialize_${configuration_name}(manifest);\n" self.epilogue_template = """ } /////////////////////////////////////////////////////////////////////////////////////////////////// } // namespace library } // namespace cutlass """ # def __enter__(self): self.operation_path = os.path.join( self.generated_path, OperationKindNames[self.kind] ) os.mkdir(self.operation_path) self.top_level_path = os.path.join( self.operation_path, "all_%s_operations.cu" % OperationKindNames[self.kind] ) self.top_level_file = open(self.top_level_path, "w") self.top_level_file.write(self.header_template) self.source_files = [self.top_level_path] return self # def emit(self, configuration_name, operations): with self.emitters[self.kind]( self.operation_path, configuration_name ) as configuration_emitter: for operation in operations: configuration_emitter.emit(operation) self.source_files.append(configuration_emitter.configuration_path) self.configurations.append(configuration_name) self.top_level_file.write( SubstituteTemplate( self.configuration_prototype_template, {"configuration_name": configuration_name}, ) ) # def __exit__(self, exception_type, exception_value, traceback): self.top_level_file.write( SubstituteTemplate( self.entry_template, {"operation_name": OperationKindNames[self.kind]} ) ) for configuration_name in self.configurations: self.top_level_file.write( SubstituteTemplate( self.configuration_template, {"configuration_name": configuration_name}, ) ) self.top_level_file.write(self.epilogue_template) self.top_level_file.close() ################################################################################################### ################################################################################################### class Options: def __init__(self): pass ################################################################################################### # class Manifest: # def __init__(self, args): self.operations = {} self.args = args architectures = ( args.architectures.split(";") if len(args.architectures) else ["50"] ) self.compute_capabilities = [int(x) for x in architectures] self.selected_kernels = [] if args.operations == "all": self.operations_enabled = [] else: operations_list = [OperationKind.Gemm, OperationKind.Conv2d] self.operations_enabled = [ x for x in operations_list if OperationKindNames[x] in args.operations.split(",") ] if args.kernels == "all": self.kernel_names = [] else: self.kernel_names = [x for x in args.kernels.split(",") if x != ""] self.ignore_kernel_names = [ x for x in args.ignore_kernels.split(",") if x != "" ] if args.kernel_filter_file is None: self.kernel_filter_list = [] else: self.kernel_filter_list = self.get_kernel_filters(args.kernel_filter_file) self.operation_count = 0 self.operations_by_name = {} self.top_level_prologue = """ #include "cutlass/library/library.h" #include "cutlass/library/manifest.h" namespace cutlass { namespace library { ${prototypes} void initialize_all(Manifest &manifest) { """ self.top_level_reserve = " manifest.reserve(${operation_count});\n\n" self.top_level_epilogue = """ } } // namespace library } // namespace cutlass """ def get_kernel_filters(self, kernelListFile): if os.path.isfile(kernelListFile): with open(kernelListFile, "r") as fileReader: lines = [ line.rstrip() for line in fileReader if not line.startswith("#") ] lines = [re.compile(line) for line in lines if line] return lines else: return [] def filter_out_kernels(self, kernel_name, kernel_filter_list): for kernel_filter_re in kernel_filter_list: if kernel_filter_re.search(kernel_name) is not None: return True return False # def _filter_string_matches(self, filter_string, haystack): """ Returns true if all substrings appear in the haystack in order""" substrings = filter_string.split("*") for sub in substrings: idx = haystack.find(sub) if idx < 0: return False haystack = haystack[idx + len(sub) :] return True # def filter(self, operation): """ Filtering operations based on various criteria""" # filter based on compute capability enabled = False for cc in self.compute_capabilities: if ( cc >= operation.tile_description.minimum_compute_capability and cc <= operation.tile_description.maximum_compute_capability ): enabled = True break if not enabled: return False if ( len(self.operations_enabled) and not operation.operation_kind in self.operations_enabled ): return False # eliminate duplicates if operation.procedural_name() in self.operations_by_name.keys(): return False # Filter based on list of valid substrings if len(self.kernel_names): name = operation.procedural_name() enabled = False # compare against the include list for name_substr in self.kernel_names: if self._filter_string_matches(name_substr, name): enabled = True break # compare against the exclude list for name_substr in self.ignore_kernel_names: if self._filter_string_matches(name_substr, name): enabled = False break if len(self.kernel_filter_list) > 0: enabled = False if self.filter_out_kernels( operation.procedural_name(), self.kernel_filter_list ): enabled = True # todo: filter based on compute data type return enabled # # def append(self, operation): """ Inserts the operation. operation_kind -> configuration_name -> [] """ if self.filter(operation): self.selected_kernels.append(operation.procedural_name()) self.operations_by_name[operation.procedural_name()] = operation # add the configuration configuration_name = operation.configuration_name() if operation.operation_kind not in self.operations.keys(): self.operations[operation.operation_kind] = {} if ( configuration_name not in self.operations[operation.operation_kind].keys() ): self.operations[operation.operation_kind][configuration_name] = [] self.operations[operation.operation_kind][configuration_name].append( operation ) self.operation_count += 1 # # def emit(self, target=GeneratorTarget.Library): operation_emitters = {GeneratorTarget.Library: EmitOperationKindLibrary} generated_path = os.path.join(self.args.curr_build_dir, "generated") # create generated/ if os.path.exists(generated_path): shutil.rmtree(generated_path) os.mkdir(generated_path) source_files = [] top_level_path = os.path.join(generated_path, "initialize_all.cpp") with open(top_level_path, "w") as top_level_file: if target == GeneratorTarget.Library: source_files.append(top_level_path) prototypes = [] for operation_kind, configurations in self.operations.items(): prototypes.append( SubstituteTemplate( "void initialize_all_${operation_kind}_operations(Manifest &manifest);", {"operation_kind": OperationKindNames[operation_kind]}, ) ) top_level_file.write( SubstituteTemplate( self.top_level_prologue, {"prototypes": "\n".join(prototypes)} ) ) top_level_file.write( SubstituteTemplate( self.top_level_reserve, {"operation_count": str(self.operation_count)}, ) ) # for each operation kind, emit initializer for all configurations for operation_kind, configurations in self.operations.items(): with operation_emitters[target]( generated_path, operation_kind, self.args ) as operation_kind_emitter: for configuration_name, operations in configurations.items(): operation_kind_emitter.emit(configuration_name, operations) source_files += operation_kind_emitter.source_files top_level_file.write( SubstituteTemplate( " initialize_all_${operation_kind}_operations(manifest);\n", {"operation_kind": OperationKindNames[operation_kind]}, ) ) top_level_file.write(self.top_level_epilogue) # write the manifest.cmake file containing paths from all targets manifest_path = os.path.join(generated_path, "manifest.cmake") with open(manifest_path, "w") as manifest_file: target_name = "cutlass_library_objs" target_text = SubstituteTemplate( """cutlass_target_sources( ${target_name} BATCH_SOURCES ON PRIVATE """, {"target_name": target_name}, ) manifest_file.write(target_text) for source_file in source_files: manifest_file.write(" %s\n" % str(source_file.replace("\\", "/"))) manifest_file.write(")") # ################################################################################################### def GenerateManifest(args, operations, output_dir): assert isinstance(operations, list) if len(operations) == 0: return op = operations[0] required_cuda_ver_major = op.required_cuda_ver_major required_cuda_ver_minor = op.required_cuda_ver_minor manifest_path = os.path.join( output_dir, "all_%s_%s_operations.cu" % (args.operations, args.type) ) f = open(manifest_path, "w") f.write( """ /* Generated by generator.py - Do not edit. */ #if __CUDACC_VER_MAJOR__ > %s || (__CUDACC_VER_MAJOR__ == %s && __CUDACC_VER_MINOR__ >= %s) #include "cutlass/cutlass.h" #include "src/cuda/cutlass/library.h" #include "src/cuda/cutlass/manifest.h" namespace cutlass { namespace library { """ % ( str(required_cuda_ver_major), str(required_cuda_ver_major), str(required_cuda_ver_minor), ) ) for op in operations: f.write("void initialize_%s(Manifest &manifest);\n" % op.procedural_name()) f.write( """ void initialize_all_%s_%s_operations(Manifest &manifest) { """ % (args.operations, args.type) ) for op in operations: f.write(" initialize_%s(manifest);\n" % op.procedural_name()) f.write( """ } } // namespace library } // namespace cutlass #endif """ ) f.close()