Fix typos 2 (#842)

Co-authored-by: Haicheng Wu <57973641+hwu36@users.noreply.github.com>
This commit is contained in:
Alexander Pivovarov
2023-03-09 23:22:56 -05:00
committed by GitHub
co-authored by Haicheng Wu
parent c4f6b8c6bc
commit 7e370c9637
161 changed files with 310 additions and 309 deletions
+1 -1
View File
@@ -47,7 +47,7 @@
or utilities within CUTLASS. Such utilities are demonstrated elsewhere in other examples and are
prevalent in the CUTLASS unit tests.
This example has delibrately been kept similar to the basic_gemm example from cutass-1.3 to
This example has delibrately been kept similar to the basic_gemm example from cutlass-1.3 to
highlight the minimum amount of differences needed to transition to cutlass-2.0.
Cutlass-1.3 sgemm: https://github.com/NVIDIA/cutlass/blob/master/examples/00_basic_gemm/basic_gemm.cu
@@ -75,7 +75,7 @@ Now that we setup the properties of data, we have to setup properties of computa
Second, we create template variables of tile sizes for thread-block, warp and mma-op to 128x128x32,
64x64x32, 8x8x4 (MxNxK) respectively. When passed to instantiate CUTLASS GEMM kernel, it internally
deduce the amount of threads needed per thread-block, amount of shared memory, storing data in
bank-conflict free manner, and ton of other variables required to compose, intialize and launch a
bank-conflict free manner, and ton of other variables required to compose, initialize and launch a
high performance GEMM kernel. This is the beauty of CUTLASS, it relieves developer from
understanding and coding complicated hardware optimizations which can easily go wrong.
@@ -107,7 +107,7 @@ is done which threadblock launched on an SM, CUDA SM architecture of GPU you wan
These are all put together to create a template variable which describes CUTLASS GEMM kernel using
cutlass::gemm::device::Gemm template.
The next step is to intialize physical data, instantiate and initialize CUTLASS kernel and run it.
The next step is to initialize physical data, instantiate and initialize CUTLASS kernel and run it.
We use CUTLASS utilities to initialize, fill, compare matrices as they are simple and doesn't come
in the way of learning CUTLASS.
@@ -115,7 +115,7 @@ Once all the matrices are initialized and filled with data, create arguments tup
kernel which takes problem size (M = 5120, N = 4096 and K = 4096), matrices, alpha, beta and the
important one, split k-dimension factor. Along with that, we query CUTLASS if any scratch-space
memory required by the kernel we instantiated. If yes, we create it and pass it along with other
arguments created to intialize CUTLASS kernel then, the kernel is launched.
arguments created to initialize CUTLASS kernel then, the kernel is launched.
In this example, we later on launch a reference gemm kernel (from CUTLASS utilities) to compare if
the output from CUTLASS kernel is same as reference GEMM kernel.
@@ -74,7 +74,7 @@ Now that we setup the properties of data, we have to setup properties of computa
Second, we create template variables of tile sizes for thread-block, warp and mma-op to 128x256x64,
64x64x16, 8x8x16 (MxNxK) respectively. When passed to instantiate CUTLASS GEMM kernel, it internally
deduce the amount of threads needed per thread-block, amount of shared memory, storing data in
bank-conflict free manner, and ton of other variables required to compose, intialize and launch a
bank-conflict free manner, and ton of other variables required to compose, initialize and launch a
high performance GEMM kernel. This is the beauty of CUTLASS, it relieves developer from
understanding and coding complicated hardware optimizations which can easily go wrong.
@@ -106,7 +106,7 @@ is done which threadblock launched on an SM, CUDA SM architecture of GPU you wan
These are all put together to create a template variable which describes CUTLASS GEMM kernel using
cutlass::gemm::device::Gemm template.
The next step is to intialize physical data, instantiate and initialize CUTLASS kernel and run it.
The next step is to initialize physical data, instantiate and initialize CUTLASS kernel and run it.
We use CUTLASS utilities to initialize, fill, compare matrices as they are simple and doesn't come
in the way of learning CUTLASS.
@@ -114,7 +114,7 @@ Once all the matrices are initialized and filled with data, create arguments tup
kernel which takes problem size (M = 5120, N = 4096 and K = 4096), matrices, alpha, beta and the
important one, split k-dimension factor. Along with that, we query CUTLASS if any scratch-space
memory required by the kernel we instantiated. If yes, we create it and pass it along with other
arguments created to intialize CUTLASS kernel then, the kernel is launched.
arguments created to initialize CUTLASS kernel then, the kernel is launched.
In this example, we later on launch a reference gemm kernel (from CUTLASS utilities) to compare if
the output from CUTLASS kernel is same as reference GEMM kernel.
@@ -76,7 +76,7 @@ Now that we setup the properties of data, we have to setup properties of computa
Second, we create template variables of tile sizes for thread-block, warp and mma-op to 128x128x128,
64x64x128, 8x8x32 (MxNxK) respectively. When passed to instantiate CUTLASS Implicit GEMM kernel, it
internally deduces the amount of threads needed per thread-block, amount of shared memory, storing
data in bank-conflict free manner, and ton of other variables required to compose, intialize and
data in bank-conflict free manner, and ton of other variables required to compose, initialize and
launch a high performance Implicit GEMM kernel. This is the beauty of CUTLASS, it relieves developer
from understanding and coding complicated hardware optimizations which can easily go wrong.
@@ -108,7 +108,7 @@ is done which threadblock launched on an SM, CUDA SM architecture of GPU you wan
These are all put together to create a template variable which describes CUTLASS Implicit GEMM
kernel using cutlass::conv::device::ImplicitGemm template.
The next step is to intialize physical data, instantiate and initialize CUTLASS kernel and run it.
The next step is to initialize physical data, instantiate and initialize CUTLASS kernel and run it.
We use CUTLASS utilities to initialize, fill, compare tensors as they are simple and doesn't come
in the way of learning CUTLASS.
@@ -117,7 +117,7 @@ kernel which takes problem size (N = 1, H = 64, W = 64, C = 128), filter size (K
R = 3, S = 3, C = 128 ), padding, strides, dilation, tensors, alpha, beta and the
important one, split k-dimension factor. Along with that, we query CUTLASS if any scratch-space
memory required by the kernel we instantiated. If yes, we create it and pass it along with other
arguments created to intialize CUTLASS kernel then, the kernel is launched.
arguments created to initialize CUTLASS kernel then, the kernel is launched.
In this example, we later on launch a reference convolution kernel (from CUTLASS utilities) to
compare if the output from CUTLASS kernel is same as the reference implicit GEMM kernel.
@@ -321,7 +321,7 @@ public:
int smem_write_stage_idx = 1;
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing
// shared memory loads (which have the tighest latency requirement).
// shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -461,7 +461,7 @@ public:
int gemm_k_iterations_1 = FragmentIteratorA1::Policy::kIterations / Base::kWarpGemmIterations1;
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing
// shared memory loads (which have the tighest latency requirement).
// shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -341,7 +341,7 @@ public:
int smem_write_stage_idx = 1;
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing
// shared memory loads (which have the tighest latency requirement).
// shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -325,7 +325,7 @@ public:
iterator_B0.clear_mask(gemm_k_iterations_0 <= 1);
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing
// shared memory loads (which have the tighest latency requirement).
// shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -346,7 +346,7 @@ public:
iterator_B0.clear_mask(gemm_k_iterations_0 <= 1);
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing
// shared memory loads (which have the tighest latency requirement).
// shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -73,7 +73,7 @@ Now that we setup the properties of data, we have to setup properties of computa
Second, we create template variables of tile sizes for thread-block, warp and mma-op to 128x128x64,
64x64x64, 16x8x16 (MxNxK) respectively. When passed to instantiate CUTLASS Implicit GEMM kernel, it
internally deduces the amount of threads needed per thread-block, amount of shared memory, storing
data in bank-conflict free manner, and ton of other variables required to compose, intialize and
data in bank-conflict free manner, and ton of other variables required to compose, initialize and
launch a high performance Implicit GEMM kernel. This is the beauty of CUTLASS, it relieves developer
from understanding and coding complicated hardware optimizations which can easily go wrong.
@@ -95,7 +95,7 @@ is done which threadblock launched on an SM, CUDA SM architecture of GPU you wan
These are all put together to create a template variable which describes CUTLASS Implicit GEMM
kernel using cutlass::conv::device::ImplicitGemm template.
The next step is to intialize physical data, instantiate and initialize CUTLASS kernel and run it.
The next step is to initialize physical data, instantiate and initialize CUTLASS kernel and run it.
We use CUTLASS utilities to initialize, fill, compare tensors as they are simple and doesn't come
in the way of learning CUTLASS.
@@ -104,7 +104,7 @@ kernel which takes problem size (N = 1, H = 64, W = 64, C = 128), filter size (K
R = 3, S = 3, C = 128 ), padding, strides, dilation, tensors, alpha, beta and the
important one, split k-dimension factor. Along with that, we query CUTLASS if any scratch-space
memory required by the kernel we instantiated. If yes, we create it and pass it along with other
arguments created to intialize CUTLASS kernel then, the kernel is launched.
arguments created to initialize CUTLASS kernel then, the kernel is launched.
In this example, we later on launch a reference convolution kernel (from CUTLASS utilities) to
compare if the output from CUTLASS kernel is same as the reference implicit GEMM kernel.
@@ -36,7 +36,7 @@ computing GEMM. So the output also contains either a Mx1 or 1XN vector. It onl
core instructions.
Most of the reduction is done in gemm/warp level, see gemm/warp/mma_with_reduction_tensor_op.h
A few bit of reduction is done in the epilouge before storing the vector, see
A few bit of reduction is done in the epilogue before storing the vector, see
epilogue/threadblock/epilogue_gemm_k_reduction.h
*/
@@ -1088,7 +1088,7 @@ int main(int argc, char const **args) {
// Determine kernel configuration based on head size.
// If head size is less than or equal to 64, each block operates over 64 queries and
// 64 keys, and parital results can be stored in the register file.
// 64 keys, and partial results can be stored in the register file.
// If head size is greater than 64, each block operates over 32 queries and 128 keys,
// and partial results are stored in shared memory.
if (options.head_size_v > 64) {
@@ -1173,7 +1173,7 @@ int main(int argc, char const **args) {
// Determine kernel configuration based on head size.
// If head size is less than or equal to 64, each block operates over 64 queries and
// 64 keys, and parital results can be stored in the register file.
// 64 keys, and partial results can be stored in the register file.
// If head size is greater than 64, each block operates over 32 queries and 128 keys,
// and partial results are stored in shared memory.
if (options.head_size_v > 64) {
@@ -310,7 +310,7 @@ class CustomMmaPipelined : public CustomMmaBase<Shape_, Policy_, 2> {
iterator_B.clear_mask(gemm_k_iterations <= 1);
// Issue loads during the first warp-level matrix multiply-add *AFTER*
// issuing shared memory loads (which have the tighest latency requirement).
// issuing shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -600,7 +600,7 @@ class MmaPipelinedFromSharedMemory : public MmaBaseFromSharedMemory<
iterator_B.clear_mask(gemm_k_iterations <= 1);
// Issue loads during the first warp-level matrix multiply-add *AFTER*
// issuing shared memory loads (which have the tighest latency requirement).
// issuing shared memory loads (which have the tightest latency requirement).
//
// Mainloop
@@ -181,7 +181,7 @@ class PredicatedTileAccessIteratorResidualLast<
BytePointer pointer_;
/// Below is used when Gather is turned on. We need to record strided_offset
/// and contiguous_offset seperated to compute the offset by using
/// and contiguous_offset separated to compute the offset by using
///
/// offset = contiguous_offset + indices[strided_offset]
///
@@ -86,14 +86,14 @@ class gen_default_b2b_mma:
"OperatorClass", str(stage), "Operator")
return gen_code
def gen_using_FusedAddBiasEpilouge(self):
def gen_using_FusedAddBiasEpilogue(self):
gen_code = ""
for i in range(self.b2b_num - 1):
code_using = helper.var_idx("using FusedAddBiasEpilouge", i)
epilouge_name = "typename cutlass::epilogue::threadblock::DefaultFusedBiasActEpilogueTensorOp"
code_using = helper.var_idx("using FusedAddBiasEpilogue", i)
epilogue_name = "typename cutlass::epilogue::threadblock::DefaultFusedBiasActEpilogueTensorOp"
template_args = helper.var_idx("<ThreadblockShape", i) + helper.var_idx(",typename MmaCore", i) + helper.var_idx("::MmaPolicy::Operator, 1, EpilogueOutputOp", i) + ", 2>::Epilogue"
gen_code += code_using + " = " + epilouge_name + template_args + ";\n"
gen_code += code_using + " = " + epilogue_name + template_args + ";\n"
return gen_code
@@ -161,12 +161,12 @@ class gen_default_b2b_mma:
MmaPipelined_param_list += "ElementAccumulator0, layout::RowMajor, "
for i in range(self.b2b_num - 1):
epilouge_name = "EpilogueOutputOp" + str(i)
MmaPipelined_param_list += epilouge_name + ", "
epilogue_name = "EpilogueOutputOp" + str(i)
MmaPipelined_param_list += epilogue_name + ", "
for i in range(self.b2b_num - 1):
epilouge_name = "FusedAddBiasEpilouge" + str(i)
MmaPipelined_param_list += epilouge_name + ", "
epilogue_name = "FusedAddBiasEpilogue" + str(i)
MmaPipelined_param_list += epilogue_name + ", "
for i in range(self.b2b_num):
MmaPolicy = "typename MmaCore" + str(i) + "::MmaPolicy"
@@ -198,7 +198,7 @@ class gen_default_b2b_mma:
mmacore_codebody = self.gen_using_MmaCore(2)
iterator_codebody = self.gen_using_Iterator()
fragment_iterator_codebody = self.gen_fragment_iterator()
epilogue_iterator_codebody = self.gen_using_FusedAddBiasEpilouge()
epilogue_iterator_codebody = self.gen_using_FusedAddBiasEpilogue()
threadBlockMma = self.gen_threadblockmma()
specialized_code = mmacore_codebody + iterator_codebody + fragment_iterator_codebody + epilogue_iterator_codebody + threadBlockMma
@@ -352,7 +352,7 @@ class gen_b2b_mme_pipelined:
}\n\
\n\
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing \n\
// shared memory loads (which have the tighest latency requirement).\n\
// shared memory loads (which have the tightest latency requirement).\n\
\n\
//\n\
// Mainloop\n\
@@ -459,7 +459,7 @@ class gen_b2b_mme_pipelined:
}\n\
\n\
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing \n\
// shared memory loads (which have the tighest latency requirement).\n\
// shared memory loads (which have the tightest latency requirement).\n\
iterator_A.load(tb_frag_A);\n\
\n\
//\n\
@@ -490,7 +490,7 @@ class gen_b2b_mme_pipelined:
__syncthreads();\n\
\n\
// Issue loads during the first warp-level matrix multiply-add *AFTER* issuing \n\
// shared memory loads (which have the tighest latency requirement).\n\
// shared memory loads (which have the tightest latency requirement).\n\
iterator_A.load(tb_frag_A);\n\
\n\
++this->smem_iterator_B0_;\n\
@@ -549,12 +549,12 @@ class gen_b2b_mme_pipelined:
code = "// " + str(id + 1) + " Gemm"
code += " /// Iterator to load a warp-scoped tile of A1 operand from intermediate accumulator tile\n"
code += " " + helper.var_idx("FragmentC", id - 1) + helper.var_idx(" after_epilouge_accu", id - 1) + ";\n"
code += " " + helper.var_idx("FragmentC", id - 1) + helper.var_idx(" after_epilogue_accu", id - 1) + ";\n"
code += " " + helper.var_idx("epilogue_", id - 1) + helper.var_idx("(output_op_", id - 1) + helper.var_idx(", accum", id - 1) \
+ helper.var_idx(", after_epilouge_accu", id - 1) + helper.var_idx(", iterator_C", id - 1) +");\n"
+ helper.var_idx(", after_epilogue_accu", id - 1) + helper.var_idx(", iterator_C", id - 1) +");\n"
# FragmentIteratorA1 warp_tile_iterator_A1_(accum0);
code += " " + helper.var_idx("FragmentIteratorA", id) + helper.var_idx(" warp_tile_iterator_A", id) +"_(" + helper.var_idx("after_epilouge_accu", id - 1) + ");\n"
code += " " + helper.var_idx("FragmentIteratorA", id) + helper.var_idx(" warp_tile_iterator_A", id) +"_(" + helper.var_idx("after_epilogue_accu", id - 1) + ");\n"
# FragmentB1 tb_frag_B1;
code += " " + helper.var_idx("FragmentB", id) + " " + helper.var_idx("tb_frag_B", id) + ";\n"
# tb_frag_B1.clear();
@@ -990,7 +990,7 @@ class gen_threadblock:
self.gen_b2b_mma_base = gen_b2b_mma_base(template_param, gen_class_name, b2b_num, cutlass_deps_root, project_root)
self.gen_b2b_mma_piplined = gen_b2b_mme_pipelined(template_param, gen_class_name, b2b_num, cutlass_deps_root, project_root)
self.gen_b2b_mma_pipelined = gen_b2b_mme_pipelined(template_param, gen_class_name, b2b_num, cutlass_deps_root, project_root)
self.gen_default_b2b_mma = gen_default_b2b_mma(template_param, gen_class_name, b2b_num, cutlass_deps_root, project_root)
@@ -1001,7 +1001,7 @@ class gen_threadblock:
with open(self.file_dir + "b2b_mma_base.h", "w+") as f:
f.write(base_code)
pipeline_code = self.gen_b2b_mma_piplined.gen_code(first_use_1stage = first_use_1stage)
pipeline_code = self.gen_b2b_mma_pipelined.gen_code(first_use_1stage = first_use_1stage)
print("[INFO]: Gen kernel code [b2b_mma_pipelined.h]output Dir: is ", self.file_dir)
with open(self.file_dir + "b2b_mma_pipelined.h", "w+") as f:
@@ -45,7 +45,7 @@ class gen_verify:
self.user_header_file = ""
for header in user_header_file:
self.user_header_file += "#include \"" + header + "\"\n"
self.seperate_cutlass = gen_basic.gen_volta_turing_fuse_act_impl(fuse_gemm_info, gen_class_name, user_header_file, output_dir)
self.separate_cutlass = gen_basic.gen_volta_turing_fuse_act_impl(fuse_gemm_info, gen_class_name, user_header_file, output_dir)
self.gen_params()
self.output_dir = output_dir
@@ -53,14 +53,14 @@ class gen_verify:
def gen_code(self):
code = ""
code += self.user_header_file
code += self.seperate_cutlass.gen_using(False) #False -> Turing, True -> Volta
code += self.separate_cutlass.gen_using(False) #False -> Turing, True -> Volta
code_body = ""
for i in range(self.b2b_num):
code_body += " " + helper.var_idx("Gemm", i) + helper.var_idx(" gemm_op_", i) + ";\n"
code_body += " " + helper.var_idx("gemm_op_", i) + helper.var_idx(".initialize(Arguments_", i) + ", nullptr);\n"
code_body += self.seperate_cutlass.gen_run()
code_body += self.separate_cutlass.gen_run()
code += ir.gen_func(self.name, self.params, code_body)
helper.write_2_headfile("cutlass_verify.h", self.output_dir, code)
@@ -87,6 +87,6 @@ class gen_verify:
def gen_initialize():
code = ""
initialize_code = self.seperate_cutlass.gen_initialize()
initialize_code = self.separate_cutlass.gen_initialize()
code = ir.gen_func("initialize", [[]])
@@ -83,23 +83,23 @@ def list_2_string(input_list, ):
return rtn_string
def get_epilouge_info(layer_info):
def get_epilogue_info(layer_info):
return layer_info['epilogue']
def get_epilogue_tp(layer_info):
epilogue_info = get_epilouge_info(layer_info)
epilogue_info = get_epilogue_info(layer_info)
return epilogue_info['tp']
def get_epilogue_add_bias_or_not(layer_info):
epilogue_info = get_epilouge_info(layer_info)
epilogue_info = get_epilogue_info(layer_info)
return epilogue_info['bias']['addbias']
def get_epilogue_add_bias_tp(layer_info):
epilogue_info = get_epilouge_info(layer_info)
epilogue_info = get_epilogue_info(layer_info)
return epilogue_info['bias']['bias_tp']
def get_epilogue_args(layer_info):
epilogue_info = get_epilouge_info(layer_info)
epilogue_info = get_epilogue_info(layer_info)
return epilogue_info['args']
def get_epilogue_bias_shape(layer_info):
@@ -33,7 +33,7 @@
\brief Hopper GEMM example leveraging collective operation builders.
This example showcases the use of CUTLASS's CollectiveBuilder to easily construct performant kernels
targetting the NVIDIA Hopper architecture.
targeting the NVIDIA Hopper architecture.
Background and motivation
-------------------------
@@ -45,7 +45,7 @@
However, DefaultGemmConfigurations leave multiple opportunities for improvement, which are addressed
in CUTLASS 3:
(1) DefaultGemmConfigurations do not allow one to use a more-performant set of parameters without
specifying every parameter. For example, the DefaultGemmConfigurations for GEMMs targetting
specifying every parameter. For example, the DefaultGemmConfigurations for GEMMs targeting
Ampere specify that three pipeline stages should be used regardless of the sizes of operands.
If one wished to increase this value, one would also need to specify all other template parameters.
This leaves a gap between a high-level ease-of-use interface and a lower-level detailed interface.
@@ -55,7 +55,7 @@
Alongside these opportunities for improvement, the Hopper architecture offers new features that increase
the number of valid configurations of a kernel. In addition to the many template parameters already available
in CUTLASS 2 kernels, CUTLASS 3 kernels targetting Hopper also have various scheduling modes to select from that control:
in CUTLASS 2 kernels, CUTLASS 3 kernels targeting Hopper also have various scheduling modes to select from that control:
(1) how data is to be loaded (e.g., using the Hopper TMA feature or Ampere cp.async)
(2) how work is to be divided among warps in a thread block (e.g., whether to use "warp specialization")
(3) whether persistent thread blocks should be used
@@ -64,13 +64,13 @@
Introduction to the CollectiveBuilder
-------------------------------------
CUTLASS 3 introduces the CollectiveBuilder to further ease the process of selecting template parameters
for kernels targetting Hopper. Similar to the DefaultGemmConfigurations used in CUTLASS 2, the CollectiveBuilder
for kernels targeting Hopper. Similar to the DefaultGemmConfigurations used in CUTLASS 2, the CollectiveBuilder
takes in a small set of template parameters (e.g., the data types of operands A and B). It then automatically
determines the data loading strategy to use depending on whether the Hopper TMA feature can be used with the provided
parameters. If one does not indicate a particular scheduling policy or stage count to use (by using `Auto` template
parameters), the CollectiveBuilder will also automatically select these.
Unlike DefaultGemmConfigurations a parital specialization of the CollectiveBuilder is not needed for many
Unlike DefaultGemmConfigurations a partial specialization of the CollectiveBuilder is not needed for many
configurations of operand types. Instead the CollectiveBuilder "builds" a configuration based on generic
properties of the specified operands, layouts, and other parameters. For example, when the stage count
is set to `Auto`, the CollectiveBuilder may automatically calculate the maximum number of stages that
@@ -90,7 +90,7 @@
Details of this example
-----------------------
This example walks through the use of the CollectiveBuilder with various schedules and stage counts specified.
This example also illustrates how CUTLASS 3 GEMMs targetting Hopper automatically support batched GEMMs by simply
This example also illustrates how CUTLASS 3 GEMMs targeting Hopper automatically support batched GEMMs by simply
extending the problem size with an additional tensor rank.
Example usage:
@@ -162,7 +162,7 @@ struct Options {
out << "49_hopper_gemm_schedules_with_collective_builder\n\n"
<< " This example showcases the use of CUTLASS's collective operation builders to easily construct\n"
<< " performant kernels targetting NVIDIA's Hopper architecture.\n\n"
<< " performant kernels targeting NVIDIA's Hopper architecture.\n\n"
<< "Options:\n\n"
<< " --help If specified, displays this usage statement\n\n"
<< " --m=<int> Sets the M extent of the GEMM\n"