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
@@ -59,7 +59,7 @@ if (is_float || is_complex) && !is_object
59
59
  def_id "gemm"
60
60
  end
61
61
  # cudnn
62
- if is_float && !is_complex && !is_object
62
+ if is_float && !is_complex && !is_object && !cudnn_dtype.empty?
63
63
  def_id "conv"
64
64
  def_id "conv_transpose"
65
65
  def_id "conv_grad_w"
@@ -128,6 +128,7 @@ def_method "store" do
128
128
  store_from "SComplex", "cumo_scomplex", "m_from_scomplex"
129
129
  end
130
130
  store_from "HFloat", "cumo_half", "m_from_half"
131
+ store_from "BFloat", "cumo_bfloat", "m_from_bfloat"
131
132
  store_from "DFloat", "double", "m_from_real"
132
133
  store_from "SFloat", "float", "m_from_real"
133
134
  store_from "Int64", "int64_t", "m_from_int64"
@@ -367,7 +368,7 @@ if (is_float || is_complex) && !is_object
367
368
  end
368
369
 
369
370
  # cudnn
370
- if is_float && !is_complex && !is_object
371
+ if is_float && !is_complex && !is_object && !cudnn_dtype.empty?
371
372
  def_method "conv"
372
373
  def_method "conv_transpose" # conv_backward_data
373
374
  def_method "conv_grad_w" # conv_backward_filter
@@ -378,6 +379,12 @@ if is_float && !is_complex && !is_object
378
379
  def_method "pooling_backward"
379
380
  end
380
381
 
382
+ # fused
383
+ if is_float && !is_complex && !is_object
384
+ def_method "layer_norm"
385
+ def_method "softmax"
386
+ end
387
+
381
388
  # rmsdev
382
389
  # prod
383
390
 
@@ -400,19 +407,7 @@ end
400
407
  def_method "poly"
401
408
 
402
409
  if is_comparable && !is_object
403
- if is_float
404
- qsort type_name, "dtype", "*(dtype*)", "_prnan"
405
- qsort type_name, "dtype", "*(dtype*)", "_ignan"
406
- else
407
- qsort type_name, "dtype", "*(dtype*)"
408
- end
409
410
  def_method "sort"
410
- if is_float
411
- qsort type_name + "_index", "dtype*", "**(dtype**)", "_prnan"
412
- qsort type_name + "_index", "dtype*", "**(dtype**)", "_ignan"
413
- else
414
- qsort type_name + "_index", "dtype*", "**(dtype**)"
415
- end
416
411
  def_method "sort_index"
417
412
  def_method "median"
418
413
  end
@@ -461,6 +456,8 @@ def_module do
461
456
  math "hypot", 2
462
457
  math "erf"
463
458
  math "erfc"
459
+ math "gelu"
460
+ math "gelu_tanh"
464
461
  math "log1p"
465
462
  math "expm1"
466
463
  math "ldexp", 2
@@ -38,7 +38,7 @@ static void
38
38
  <%=name%>. Returns an index of the <%=name[3..5]%>imum value along the axis. See also `<%=name[3..5]%>_index`.
39
39
  <% if is_float %>
40
40
  @overload <%=name%>(axis:nil, nan:false)
41
- @param [TrueClass] nan If true, apply NaN-aware algorithm (return NaN posision if exist).
41
+ @param [TrueClass] nan If true, apply NaN-aware algorithm (return NaN position if exist).
42
42
  <% else %>
43
43
  @overload <%=name%>(axis:nil)
44
44
  <% end %>
@@ -14,19 +14,19 @@
14
14
 
15
15
  // The product is never an array of its own: MapIn takes one element of each
16
16
  // operand, which is what a zip reduction hands it. The accumulator stays dtype
17
- // rather than widening, as the host loop it replaces did, except for half,
18
- // which cannot carry a sum of more than a couple of thousand terms.
17
+ // rather than widening, as the host loop it replaces did, except for the
18
+ // types that set an accumulator of their own.
19
19
  struct <%="cumo_#{type_name}_#{name}#{nan}_impl"%> {
20
- <% if is_half %>
21
- __device__ float Identity(int64_t /*index*/) { return 0.0f; }
22
- __device__ float MapIn(dtype x, dtype y, int64_t /*index*/) {
20
+ <% unless acc_type.empty? %>
21
+ __device__ <%=acc_type%> Identity(int64_t /*index*/) { return <%=acc_zero%>; }
22
+ __device__ <%=acc_type%> MapIn(dtype x, dtype y, int64_t /*index*/) {
23
23
  <% if nan == '_nan' %>
24
- if (!not_nan(x) || !not_nan(y)) { return 0.0f; }
24
+ if (!not_nan(x) || !not_nan(y)) { return <%=acc_zero%>; }
25
25
  <% end %>
26
- return cumo_half2float(x) * cumo_half2float(y);
26
+ return <%=to_acc%>(x) * <%=to_acc%>(y);
27
27
  }
28
- __device__ void Reduce(float next, float& accum) { accum = next + accum; }
29
- __device__ dtype MapOut(float accum) { return cumo_float2half(accum); }
28
+ __device__ void Reduce(<%=acc_type%> next, <%=acc_type%>& accum) { accum = next + accum; }
29
+ __device__ dtype MapOut(<%=acc_type%> accum) { return <%=from_acc%>(accum); }
30
30
  <% else %>
31
31
  __device__ dtype Identity(int64_t /*index*/) { return m_zero; }
32
32
  __device__ dtype MapIn(dtype x, dtype y, int64_t /*index*/) {
@@ -39,7 +39,7 @@ static void
39
39
  <%=name%>. Return an index of result.
40
40
  <% if is_float %>
41
41
  @overload <%=name%>(axis:nil, nan:false)
42
- @param [TrueClass] nan If true, apply NaN-aware algorithm (return NaN posision if exist).
42
+ @param [TrueClass] nan If true, apply NaN-aware algorithm (return NaN position if exist).
43
43
  <% else %>
44
44
  @overload <%=name%>(axis:nil)
45
45
  <% end %>
@@ -148,18 +148,18 @@ static VALUE
148
148
  }
149
149
  y_ptr = cumo_na_get_offset_pointer_for_write(y);
150
150
 
151
+ mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
152
+ handle = cumo_cuda_cudnn_handle();
153
+
151
154
  status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
152
155
  if (status != CUDNN_STATUS_SUCCESS) goto BATCH_NORM_ERROR;
153
156
 
154
- mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
155
157
  status = cumo_cuda_cudnn_CreateBNTensorDescriptor(&bn_desc, x_desc, mode);
156
158
  if (status != CUDNN_STATUS_SUCCESS) goto BATCH_NORM_ERROR;
157
159
  // cuDNN derives the parameter descriptor as float for a half x and as x's
158
160
  // own type otherwise, and the checks above hold every parameter to exactly
159
161
  // that class, so none of them needs a cast.
160
162
 
161
- handle = cumo_cuda_cudnn_handle();
162
-
163
163
  status = cudnnBatchNormalizationForwardTraining(
164
164
  handle,
165
165
  mode,
@@ -133,18 +133,18 @@ static VALUE
133
133
  }
134
134
  gbeta_ptr = cumo_na_get_offset_pointer_for_write(gbeta);
135
135
 
136
+ mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
137
+ handle = cumo_cuda_cudnn_handle();
138
+
136
139
  status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
137
140
  if (status != CUDNN_STATUS_SUCCESS) goto BATCH_NORM_BACKWARD_ERROR;
138
141
 
139
- mode = cumo_cuda_cudnn_GetBatchNormMode(axis_ndim, int_axis);
140
142
  status = cumo_cuda_cudnn_CreateBNTensorDescriptor(&bn_desc, x_desc, mode);
141
143
  if (status != CUDNN_STATUS_SUCCESS) goto BATCH_NORM_BACKWARD_ERROR;
142
144
  // cuDNN derives the parameter descriptor as float for a half x and as x's
143
145
  // own type otherwise, and the checks above hold every parameter to exactly
144
146
  // that class, so none of them needs a cast.
145
147
 
146
- handle = cumo_cuda_cudnn_handle();
147
-
148
148
  status = cudnnBatchNormalizationBackward(
149
149
  handle,
150
150
  mode,
@@ -43,11 +43,21 @@ struct cumo_<%=type_name%>_moments_impl {
43
43
  }
44
44
  };
45
45
 
46
- struct cumo_<%=type_name%>_var_impl : cumo_<%=type_name%>_moments_impl {
46
+ // An infinity has no deviation from a mean that is itself infinite, so a row
47
+ // holding one has a variance of NaN wherever the infinity falls in the tree.
48
+ // It sits below the accumulator so that the forms that cannot use it, as the
49
+ // real layer_norm cannot, do not pay for it.
50
+ struct cumo_<%=type_name%>_spread_impl : cumo_<%=type_name%>_moments_impl {
51
+ __device__ Moments MapIn(dtype in, int64_t /*index*/) {
52
+ return {1, in, c_isinf(in) ? (rtype)NAN : (rtype)0};
53
+ }
54
+ };
55
+
56
+ struct cumo_<%=type_name%>_var_impl : cumo_<%=type_name%>_spread_impl {
47
57
  __device__ rtype MapOut(Moments accum) { return accum.m2 / (accum.n - 1); }
48
58
  };
49
59
 
50
- struct cumo_<%=type_name%>_stddev_impl : cumo_<%=type_name%>_moments_impl {
60
+ struct cumo_<%=type_name%>_stddev_impl : cumo_<%=type_name%>_spread_impl {
51
61
  __device__ rtype MapOut(Moments accum) { return r_sqrt(accum.m2 / (accum.n - 1)); }
52
62
  };
53
63
 
@@ -87,10 +97,10 @@ struct cumo_<%=type_name%>_prod_nan_impl {
87
97
  __device__ <%=dtype%> MapOut(<%=dtype%> accum) { return accum; }
88
98
  };
89
99
 
90
- struct cumo_<%=type_name%>_moments_nan_impl : cumo_<%=type_name%>_moments_impl {
91
- __device__ Moments MapIn(dtype in, int64_t /*index*/) {
100
+ struct cumo_<%=type_name%>_moments_nan_impl : cumo_<%=type_name%>_spread_impl {
101
+ __device__ Moments MapIn(dtype in, int64_t index) {
92
102
  if (!not_nan(in)) { return {0, m_zero, 0}; }
93
- return {1, in, 0};
103
+ return cumo_<%=type_name%>_spread_impl::MapIn(in, index);
94
104
  }
95
105
  };
96
106
 
@@ -1,17 +1,79 @@
1
1
  #ifdef CUDNN_FOUND
2
2
 
3
3
 
4
+ // Everything here holds a descriptor or the workspace, and the two allocations
5
+ // below raise on their own when the device is full.
6
+ static VALUE
7
+ <%=c_iter%>_run(VALUE v)
8
+ {
9
+ cumo_cuda_cudnn_conv_run_t *r = (cumo_cuda_cudnn_conv_run_t*)v;
10
+ cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
11
+ <%=cudnn_scalar_t%> alpha = 1;
12
+ <%=cudnn_scalar_t%> beta = 0;
13
+ cudnnConvolutionFwdAlgoPerf_t perf_result;
14
+ size_t workspace_size;
15
+
16
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.x_desc, r->x_cont, cudnn_dtype);
17
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
18
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.y_desc, r->y, cudnn_dtype);
19
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
20
+ r->status = cumo_cuda_cudnn_CreateFilterDescriptor(&r->held.w_desc, r->w_cont, cudnn_dtype);
21
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
22
+ r->status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&r->held.conv_desc, r->ndim, r->int_stride, r->int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
23
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
24
+
25
+ // auto tune
26
+ r->status = cumo_cuda_cudnn_FindConvolutionForwardAlgorithm(
27
+ &perf_result,
28
+ r->handle,
29
+ r->held.x_desc,
30
+ r->x_cont,
31
+ r->held.w_desc,
32
+ r->w_cont,
33
+ r->held.conv_desc,
34
+ r->held.y_desc,
35
+ r->y,
36
+ cumo_cuda_cudnn_max_workspace_size(),
37
+ r->int_stride,
38
+ r->int_pad,
39
+ r->ndim,
40
+ cudnn_dtype);
41
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
42
+ // The descriptor asked for a math type; use the one the search settled on.
43
+ r->status = cudnnSetConvolutionMathType(r->held.conv_desc, perf_result.mathType);
44
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
45
+
46
+ // The search may look at algorithms needing up to max_workspace_size,
47
+ // but only the one it picked has to be paid for.
48
+ workspace_size = perf_result.memory;
49
+ if (workspace_size > 0) r->held.workspace = cumo_cuda_runtime_malloc(workspace_size);
50
+ r->status = cudnnConvolutionForward(
51
+ r->handle,
52
+ (void*)&alpha,
53
+ r->held.x_desc,
54
+ (void*)r->x_cont_ptr,
55
+ r->held.w_desc,
56
+ (void*)r->w_cont_ptr,
57
+ r->held.conv_desc,
58
+ perf_result.algo,
59
+ (void*)r->held.workspace,
60
+ workspace_size,
61
+ (void*)&beta,
62
+ r->held.y_desc,
63
+ (void*)r->y_ptr);
64
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
65
+
66
+ r->status = cumo_cuda_cudnn_AddBias(r, cudnn_dtype, &alpha);
67
+ return Qnil;
68
+ }
69
+
4
70
  // cover_all=true is not supported with CUDNN
5
71
  // dilation > 1 is not supported yet
6
72
  // x.conv(w, b: nil, stride: 1, pad: 0, y: nil)
7
73
  static VALUE
8
74
  <%=c_func(-1)%>(int argc, VALUE argv[], VALUE self)
9
75
  {
10
- cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
11
- cudnnStatus_t status = 0;
12
- cudnnHandle_t handle = 0;
13
- <%=cudnn_scalar_t%> alpha = 1;
14
- <%=cudnn_scalar_t%> beta = 0;
76
+ cumo_cuda_cudnn_conv_run_t r;
15
77
 
16
78
  VALUE x=self, w, b, stride, pad, y;
17
79
  VALUE kw_hash = Qnil;
@@ -24,22 +86,10 @@ static VALUE
24
86
  size_t out_channels, batch_size;
25
87
 
26
88
  VALUE x_cont, w_cont;
27
- cudnnTensorDescriptor_t x_desc = 0;
28
- cudnnTensorDescriptor_t y_desc = 0;
29
- cudnnTensorDescriptor_t b_desc = 0;
30
- cudnnFilterDescriptor_t w_desc = 0;
31
- cudnnConvolutionDescriptor_t conv_desc = 0;
32
- char *x_cont_ptr, *w_cont_ptr, *y_ptr;
33
-
34
- cudnnConvolutionFwdAlgoPerf_t perf_result;
35
- cudnnConvolutionFwdAlgo_t algo;
36
- size_t max_workspace_size = cumo_cuda_cudnn_max_workspace_size();
37
- size_t workspace_size;
38
- char* workspace = 0;
39
-
40
89
  int int_stride[CUMO_NA_MAX_DIMENSION];
41
90
  int int_pad[CUMO_NA_MAX_DIMENSION];
42
91
 
92
+ memset(&r, 0, sizeof(r));
43
93
  rb_scan_args(argc, argv, "1:", &w, &kw_hash);
44
94
  rb_get_kwargs(kw_hash, kw_table, 0, 4, opts);
45
95
  stride = cumo_option_value(opts[0], Qnil);
@@ -91,112 +141,29 @@ static VALUE
91
141
  x_cont = cumo_na_as_contiguous_array(x);
92
142
  w_cont = cumo_na_as_contiguous_array(w);
93
143
 
94
- x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
95
- w_cont_ptr = cumo_na_get_offset_pointer_for_read(w_cont);
96
- y_ptr = cumo_na_get_offset_pointer_for_write(y);
97
-
98
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
99
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
100
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&y_desc, y, cudnn_dtype);
101
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
102
- status = cumo_cuda_cudnn_CreateFilterDescriptor(&w_desc, w_cont, cudnn_dtype);
103
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
104
- status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&conv_desc, ndim, int_stride, int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
105
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
106
-
107
- handle = cumo_cuda_cudnn_handle();
108
-
109
- // auto tune
110
- status = cumo_cuda_cudnn_FindConvolutionForwardAlgorithm(
111
- &perf_result,
112
- handle,
113
- x_desc,
114
- x_cont,
115
- w_desc,
116
- w_cont,
117
- conv_desc,
118
- y_desc,
119
- y,
120
- max_workspace_size,
121
- int_stride,
122
- int_pad,
123
- ndim,
124
- cudnn_dtype);
125
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
126
- // The descriptor asked for a math type; use the one the search settled on.
127
- status = cudnnSetConvolutionMathType(conv_desc, perf_result.mathType);
128
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
129
-
130
- algo = perf_result.algo;
131
- workspace_size = perf_result.memory;
132
-
133
- // The search may look at algorithms needing up to max_workspace_size,
134
- // but only the one it picked has to be paid for.
135
- if (workspace_size > 0) workspace = cumo_cuda_runtime_malloc(workspace_size);
136
- status = cudnnConvolutionForward(
137
- handle,
138
- (void*)&alpha,
139
- x_desc,
140
- (void*)x_cont_ptr,
141
- w_desc,
142
- (void*)w_cont_ptr,
143
- conv_desc,
144
- algo,
145
- (void*)workspace,
146
- workspace_size,
147
- (void*)&beta,
148
- y_desc,
149
- (void*)y_ptr);
150
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
151
-
144
+ r.x_cont = x_cont;
145
+ r.w_cont = w_cont;
146
+ r.y = y;
147
+ r.x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
148
+ r.w_cont_ptr = cumo_na_get_offset_pointer_for_read(w_cont);
149
+ r.y_ptr = cumo_na_get_offset_pointer_for_write(y);
150
+ r.ndim = ndim;
151
+ r.int_stride = int_stride;
152
+ r.int_pad = int_pad;
153
+
154
+ // Taking the bias runs Ruby, which can raise, so it is settled before the
155
+ // ensure below has anything to give back.
152
156
  if (b != Qnil) {
153
- size_t new_shape[CUMO_NA_MAX_DIMENSION];
154
- VALUE b_cont;
155
- char* b_cont_ptr;
156
- cumo_narray_t *nb, *nb_cont;
157
- size_t *b_shape;
158
- int b_ndim;
159
-
160
- CUMO_CHECK_NARRAY_TYPE(b, cT);
161
- CumoGetNArray(b, nb);
162
- new_shape[0] = 1;
163
- new_shape[1] = nb->size;
164
- for (size_t i = 0; i < ndim; ++i) {
165
- new_shape[i + 2] = 1;
166
- }
167
- b_cont = cumo_na_as_contiguous_array(b);
168
- b_cont_ptr = cumo_na_get_offset_pointer_for_read(b_cont);
169
- CumoGetNArray(b_cont, nb_cont);
170
- b_shape = nb_cont->shape;
171
- b_ndim = nb_cont->ndim;
172
- // reshape b
173
- nb_cont->ndim = ndim + 2;
174
- nb_cont->shape = new_shape;
175
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&b_desc, b_cont, cudnn_dtype);
176
- // restore b.shape
177
- nb_cont->ndim = b_ndim;
178
- nb_cont->shape = b_shape;
179
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
180
-
181
- status = cudnnAddTensor(
182
- handle,
183
- (void*)&alpha,
184
- b_desc,
185
- (void*)b_cont_ptr,
186
- (void*)&alpha,
187
- y_desc,
188
- (void*)y_ptr);
189
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_ERROR;
157
+ cumo_cuda_cudnn_check_input(b, cT, 1, &out_channels);
158
+ r.b_cont = cumo_na_as_contiguous_array(b);
159
+ r.b_cont_ptr = cumo_na_get_offset_pointer_for_read(r.b_cont);
190
160
  }
161
+ r.handle = cumo_cuda_cudnn_handle();
191
162
 
192
- CONV_ERROR:
193
- if (x_desc) cudnnDestroyTensorDescriptor(x_desc);
194
- if (y_desc) cudnnDestroyTensorDescriptor(y_desc);
195
- if (b_desc) cudnnDestroyTensorDescriptor(b_desc);
196
- if (w_desc) cudnnDestroyFilterDescriptor(w_desc);
197
- if (conv_desc) cudnnDestroyConvolutionDescriptor(conv_desc);
198
- if (workspace) cumo_cuda_runtime_free(workspace);
199
- cumo_cuda_cudnn_check_status(status);
163
+ rb_ensure(<%=c_iter%>_run, (VALUE)&r, cumo_cuda_cudnn_release_conv_held, (VALUE)&r.held);
164
+ RB_GC_GUARD(r.b_cont);
165
+ cumo_cuda_cudnn_check_status(r.status);
166
+ cumo_cuda_runtime_check_status(r.held.wait_status);
200
167
 
201
168
  return y;
202
169
  }
@@ -11,16 +11,85 @@ cumo_cuda_cudnn_get_sizet_ary(size_t *sizet_ary, VALUE ary, size_t ndim)
11
11
  }
12
12
  }
13
13
 
14
- // cover_all=true is not supported with CUDNN
15
- // gw = x.conv_grad_w(gy, w_shape, stride: 1, pad: 0, gw: nil)
14
+ typedef struct {
15
+ cumo_cuda_cudnn_conv_held_t held;
16
+ cudnnHandle_t handle;
17
+ VALUE x_cont, gy_cont, gw;
18
+ char *x_cont_ptr, *gy_cont_ptr, *gw_ptr;
19
+ size_t ndim;
20
+ int *int_stride, *int_pad;
21
+ cudnnStatus_t status;
22
+ } <%=c_iter%>_run_t;
23
+
24
+ // Everything here holds a descriptor or the workspace, and the two allocations
25
+ // below raise on their own when the device is full.
16
26
  static VALUE
17
- <%=c_func(-1)%>(int argc, VALUE argv[], VALUE self)
27
+ <%=c_iter%>_run(VALUE v)
18
28
  {
29
+ <%=c_iter%>_run_t *r = (<%=c_iter%>_run_t*)v;
19
30
  cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
20
- cudnnStatus_t status = 0;
21
- cudnnHandle_t handle = 0;
22
31
  <%=cudnn_scalar_t%> one = 1;
23
32
  <%=cudnn_scalar_t%> zero = 0;
33
+ cudnnConvolutionBwdFilterAlgoPerf_t perf_result;
34
+ size_t workspace_size;
35
+
36
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.x_desc, r->x_cont, cudnn_dtype);
37
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
38
+ r->status = cumo_cuda_cudnn_CreateTensorDescriptor(&r->held.y_desc, r->gy_cont, cudnn_dtype);
39
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
40
+ r->status = cumo_cuda_cudnn_CreateFilterDescriptor(&r->held.w_desc, r->gw, cudnn_dtype);
41
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
42
+ r->status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&r->held.conv_desc, r->ndim, r->int_stride, r->int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
43
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
44
+
45
+ // auto tune
46
+ r->status = cumo_cuda_cudnn_FindConvolutionBackwardFilterAlgorithm(
47
+ &perf_result,
48
+ r->handle,
49
+ r->held.x_desc,
50
+ r->x_cont,
51
+ r->held.y_desc,
52
+ r->gy_cont,
53
+ r->held.conv_desc,
54
+ r->held.w_desc,
55
+ r->gw,
56
+ cumo_cuda_cudnn_max_workspace_size(),
57
+ r->int_stride,
58
+ r->int_pad,
59
+ r->ndim,
60
+ cudnn_dtype);
61
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
62
+ // The descriptor asked for a math type; use the one the search settled on.
63
+ r->status = cudnnSetConvolutionMathType(r->held.conv_desc, perf_result.mathType);
64
+ if (r->status != CUDNN_STATUS_SUCCESS) return Qnil;
65
+
66
+ // The search may look at algorithms needing up to max_workspace_size,
67
+ // but only the one it picked has to be paid for.
68
+ workspace_size = perf_result.memory;
69
+ if (workspace_size > 0) r->held.workspace = cumo_cuda_runtime_malloc(workspace_size);
70
+ r->status = cudnnConvolutionBackwardFilter(
71
+ r->handle,
72
+ (void*)&one,
73
+ r->held.x_desc,
74
+ (void*)r->x_cont_ptr,
75
+ r->held.y_desc,
76
+ (void*)r->gy_cont_ptr,
77
+ r->held.conv_desc,
78
+ perf_result.algo,
79
+ (void*)r->held.workspace,
80
+ workspace_size,
81
+ (void*)&zero,
82
+ r->held.w_desc,
83
+ (void*)r->gw_ptr);
84
+ return Qnil;
85
+ }
86
+
87
+ // cover_all=true is not supported with CUDNN
88
+ // gw = x.conv_grad_w(gy, w_shape, stride: 1, pad: 0, gw: nil)
89
+ static VALUE
90
+ <%=c_func(-1)%>(int argc, VALUE argv[], VALUE self)
91
+ {
92
+ <%=c_iter%>_run_t r;
24
93
 
25
94
  VALUE x=self, gy, w_shape, stride, pad, gw;
26
95
  VALUE kw_hash = Qnil;
@@ -31,22 +100,12 @@ static VALUE
31
100
  cumo_narray_t *nx, *ngy;
32
101
 
33
102
  VALUE x_cont, gy_cont;
34
- cudnnTensorDescriptor_t x_desc = 0;
35
- cudnnTensorDescriptor_t gy_desc = 0;
36
- cudnnConvolutionDescriptor_t conv_desc = 0;
37
- cudnnFilterDescriptor_t gw_desc = 0;
38
- char *x_cont_ptr, *gy_cont_ptr, *gw_ptr;
39
-
40
- cudnnConvolutionBwdFilterAlgoPerf_t perf_result;
41
- cudnnConvolutionBwdFilterAlgo_t algo;
42
- size_t max_workspace_size = cumo_cuda_cudnn_max_workspace_size();
43
- size_t workspace_size;
44
- char* workspace = 0;
45
103
 
46
104
  size_t sizet_w_shape[CUMO_NA_MAX_DIMENSION];
47
105
  int int_stride[CUMO_NA_MAX_DIMENSION];
48
106
  int int_pad[CUMO_NA_MAX_DIMENSION];
49
107
 
108
+ memset(&r, 0, sizeof(r));
50
109
  rb_scan_args(argc, argv, "2:", &gy, &w_shape, &kw_hash);
51
110
  rb_get_kwargs(kw_hash, kw_table, 0, 3, opts);
52
111
  stride = cumo_option_value(opts[0], Qnil);
@@ -102,71 +161,20 @@ static VALUE
102
161
  x_cont = cumo_na_as_contiguous_array(x);
103
162
  gy_cont = cumo_na_as_contiguous_array(gy);
104
163
 
105
- x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
106
- gy_cont_ptr = cumo_na_get_offset_pointer_for_read(gy_cont);
107
- gw_ptr = cumo_na_get_offset_pointer_for_write(gw);
108
-
109
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&x_desc, x_cont, cudnn_dtype);
110
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
111
- status = cumo_cuda_cudnn_CreateTensorDescriptor(&gy_desc, gy_cont, cudnn_dtype);
112
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
113
- status = cumo_cuda_cudnn_CreateFilterDescriptor(&gw_desc, gw, cudnn_dtype);
114
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
115
- status = cumo_cuda_cudnn_CreateConvolutionDescriptor(&conv_desc, ndim, int_stride, int_pad, <%=cudnn_compute_dtype%>, <%=cudnn_math_type%>);
116
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
117
-
118
- handle = cumo_cuda_cudnn_handle();
119
-
120
- // auto tune
121
- status = cumo_cuda_cudnn_FindConvolutionBackwardFilterAlgorithm(
122
- &perf_result,
123
- handle,
124
- x_desc,
125
- x_cont,
126
- gy_desc,
127
- gy_cont,
128
- conv_desc,
129
- gw_desc,
130
- gw,
131
- max_workspace_size,
132
- int_stride,
133
- int_pad,
134
- ndim,
135
- cudnn_dtype);
136
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
137
- // The descriptor asked for a math type; use the one the search settled on.
138
- status = cudnnSetConvolutionMathType(conv_desc, perf_result.mathType);
139
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
140
-
141
- algo = perf_result.algo;
142
- workspace_size = perf_result.memory;
143
-
144
- // The search may look at algorithms needing up to max_workspace_size,
145
- // but only the one it picked has to be paid for.
146
- if (workspace_size > 0) workspace = cumo_cuda_runtime_malloc(workspace_size);
147
- status = cudnnConvolutionBackwardFilter(
148
- handle,
149
- (void*)&one,
150
- x_desc,
151
- (void*)x_cont_ptr,
152
- gy_desc,
153
- (void*)gy_cont_ptr,
154
- conv_desc,
155
- algo,
156
- (void*)workspace,
157
- workspace_size,
158
- (void*)&zero,
159
- gw_desc,
160
- (void*)gw_ptr);
161
- if (status != CUDNN_STATUS_SUCCESS) goto CONV_GRAD_W_ERROR;
162
-
163
- CONV_GRAD_W_ERROR:
164
- if (x_desc) cudnnDestroyTensorDescriptor(x_desc);
165
- if (gy_desc) cudnnDestroyTensorDescriptor(gy_desc);
166
- if (gw_desc) cudnnDestroyFilterDescriptor(gw_desc);
167
- if (conv_desc) cudnnDestroyConvolutionDescriptor(conv_desc);
168
- if (workspace) cumo_cuda_runtime_free(workspace);
169
- cumo_cuda_cudnn_check_status(status);
164
+ r.x_cont = x_cont;
165
+ r.gy_cont = gy_cont;
166
+ r.gw = gw;
167
+ r.x_cont_ptr = cumo_na_get_offset_pointer_for_read(x_cont);
168
+ r.gy_cont_ptr = cumo_na_get_offset_pointer_for_read(gy_cont);
169
+ r.gw_ptr = cumo_na_get_offset_pointer_for_write(gw);
170
+ r.ndim = ndim;
171
+ r.int_stride = int_stride;
172
+ r.int_pad = int_pad;
173
+ r.handle = cumo_cuda_cudnn_handle();
174
+
175
+ rb_ensure(<%=c_iter%>_run, (VALUE)&r, cumo_cuda_cudnn_release_conv_held, (VALUE)&r.held);
176
+ cumo_cuda_cudnn_check_status(r.status);
177
+ cumo_cuda_runtime_check_status(r.held.wait_status);
170
178
 
171
179
  return gw;
172
180
  }