cumo 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +590 -152
  3. data/CHANGELOG.md +29 -0
  4. data/README.md +140 -0
  5. data/bench/cumo_probe.rb +1 -1
  6. data/cumo.gemspec +1 -1
  7. data/ext/cumo/cuda/cudnn.c +18 -0
  8. data/ext/cumo/cuda/cudnn_impl.cpp +60 -14
  9. data/ext/cumo/cuda/driver.c +11 -9
  10. data/ext/cumo/cuda/runtime.c +56 -0
  11. data/ext/cumo/cumo.c +4 -0
  12. data/ext/cumo/extconf.rb +2 -0
  13. data/ext/cumo/include/cumo/bit_reduce_kernel.h +6 -6
  14. data/ext/cumo/include/cumo/cuda/cudnn.h +40 -0
  15. data/ext/cumo/include/cumo/cuda/runtime.h +2 -0
  16. data/ext/cumo/include/cumo/intern.h +9 -0
  17. data/ext/cumo/include/cumo/narray.h +1 -0
  18. data/ext/cumo/include/cumo/reduce_kernel.h +32 -28
  19. data/ext/cumo/include/cumo/row_kernel.h +170 -0
  20. data/ext/cumo/include/cumo/row_method.h +48 -0
  21. data/ext/cumo/include/cumo/template_kernel.h +26 -0
  22. data/ext/cumo/include/cumo/types/bf16_def.h +82 -0
  23. data/ext/cumo/include/cumo/types/bf16_def_kernel.h +28 -0
  24. data/ext/cumo/include/cumo/types/bf16_macro.h +186 -0
  25. data/ext/cumo/include/cumo/types/bf16_macro_kernel.h +213 -0
  26. data/ext/cumo/include/cumo/types/bfloat.h +22 -0
  27. data/ext/cumo/include/cumo/types/bfloat_kernel.h +16 -0
  28. data/ext/cumo/include/cumo/types/bit.h +2 -0
  29. data/ext/cumo/include/cumo/types/bit_kernel.h +2 -0
  30. data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
  31. data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
  32. data/ext/cumo/include/cumo/types/float_def.h +8 -0
  33. data/ext/cumo/include/cumo/types/float_def_kernel.h +8 -0
  34. data/ext/cumo/include/cumo/types/float_macro.h +19 -17
  35. data/ext/cumo/include/cumo/types/float_macro_kernel.h +19 -19
  36. data/ext/cumo/include/cumo/types/half_macro.h +15 -17
  37. data/ext/cumo/include/cumo/types/half_macro_kernel.h +15 -0
  38. data/ext/cumo/include/cumo/types/robj_macro.h +2 -0
  39. data/ext/cumo/include/cumo/types/xint_macro.h +2 -6
  40. data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
  41. data/ext/cumo/include/cumo.h +2 -2
  42. data/ext/cumo/narray/gen/def/bfloat.rb +56 -0
  43. data/ext/cumo/narray/gen/def/bit.rb +11 -1
  44. data/ext/cumo/narray/gen/def/dcomplex.rb +11 -1
  45. data/ext/cumo/narray/gen/def/dfloat.rb +10 -1
  46. data/ext/cumo/narray/gen/def/hfloat.rb +10 -1
  47. data/ext/cumo/narray/gen/def/int16.rb +11 -1
  48. data/ext/cumo/narray/gen/def/int32.rb +11 -1
  49. data/ext/cumo/narray/gen/def/int64.rb +11 -1
  50. data/ext/cumo/narray/gen/def/int8.rb +11 -1
  51. data/ext/cumo/narray/gen/def/robject.rb +11 -1
  52. data/ext/cumo/narray/gen/def/scomplex.rb +11 -1
  53. data/ext/cumo/narray/gen/def/sfloat.rb +10 -1
  54. data/ext/cumo/narray/gen/def/uint16.rb +11 -1
  55. data/ext/cumo/narray/gen/def/uint32.rb +11 -1
  56. data/ext/cumo/narray/gen/def/uint64.rb +11 -1
  57. data/ext/cumo/narray/gen/def/uint8.rb +11 -1
  58. data/ext/cumo/narray/gen/narray_def.rb +0 -5
  59. data/ext/cumo/narray/gen/spec.rb +11 -14
  60. data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
  61. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +9 -9
  62. data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
  63. data/ext/cumo/narray/gen/tmpl/batch_norm.c +3 -3
  64. data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +3 -3
  65. data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +15 -5
  66. data/ext/cumo/narray/gen/tmpl/conv.c +88 -121
  67. data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +89 -81
  68. data/ext/cumo/narray/gen/tmpl/conv_transpose.c +88 -121
  69. data/ext/cumo/narray/gen/tmpl/cum.c +8 -7
  70. data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +7 -6
  71. data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +3 -3
  72. data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +22 -10
  73. data/ext/cumo/narray/gen/tmpl/gemm.c +11 -35
  74. data/ext/cumo/narray/gen/tmpl/layer_norm.c +91 -0
  75. data/ext/cumo/narray/gen/tmpl/layer_norm_kernel.cu +54 -0
  76. data/ext/cumo/narray/gen/tmpl/lib.c +1 -0
  77. data/ext/cumo/narray/gen/tmpl/lib_kernel.cu +1 -0
  78. data/ext/cumo/narray/gen/tmpl/median.c +1 -1
  79. data/ext/cumo/narray/gen/tmpl/poly.c +0 -3
  80. data/ext/cumo/narray/gen/tmpl/poly_kernel.cu +1 -1
  81. data/ext/cumo/narray/gen/tmpl/pooling_backward.c +2 -1
  82. data/ext/cumo/narray/gen/tmpl/pooling_forward.c +2 -1
  83. data/ext/cumo/narray/gen/tmpl/rand.c +9 -9
  84. data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +9 -9
  85. data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
  86. data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -7
  87. data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +6 -6
  88. data/ext/cumo/narray/gen/tmpl/softmax.c +56 -0
  89. data/ext/cumo/narray/gen/tmpl/softmax_kernel.cu +157 -0
  90. data/ext/cumo/narray/gen/tmpl/sort.c +9 -32
  91. data/ext/cumo/narray/gen/tmpl/sort_index.c +16 -95
  92. data/ext/cumo/narray/gen/tmpl/store_array.c +12 -9
  93. data/ext/cumo/narray/gen/tmpl_bit/mask.c +11 -11
  94. data/ext/cumo/narray/gen/tmpl_bit/store_array.c +5 -9
  95. data/ext/cumo/narray/gen/tmpl_bit/where.c +50 -11
  96. data/ext/cumo/narray/gen/tmpl_bit/where2.c +11 -12
  97. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +1 -2
  98. data/ext/cumo/narray/index.c +2 -1
  99. data/ext/cumo/narray/math.c +2 -0
  100. data/ext/cumo/narray/narray.c +27 -1
  101. data/ext/cumo/narray/ndloop.c +73 -4
  102. data/ext/cumo/narray/sort_kernel.cu +61 -41
  103. data/ext/cumo/narray/struct.c +3 -0
  104. data/lib/cumo/cuda/cudnn.rb +1 -1
  105. data/lib/cumo/narray/extra.rb +2 -2
  106. data/test/bfloat_test.rb +238 -0
  107. data/test/cuda/memory_pool_test.rb +8 -7
  108. data/test/cuda/runtime_test.rb +25 -0
  109. data/test/cudnn_test.rb +108 -24
  110. data/test/extra_test.rb +1 -1
  111. data/test/fused_test.rb +311 -0
  112. data/test/math_test.rb +77 -2
  113. data/test/narray_alt_coverage_test.rb +1 -0
  114. data/test/narray_test.rb +70 -10
  115. data/test/test_helper.rb +1 -0
  116. metadata +20 -4
  117. data/ext/cumo/narray/gen/tmpl/qsort.c +0 -205
@@ -26,17 +26,79 @@ get_int_out_size(int* int_out_size, VALUE out_size, size_t ndim, size_t* x_shape
26
26
  }
27
27
  }
28
28
 
29
+ // Everything here holds a descriptor or the workspace, and the two allocations
30
+ // below raise on their own when the device is full.
31
+ static VALUE
32
+ <%=c_iter%>_run(VALUE v)
33
+ {
34
+ cumo_cuda_cudnn_conv_run_t *r = (cumo_cuda_cudnn_conv_run_t*)v;
35
+ cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
36
+ <%=cudnn_scalar_t%> alpha = 1;
37
+ <%=cudnn_scalar_t%> beta = 0;
38
+ cudnnConvolutionBwdDataAlgoPerf_t perf_result;
39
+ size_t workspace_size;
40
+
41
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.x_desc, r->x_cont, cudnn_dtype);
42
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
43
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.y_desc, r->y, cudnn_dtype);
44
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
45
+ r->status = cumo_cuda_cudnn_CreateFilterDescriptor(&r->held.w_desc, r->w_cont, cudnn_dtype);
46
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
47
+ r->status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&r->held.conv_desc, r->ndim, r->int_stride, r->int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
48
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
49
+
50
+ // auto tune
51
+ r->status = cumo_cuda_cudnn_FindConvolutionBackwardDataAlgorithm(
52
+ &perf_result,
53
+ r->handle,
54
+ r->held.w_desc,
55
+ r->w_cont,
56
+ r->held.x_desc,
57
+ r->x_cont,
58
+ r->held.conv_desc,
59
+ r->held.y_desc,
60
+ r->y,
61
+ cumo_cuda_cudnn_max_workspace_size(),
62
+ r->int_stride,
63
+ r->int_pad,
64
+ r->ndim,
65
+ cudnn_dtype);
66
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
67
+ // The descriptor asked for a math type; use the one the search settled on.
68
+ r->status = cudnnSetConvolutionMathType(r->held.conv_desc, perf_result.mathType);
69
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
70
+
71
+ // The search may look at algorithms needing up to max_workspace_size,
72
+ // but only the one it picked has to be paid for.
73
+ workspace_size = perf_result.memory;
74
+ if (workspace_size > 0) r->held.workspace = cumo_cuda_runtime_malloc(workspace_size);
75
+ r->status = cudnnConvolutionBackwardData(
76
+ r->handle,
77
+ (void*)&alpha,
78
+ r->held.w_desc,
79
+ (void*)r->w_cont_ptr,
80
+ r->held.x_desc,
81
+ (void*)r->x_cont_ptr,
82
+ r->held.conv_desc,
83
+ perf_result.algo,
84
+ (void*)r->held.workspace,
85
+ workspace_size,
86
+ (void*)&beta,
87
+ r->held.y_desc,
88
+ (void*)r->y_ptr);
89
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
90
+
91
+ r->status = cumo_cuda_cudnn_AddBias(r, cudnn_dtype, &alpha);
92
+ return Qnil;
93
+ }
94
+
29
95
  // cover_all=true is not supported with CUDNN
30
96
  // dilation > 1 is not supported yet
31
97
  // x.conv(w, b: nil, stride: 1, pad: 0, out_size: nil, y: nil)
32
98
  static VALUE
33
99
  <%=c_func(-1)%>(int argc, VALUE argv[], VALUE self)
34
100
  {
35
- cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
36
- cudnnStatus_t status = 0;
37
- cudnnHandle_t handle = 0;
38
- <%=cudnn_scalar_t%> alpha = 1;
39
- <%=cudnn_scalar_t%> beta = 0;
101
+ cumo_cuda_cudnn_conv_run_t r;
40
102
 
41
103
  VALUE x=self, w, b, stride, pad, out_size, y;
42
104
  VALUE kw_hash = Qnil;
@@ -49,23 +111,11 @@ static VALUE
49
111
  size_t out_channels, batch_size;
50
112
 
51
113
  VALUE x_cont, w_cont;
52
- cudnnTensorDescriptor_t x_desc = 0;
53
- cudnnTensorDescriptor_t y_desc = 0;
54
- cudnnTensorDescriptor_t b_desc = 0;
55
- cudnnFilterDescriptor_t w_desc = 0;
56
- cudnnConvolutionDescriptor_t conv_desc = 0;
57
- char *x_cont_ptr, *w_cont_ptr, *y_ptr;
58
-
59
- cudnnConvolutionBwdDataAlgoPerf_t perf_result;
60
- cudnnConvolutionBwdDataAlgo_t algo;
61
- size_t max_workspace_size = cumo_cuda_cudnn_max_workspace_size();
62
- size_t workspace_size;
63
- char* workspace = 0;
64
-
65
114
  int int_stride[CUMO_NA_MAX_DIMENSION];
66
115
  int int_pad[CUMO_NA_MAX_DIMENSION];
67
116
  int int_out_size[CUMO_NA_MAX_DIMENSION];
68
117
 
118
+ memset(&r, 0, sizeof(r));
69
119
  rb_scan_args(argc, argv, "1:", &w, &kw_hash);
70
120
  rb_get_kwargs(kw_hash, kw_table, 0, 5, opts);
71
121
  b = cumo_option_value(opts[0], Qnil);
@@ -118,112 +168,29 @@ static VALUE
118
168
  x_cont = cumo_na_as_contiguous_array(x);
119
169
  w_cont = cumo_na_as_contiguous_array(w);
120
170
 
121
- x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
122
- w_cont_ptr = cumo_na_get_offset_pointer_for_read(w_cont);
123
- y_ptr = cumo_na_get_offset_pointer_for_write(y);
124
-
125
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
126
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
127
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&y_desc, y, cudnn_dtype);
128
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
129
- status = cumo_cuda_cudnn_CreateFilterDescriptor(&w_desc, w_cont, cudnn_dtype);
130
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
131
- status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&conv_desc, ndim, int_stride, int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
132
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
133
-
134
- handle = cumo_cuda_cudnn_handle();
135
-
136
- // auto tune
137
- status = cumo_cuda_cudnn_FindConvolutionBackwardDataAlgorithm(
138
- &perf_result,
139
- handle,
140
- w_desc,
141
- w_cont,
142
- x_desc,
143
- x_cont,
144
- conv_desc,
145
- y_desc,
146
- y,
147
- max_workspace_size,
148
- int_stride,
149
- int_pad,
150
- ndim,
151
- cudnn_dtype);
152
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
153
- // The descriptor asked for a math type; use the one the search settled on.
154
- status = cudnnSetConvolutionMathType(conv_desc, perf_result.mathType);
155
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
156
-
157
- algo = perf_result.algo;
158
- workspace_size = perf_result.memory;
159
-
160
- // The search may look at algorithms needing up to max_workspace_size,
161
- // but only the one it picked has to be paid for.
162
- if (workspace_size > 0) workspace = cumo_cuda_runtime_malloc(workspace_size);
163
- status = cudnnConvolutionBackwardData(
164
- handle,
165
- (void*)&alpha,
166
- w_desc,
167
- (void*)w_cont_ptr,
168
- x_desc,
169
- (void*)x_cont_ptr,
170
- conv_desc,
171
- algo,
172
- (void*)workspace,
173
- workspace_size,
174
- (void*)&beta,
175
- y_desc,
176
- (void*)y_ptr);
177
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
178
-
171
+ r.x_cont = x_cont;
172
+ r.w_cont = w_cont;
173
+ r.y = y;
174
+ r.x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
175
+ r.w_cont_ptr = cumo_na_get_offset_pointer_for_read(w_cont);
176
+ r.y_ptr = cumo_na_get_offset_pointer_for_write(y);
177
+ r.ndim = ndim;
178
+ r.int_stride = int_stride;
179
+ r.int_pad = int_pad;
180
+
181
+ // Taking the bias runs Ruby, which can raise, so it is settled before the
182
+ // ensure below has anything to give back.
179
183
  if (b != Qnil) {
180
- size_t new_shape[CUMO_NA_MAX_DIMENSION];
181
- VALUE b_cont;
182
- char* b_cont_ptr;
183
- cumo_narray_t *nb, *nb_cont;
184
- size_t *b_shape;
185
- int b_ndim;
186
-
187
- CUMO_CHECK_NARRAY_TYPE(b, cT);
188
- CumoGetNArray(b, nb);
189
- new_shape[0] = 1;
190
- new_shape[1] = nb->size;
191
- for (size_t i = 0; i < ndim; ++i) {
192
- new_shape[i + 2] = 1;
193
- }
194
- b_cont = cumo_na_as_contiguous_array(b);
195
- b_cont_ptr = cumo_na_get_offset_pointer_for_read(b_cont);
196
- CumoGetNArray(b_cont, nb_cont);
197
- b_shape = nb_cont->shape;
198
- b_ndim = nb_cont->ndim;
199
- // reshape b
200
- nb_cont->ndim = ndim + 2;
201
- nb_cont->shape = new_shape;
202
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&b_desc, b_cont, cudnn_dtype);
203
- // restore b.shape
204
- nb_cont->ndim = b_ndim;
205
- nb_cont->shape = b_shape;
206
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
207
-
208
- status = cudnnAddTensor(
209
- handle,
210
- (void*)&alpha,
211
- b_desc,
212
- (void*)b_cont_ptr,
213
- (void*)&alpha,
214
- y_desc,
215
- (void*)y_ptr);
216
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_TRANSPOSE_ERROR;
184
+ cumo_cuda_cudnn_check_input(b, cT, 1, &out_channels);
185
+ r.b_cont = cumo_na_as_contiguous_array(b);
186
+ r.b_cont_ptr = cumo_na_get_offset_pointer_for_read(r.b_cont);
217
187
  }
188
+ r.handle = cumo_cuda_cudnn_handle();
218
189
 
219
- CONV_TRANSPOSE_ERROR:
220
- if (x_desc) cudnnDestroyTensorDescriptor(x_desc);
221
- if (y_desc) cudnnDestroyTensorDescriptor(y_desc);
222
- if (b_desc) cudnnDestroyTensorDescriptor(b_desc);
223
- if (w_desc) cudnnDestroyFilterDescriptor(w_desc);
224
- if (conv_desc) cudnnDestroyConvolutionDescriptor(conv_desc);
225
- if (workspace) cumo_cuda_runtime_free(workspace);
226
- cumo_cuda_cudnn_check_status(status);
190
+ rb_ensure(<%=c_iter%>_run, (VALUE)&r, cumo_cuda_cudnn_release_conv_held, (VALUE)&r.held);
191
+ RB_GC_GUARD(r.b_cont);
192
+ cumo_cuda_cudnn_check_status(r.status);
193
+ cumo_cuda_runtime_check_status(r.held.wait_status);
227
194
 
228
195
  return y;
229
196
  }
@@ -28,24 +28,25 @@ static void
28
28
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%><%=j%>", "<%=type_name%>");
29
29
  cumo_cuda_runtime_device_synchronize();
30
30
 
31
- <% if is_half && name == 'cumsum' %>
32
- // The scan this stands in for carries float, and a running sum of halves
33
- // stops moving once it passes 2048, so the accumulator is float here too.
31
+ <% if !acc_type.empty? && name == 'cumsum' %>
32
+ // The scan this stands in for carries the accumulator type, and a running
33
+ // sum in the element type stops moving once the partial outgrows it, so this
34
+ // path carries it too.
34
35
  {
35
- float acc, fy;
36
+ <%=acc_type%> acc, fy;
36
37
 
37
38
  CUMO_GET_DATA_STRIDE(p1,s1,dtype,x);
38
- acc = cumo_half2float(x);
39
+ acc = <%=to_acc%>(x);
39
40
  CUMO_SET_DATA_STRIDE(p2,s2,dtype,x);
40
41
  for (i--; i--;) {
41
42
  CUMO_GET_DATA_STRIDE(p1,s1,dtype,y);
42
- fy = cumo_half2float(y);
43
+ fy = <%=to_acc%>(y);
43
44
  <% if j == '_nan' %>
44
45
  if (acc != acc) { acc = fy; } else if (fy == fy) { acc += fy; }
45
46
  <% else %>
46
47
  acc += fy;
47
48
  <% end %>
48
- x = cumo_float2half(acc);
49
+ x = <%=from_acc%>(acc);
49
50
  CUMO_SET_DATA_STRIDE(p2,s2,dtype,x);
50
51
  }
51
52
  }
@@ -1,18 +1,19 @@
1
1
  <% unless type_name == 'robject' %>
2
- <% widen = is_half && name == 'cumsum' %>
3
- <% cum_t = widen ? 'float' : 'dtype' %>
2
+ <% widen = !acc_type.empty? && name == 'cumsum' %>
3
+ <% cum_t = widen ? acc_type : 'dtype' %>
4
4
  <% if widen %>
5
5
 
6
- // A running sum of halves stops moving once it passes 2048, so the scan carries
7
- // float and each element is rounded as it is written.
6
+ // A running sum in the element type stops moving once the partial outgrows it,
7
+ // so the scan carries the accumulator type and each element is rounded as it is
8
+ // written.
8
9
  struct <%="cumo_thrust_#{name}_widen"%>
9
10
  {
10
- __host__ __device__ float operator()(dtype x) const { return cumo_half2float(x); }
11
+ __host__ __device__ <%=acc_type%> operator()(dtype x) const { return <%=to_acc%>(x); }
11
12
  };
12
13
 
13
14
  struct <%="cumo_thrust_#{name}_narrow"%>
14
15
  {
15
- __host__ __device__ dtype operator()(float x) const { return cumo_float2half(x); }
16
+ __host__ __device__ dtype operator()(<%=acc_type%> x) const { return <%=from_acc%>(x); }
16
17
  };
17
18
  <% end %>
18
19
  <% (is_float ? ["","_nan"] : [""]).each do |j| %>
@@ -97,18 +97,18 @@ static VALUE
97
97
  }
98
98
  y_ptr = cumo_na_get_offset_pointer_for_write(y);
99
99
 
100
+ mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
101
+ handle = cumo_cuda_cudnn_handle();
102
+
100
103
  status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
101
104
  if (status != CUDNN_STATUS_SUCCESS) goto FIXED_BATCH_NORM_ERROR;
102
105
 
103
- mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
104
106
  status = cumo_cuda_cudnn_CreateBNTensorDescriptor(&bn_desc, x_desc, mode);
105
107
  if (status != CUDNN_STATUS_SUCCESS) goto FIXED_BATCH_NORM_ERROR;
106
108
  // cuDNN derives the parameter descriptor as float for a half x and as x's
107
109
  // own type otherwise, and the checks above hold every parameter to exactly
108
110
  // that class, so none of them needs a cast.
109
111
 
110
- handle = cumo_cuda_cudnn_handle();
111
-
112
112
  status = cudnnBatchNormalizationForwardInference(
113
113
  handle,
114
114
  mode,
@@ -1,7 +1,5 @@
1
1
  <%= load_erb('real_accum').result(binding) %>
2
- <% acc = is_half ? 'float' : 'dtype' %>
3
- <% to_acc = is_half ? 'cumo_half2float' : '' %>
4
- <% from_acc = is_half ? 'cumo_float2half' : '' %>
2
+ <% acc = acc_type.empty? ? 'dtype' : acc_type %>
5
3
 
6
4
 
7
5
  #if defined(__cplusplus)
@@ -27,19 +25,33 @@ struct cumo_<%=type_name%>_moments_impl {
27
25
  __device__ void Reduce(Moments next, Moments& accum) {
28
26
  if (next.n == 0) { return; }
29
27
  if (accum.n == 0) { accum = next; return; }
28
+ // The two quotients share a denominator, and one reciprocal is much
29
+ // cheaper than two double divides on a GeForce card.
30
30
  <%=acc%> n = accum.n + next.n;
31
+ <%=acc%> r = <%=acc%>(1) / n;
31
32
  <%=acc%> delta = next.mean - accum.mean;
32
- accum.mean = accum.mean + delta * (next.n / n);
33
- accum.m2 += next.m2 + delta * delta * accum.n * next.n / n;
33
+ accum.mean = accum.mean + delta * (next.n * r);
34
+ accum.m2 += next.m2 + delta * delta * accum.n * next.n * r;
34
35
  accum.n = n;
35
36
  }
36
37
  };
37
38
 
38
- struct cumo_<%=type_name%>_var_impl : cumo_<%=type_name%>_moments_impl {
39
+ // An infinity has no deviation from a mean that is itself infinite, so a row
40
+ // holding one has a variance of NaN wherever the infinity falls in the tree.
41
+ // layer_norm shares the accumulator above and cannot use this, so it sits here
42
+ // rather than there.
43
+ struct cumo_<%=type_name%>_spread_impl : cumo_<%=type_name%>_moments_impl {
44
+ __device__ Moments MapIn(dtype in, int64_t /*index*/) {
45
+ <%=acc%> x = <%=to_acc%>(in);
46
+ return {1, x, isinf(x) ? <%=acc%>(NAN) : <%=acc%>(0)};
47
+ }
48
+ };
49
+
50
+ struct cumo_<%=type_name%>_var_impl : cumo_<%=type_name%>_spread_impl {
39
51
  __device__ rtype MapOut(Moments accum) { return <%=from_acc%>(accum.m2 / (accum.n - 1)); }
40
52
  };
41
53
 
42
- struct cumo_<%=type_name%>_stddev_impl : cumo_<%=type_name%>_moments_impl {
54
+ struct cumo_<%=type_name%>_stddev_impl : cumo_<%=type_name%>_spread_impl {
43
55
  __device__ rtype MapOut(Moments accum) { return <%=from_acc%>(sqrt(accum.m2 / (accum.n - 1))); }
44
56
  };
45
57
 
@@ -65,10 +77,10 @@ struct cumo_<%=type_name%>_rms_impl {
65
77
  // Reduce above already returns early on a zero count, so the moments never see
66
78
  // it, and mean and rms carry the count of what was left rather than the length
67
79
  // of the reduce axis.
68
- struct cumo_<%=type_name%>_moments_nan_impl : cumo_<%=type_name%>_moments_impl {
69
- __device__ Moments MapIn(dtype in, int64_t /*index*/) {
80
+ struct cumo_<%=type_name%>_moments_nan_impl : cumo_<%=type_name%>_spread_impl {
81
+ __device__ Moments MapIn(dtype in, int64_t index) {
70
82
  if (!not_nan(in)) { return {0, 0, 0}; }
71
- return {1, <%=to_acc%>(in), 0};
83
+ return cumo_<%=type_name%>_spread_impl::MapIn(in, index);
72
84
  }
73
85
  };
74
86
 
@@ -1,34 +1,10 @@
1
1
  <%
2
- func_prefix =
3
- case type_name
4
- when 'sfloat'
5
- 'S'
6
- when 'dfloat'
7
- 'D'
8
- when 'scomplex'
9
- 'C'
10
- when 'dcomplex'
11
- 'Z'
12
- end
13
- cutype =
14
- case type_name
15
- when 'hfloat'
16
- 'cumo_half'
17
- when 'sfloat'
18
- 'float'
19
- when 'dfloat'
20
- 'double'
21
- when 'scomplex'
22
- 'cuComplex'
23
- when 'dcomplex'
24
- 'cuDoubleComplex'
25
- end
26
- # cublasGemmStridedBatchedEx multiplies in half and accumulates in float, and
27
- # takes its scalars in the accumulator's type.
28
- scalar_t = is_half ? 'float' : 'dtype'
29
- num_to_scalar = is_half ? '(float)NUM2DBL' : 'm_num_to_data'
30
- scalar_one = is_half ? '1.0f' : 'm_one'
31
- scalar_zero = is_half ? '0.0f' : 'm_zero'
2
+ # cublasGemmStridedBatchedEx multiplies in the element type and accumulates
3
+ # in the wider one, and takes its scalars in the accumulator's type.
4
+ scalar_t = acc_type.empty? ? 'dtype' : acc_type
5
+ num_to_scalar = acc_type.empty? ? 'm_num_to_data' : "(#{acc_type})NUM2DBL"
6
+ scalar_one = acc_one
7
+ scalar_zero = acc_zero
32
8
  %>
33
9
 
34
10
  #define ROW_SIZE(na) ((na)->shape[(na)->ndim-2])
@@ -298,7 +274,7 @@ static void
298
274
 
299
275
  if (cumo_na_debug_flag) print_gemm_args(g, &a_layout, &b_layout, stridec, batch_count);
300
276
  handle = cumo_cuda_cublas_handle();
301
- <% if is_half %>
277
+ <% unless cublas_dtype.empty? %>
302
278
  status = cublasGemmStridedBatchedEx(
303
279
  handle,
304
280
  b_layout.trans,
@@ -308,23 +284,23 @@ static void
308
284
  g->k,
309
285
  &g->alpha,
310
286
  (const void*)(cumo_na_get_pointer_for_read(b_layout.a) + cumo_na_get_offset(b_layout.a)),
311
- CUDA_R_16F,
287
+ <%=cublas_dtype%>,
312
288
  b_layout.ld,
313
289
  b_layout.stride,
314
290
  (const void*)(cumo_na_get_pointer_for_read(a_layout.a) + cumo_na_get_offset(a_layout.a)),
315
- CUDA_R_16F,
291
+ <%=cublas_dtype%>,
316
292
  a_layout.ld,
317
293
  a_layout.stride,
318
294
  &g->beta,
319
295
  (void*)(cumo_na_get_pointer_for_write(c) + cumo_na_get_offset(c)),
320
- CUDA_R_16F,
296
+ <%=cublas_dtype%>,
321
297
  g->n,
322
298
  stridec,
323
299
  batch_count,
324
300
  CUBLAS_COMPUTE_32F,
325
301
  CUBLAS_GEMM_DEFAULT);
326
302
  <% else %>
327
- status = cublas<%=func_prefix%>gemmStridedBatched(
303
+ status = cublas<%=cublas_prefix%>gemmStridedBatched(
328
304
  handle,
329
305
  b_layout.trans,
330
306
  a_layout.trans,
@@ -0,0 +1,91 @@
1
+ void <%="cumo_#{c_iter}_kernel_launch"%>(char *px, char *pg, char *pb, char *py, uint64_t rows, uint64_t cols, double eps);
2
+
3
+ /*
4
+ Normalizes self along its last axis, then scales by gamma and shifts by beta.
5
+ Each row is taken on its own: y = (x - mean) / sqrt(var + eps) * gamma + beta,
6
+ where mean and var are of that row and var divides by the row length.
7
+ @overload layer_norm(gamma, beta, eps:1e-5)
8
+ @param [Cumo::<%=class_name%>] gamma scale, one-dimensional and as long as the last axis.
9
+ @param [Cumo::<%=class_name%>] beta shift, one-dimensional and as long as the last axis.
10
+ @param [Float] eps added to the variance before the square root.
11
+ Answers a new array: inplace! is not honoured.
12
+ @return [Cumo::<%=class_name%>] returns the normalized array, shaped like self.
13
+ */
14
+ static VALUE
15
+ <%=c_func(-1)%>(int argc, VALUE argv[], VALUE self)
16
+ {
17
+ VALUE x=self, gamma, beta, eps, y;
18
+ VALUE kw_hash = Qnil;
19
+ ID kw_table[] = {rb_intern("eps")};
20
+ VALUE opts[] = {Qundef};
21
+ VALUE x_cont, gamma_cont, beta_cont;
22
+ cumo_narray_t *nx, *ngamma, *nbeta, *ny;
23
+ char *x_ptr, *gamma_ptr, *beta_ptr, *y_ptr;
24
+ size_t cols, rows;
25
+ double double_eps = 1e-5;
26
+
27
+ rb_scan_args(argc, argv, "2:", &gamma, &beta, &kw_hash);
28
+ rb_get_kwargs(kw_hash, kw_table, 0, 1, opts);
29
+ eps = cumo_option_value(opts[0], Qnil);
30
+ if (eps != Qnil) {
31
+ double_eps = NUM2DBL(eps);
32
+ }
33
+
34
+ CUMO_CHECK_NARRAY_TYPE(x, cT);
35
+ CUMO_CHECK_NARRAY_TYPE(gamma, cT);
36
+ CUMO_CHECK_NARRAY_TYPE(beta, cT);
37
+
38
+ x_cont = cumo_na_as_contiguous_array(x);
39
+ gamma_cont = cumo_na_as_contiguous_array(gamma);
40
+ beta_cont = cumo_na_as_contiguous_array(beta);
41
+
42
+ CumoGetNArray(x_cont, nx);
43
+ if (nx->ndim < 1) {
44
+ rb_raise(cumo_na_eShapeError, "layer_norm needs an axis to normalize along");
45
+ }
46
+ y = cumo_na_new(cT, nx->ndim, nx->shape);
47
+
48
+ // Taking a pointer runs allocate, which is Ruby, so all four are taken
49
+ // before anything is measured and the checks below run over what is left.
50
+ // See row_method.h for why the pointers themselves have to be looked at
51
+ // again.
52
+ x_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
53
+ gamma_ptr = cumo_na_get_offset_pointer_for_read(gamma_cont);
54
+ beta_ptr = cumo_na_get_offset_pointer_for_read(beta_cont);
55
+ y_ptr = cumo_na_get_offset_pointer_for_write(y);
56
+
57
+ // cumo_na_as_contiguous_array answers what dup gave it, which a class is
58
+ // free to define, so what these carry is not what was checked above.
59
+ CUMO_CHECK_NARRAY_TYPE(x_cont, cT);
60
+ CUMO_CHECK_NARRAY_TYPE(gamma_cont, cT);
61
+ CUMO_CHECK_NARRAY_TYPE(beta_cont, cT);
62
+ CumoGetNArray(x_cont, nx);
63
+ CumoGetNArray(gamma_cont, ngamma);
64
+ CumoGetNArray(beta_cont, nbeta);
65
+ CumoGetNArray(y, ny);
66
+
67
+ if (nx->ndim < 1) {
68
+ rb_raise(cumo_na_eShapeError, "layer_norm needs an axis to normalize along");
69
+ }
70
+ cols = nx->shape[nx->ndim - 1];
71
+ if (ngamma->ndim != 1 || ngamma->shape[0] != cols) {
72
+ rb_raise(cumo_na_eShapeError, "gamma must be 1-dimensional and %"SZF"u long", cols);
73
+ }
74
+ if (nbeta->ndim != 1 || nbeta->shape[0] != cols) {
75
+ rb_raise(cumo_na_eShapeError, "beta must be 1-dimensional and %"SZF"u long", cols);
76
+ }
77
+ CUMO_ROW_CHECK_SAME_SHAPE(ny, nx);
78
+ CUMO_ROW_CHECK_READ_BUFFER(x_cont, x_ptr, "self");
79
+ CUMO_ROW_CHECK_READ_BUFFER(gamma_cont, gamma_ptr, "gamma");
80
+ CUMO_ROW_CHECK_READ_BUFFER(beta_cont, beta_ptr, "beta");
81
+ CUMO_ROW_CHECK_WRITE_BUFFER(y, y_ptr, "the result");
82
+
83
+ if (nx->size == 0) {
84
+ return y;
85
+ }
86
+ rows = nx->size / cols;
87
+
88
+ <%="cumo_#{c_iter}_kernel_launch"%>(x_ptr, gamma_ptr, beta_ptr, y_ptr, (uint64_t)rows, (uint64_t)cols, double_eps);
89
+
90
+ return y;
91
+ }
@@ -0,0 +1,54 @@
1
+ <%
2
+ acc = acc_type.empty? ? 'dtype' : acc_type
3
+ %>
4
+
5
+ #if defined(__cplusplus)
6
+ #if 0
7
+ { /* satisfy cc-mode */
8
+ #endif
9
+ } /* extern "C" { */
10
+ #endif
11
+
12
+ struct <%="cumo_#{c_iter}_stats"%> {
13
+ <%=acc%> mean;
14
+ <%=acc%> rstd;
15
+ };
16
+
17
+ // Layer norm divides the sum of squared deviations by n, where cumo's var
18
+ // divides by n - 1, so the two cannot share a MapOut however much of the
19
+ // accumulation they share.
20
+ struct <%="cumo_#{c_iter}_impl"%> : cumo_<%=type_name%>_moments_impl {
21
+ <%=acc%> eps;
22
+ __device__ <%="cumo_#{c_iter}_stats"%> MapOut(Moments accum) {
23
+ return {accum.mean, <%=acc%>(1) / sqrt(accum.m2 / accum.n + eps)};
24
+ }
25
+ };
26
+
27
+ struct <%="cumo_#{c_iter}_apply"%> {
28
+ const dtype* g;
29
+ const dtype* b;
30
+ __device__ dtype operator()(dtype x, uint64_t col, <%="cumo_#{c_iter}_stats"%> st) const {
31
+ <%=acc%> v = (<%=to_acc%>(x) - st.mean) * st.rstd;
32
+ return <%=from_acc%>(v * <%=to_acc%>(g[col]) + <%=to_acc%>(b[col]));
33
+ }
34
+ };
35
+
36
+ #if defined(__cplusplus)
37
+ extern "C" {
38
+ #if 0
39
+ } /* satisfy cc-mode */
40
+ #endif
41
+ #endif
42
+
43
+ void <%="cumo_#{c_iter}_kernel_launch"%>(
44
+ char *px, char *pg, char *pb, char *py,
45
+ uint64_t rows, uint64_t cols, double eps)
46
+ {
47
+ <%="cumo_#{c_iter}_impl"%> impl;
48
+ <%="cumo_#{c_iter}_apply"%> apply;
49
+
50
+ impl.eps = (<%=acc%>)eps;
51
+ apply.g = (const dtype*)pg;
52
+ apply.b = (const dtype*)pb;
53
+ cumo_row_reduce_apply<dtype>(px, py, rows, cols, impl, apply);
54
+ }
@@ -15,6 +15,7 @@
15
15
  #include "SFMT.h"
16
16
  #include "cumo/cuda/memory_pool.h"
17
17
  #include "cumo/cuda/runtime.h"
18
+ #include "cumo/row_method.h"
18
19
  <% unless type_name == 'robject' %>
19
20
  #include "cumo/indexer.h"
20
21
  <% end %>
@@ -2,6 +2,7 @@
2
2
  <% unless type_name == 'robject' %>
3
3
  #include "cumo/indexer.h"
4
4
  #include "cumo/reduce_kernel.h"
5
+ #include "cumo/row_kernel.h"
5
6
  <% end %>
6
7
  #include <<%="cumo/types/#{type_name}_kernel.h"%>>
7
8
 
@@ -28,7 +28,7 @@ static void
28
28
  <%=name%> of self.
29
29
  <% if is_float %>
30
30
  @overload <%=name%>(axis:nil, keepdims:false, nan:false)
31
- @param [TrueClass] nan (keyword) If true, propagete NaN. If false, ignore NaN.
31
+ @param [TrueClass] nan (keyword) If true, propagate NaN. If false, ignore NaN.
32
32
  <% else %>
33
33
  @overload <%=name%>(axis:nil, keepdims:false)
34
34
  <% end %>
@@ -1,8 +1,6 @@
1
1
  <% unless type_name == 'robject' %>
2
2
  void <%="cumo_#{c_iter}_kernel_launch"%>(cumo_na_iarray_t* x, cumo_na_iarray_t* coef, int ncoef, cumo_na_iarray_t* z, cumo_na_indexer_t* indexer);
3
3
 
4
- // Horner over every element in one launch. The host loop this replaces ran
5
- // once per element, each time behind a cudaDeviceSynchronize.
6
4
  static void
7
5
  <%=c_iter%>_kernel(cumo_na_loop_t *const lp)
8
6
  {
@@ -27,7 +25,6 @@ static void
27
25
  dtype x, y, a;
28
26
 
29
27
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
30
- cumo_cuda_runtime_device_synchronize();
31
28
  n = lp->narg - 2;
32
29
  x = *(dtype*)CUMO_NDL_PTR(lp,0);
33
30
  y = *(dtype*)CUMO_NDL_PTR(lp,n);
@@ -40,7 +40,7 @@ void <%="cumo_#{c_iter}_kernel_launch"%>(cumo_na_iarray_t* x, cumo_na_iarray_t*
40
40
  <%="cumo_#{c_iter}_kernel_dim"%><<<grid_dim, block_dim>>>(*x, d_coef, ncoef, *z, *indexer);
41
41
  break;
42
42
  }
43
- cumo_cuda_runtime_check_kernel_launch();
43
+ cumo_check_launch_holding(d_coef);
44
44
  cumo_cuda_runtime_free((char*)d_coef);
45
45
  }
46
46
  <% end %>