cumo 0.6.0 → 0.7.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -2
  3. data/README.md +133 -0
  4. data/bench/cumo_sync_probe.rb +71 -8
  5. data/ext/cumo/cuda/cublas.c +0 -40
  6. data/ext/cumo/cuda/cudnn.c +48 -0
  7. data/ext/cumo/cuda/cudnn_impl.cpp +91 -62
  8. data/ext/cumo/cuda/runtime.c +3 -4
  9. data/ext/cumo/cumo.c +34 -0
  10. data/ext/cumo/extconf.rb +2 -0
  11. data/ext/cumo/include/cumo/cuda/cudnn.h +21 -2
  12. data/ext/cumo/include/cumo/cuda/cumo_thrust.hpp +1 -0
  13. data/ext/cumo/include/cumo/cuda/runtime.h +28 -0
  14. data/ext/cumo/include/cumo/intern.h +4 -1
  15. data/ext/cumo/include/cumo/narray.h +59 -3
  16. data/ext/cumo/include/cumo/template.h +35 -10
  17. data/ext/cumo/include/cumo/template_kernel.h +96 -0
  18. data/ext/cumo/include/cumo/types/bit.h +3 -0
  19. data/ext/cumo/include/cumo/types/bit_kernel.h +3 -0
  20. data/ext/cumo/include/cumo/types/complex_macro.h +2 -0
  21. data/ext/cumo/include/cumo/types/complex_macro_kernel.h +2 -0
  22. data/ext/cumo/include/cumo/types/float_macro.h +2 -0
  23. data/ext/cumo/include/cumo/types/float_macro_kernel.h +2 -0
  24. data/ext/cumo/include/cumo/types/half_def.h +129 -0
  25. data/ext/cumo/include/cumo/types/half_def_kernel.h +28 -0
  26. data/ext/cumo/include/cumo/types/half_macro.h +188 -0
  27. data/ext/cumo/include/cumo/types/half_macro_kernel.h +198 -0
  28. data/ext/cumo/include/cumo/types/hfloat.h +22 -0
  29. data/ext/cumo/include/cumo/types/hfloat_kernel.h +16 -0
  30. data/ext/cumo/include/cumo/types/real_accum.h +2 -0
  31. data/ext/cumo/include/cumo/types/real_accum_kernel.h +2 -0
  32. data/ext/cumo/include/cumo/types/robj_macro.h +3 -0
  33. data/ext/cumo/include/cumo/types/xint_macro.h +3 -0
  34. data/ext/cumo/include/cumo/types/xint_macro_kernel.h +2 -0
  35. data/ext/cumo/include/cumo.h +2 -2
  36. data/ext/cumo/narray/data.c +49 -174
  37. data/ext/cumo/narray/gen/def/bit.rb +2 -0
  38. data/ext/cumo/narray/gen/def/dcomplex.rb +2 -0
  39. data/ext/cumo/narray/gen/def/dfloat.rb +8 -0
  40. data/ext/cumo/narray/gen/def/hfloat.rb +47 -0
  41. data/ext/cumo/narray/gen/def/int16.rb +2 -0
  42. data/ext/cumo/narray/gen/def/int32.rb +2 -0
  43. data/ext/cumo/narray/gen/def/int64.rb +2 -0
  44. data/ext/cumo/narray/gen/def/int8.rb +2 -0
  45. data/ext/cumo/narray/gen/def/robject.rb +2 -0
  46. data/ext/cumo/narray/gen/def/scomplex.rb +2 -0
  47. data/ext/cumo/narray/gen/def/sfloat.rb +8 -0
  48. data/ext/cumo/narray/gen/def/uint16.rb +3 -1
  49. data/ext/cumo/narray/gen/def/uint32.rb +3 -1
  50. data/ext/cumo/narray/gen/def/uint64.rb +2 -0
  51. data/ext/cumo/narray/gen/def/uint8.rb +3 -1
  52. data/ext/cumo/narray/gen/erbpp2.rb +6 -0
  53. data/ext/cumo/narray/gen/spec.rb +1 -0
  54. data/ext/cumo/narray/gen/tmpl/accum.c +1 -1
  55. data/ext/cumo/narray/gen/tmpl/accum_arg.c +1 -1
  56. data/ext/cumo/narray/gen/tmpl/accum_arg_kernel.cu +5 -5
  57. data/ext/cumo/narray/gen/tmpl/accum_binary_kernel.cu +14 -1
  58. data/ext/cumo/narray/gen/tmpl/accum_index.c +1 -1
  59. data/ext/cumo/narray/gen/tmpl/accum_index_kernel.cu +5 -5
  60. data/ext/cumo/narray/gen/tmpl/alloc_func.c +4 -7
  61. data/ext/cumo/narray/gen/tmpl/allocate.c +1 -2
  62. data/ext/cumo/narray/gen/tmpl/aref_cpu.c +1 -1
  63. data/ext/cumo/narray/gen/tmpl/batch_norm.c +11 -23
  64. data/ext/cumo/narray/gen/tmpl/batch_norm_backward.c +12 -24
  65. data/ext/cumo/narray/gen/tmpl/binary_kernel.cu +69 -2
  66. data/ext/cumo/narray/gen/tmpl/clip.c +3 -3
  67. data/ext/cumo/narray/gen/tmpl/cond_unary.c +1 -1
  68. data/ext/cumo/narray/gen/tmpl/conv.c +11 -17
  69. data/ext/cumo/narray/gen/tmpl/conv_grad_w.c +11 -17
  70. data/ext/cumo/narray/gen/tmpl/conv_transpose.c +11 -17
  71. data/ext/cumo/narray/gen/tmpl/cum.c +24 -1
  72. data/ext/cumo/narray/gen/tmpl/cum_kernel.cu +29 -4
  73. data/ext/cumo/narray/gen/tmpl/each.c +2 -3
  74. data/ext/cumo/narray/gen/tmpl/each_with_index.c +5 -21
  75. data/ext/cumo/narray/gen/tmpl/extract_cpu.c +1 -1
  76. data/ext/cumo/narray/gen/tmpl/extract_data.c +1 -1
  77. data/ext/cumo/narray/gen/tmpl/fill.c +1 -1
  78. data/ext/cumo/narray/gen/tmpl/fixed_batch_norm.c +9 -21
  79. data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +42 -37
  80. data/ext/cumo/narray/gen/tmpl/format.c +1 -1
  81. data/ext/cumo/narray/gen/tmpl/format_to_a.c +1 -1
  82. data/ext/cumo/narray/gen/tmpl/gemm.c +78 -27
  83. data/ext/cumo/narray/gen/tmpl/init_class.c +0 -7
  84. data/ext/cumo/narray/gen/tmpl/init_upcast.c +6 -0
  85. data/ext/cumo/narray/gen/tmpl/inspect.c +1 -1
  86. data/ext/cumo/narray/gen/tmpl/lib.c +11 -1
  87. data/ext/cumo/narray/gen/tmpl/map_with_index.c +7 -14
  88. data/ext/cumo/narray/gen/tmpl/median.c +9 -47
  89. data/ext/cumo/narray/gen/tmpl/minmax.c +1 -1
  90. data/ext/cumo/narray/gen/tmpl/poly.c +1 -1
  91. data/ext/cumo/narray/gen/tmpl/pooling_backward.c +2 -14
  92. data/ext/cumo/narray/gen/tmpl/pooling_forward.c +2 -14
  93. data/ext/cumo/narray/gen/tmpl/qsort.c +0 -1
  94. data/ext/cumo/narray/gen/tmpl/rand.c +20 -2
  95. data/ext/cumo/narray/gen/tmpl/rand_kernel.cu +13 -0
  96. data/ext/cumo/narray/gen/tmpl/rand_norm.c +4 -4
  97. data/ext/cumo/narray/gen/tmpl/rand_norm_kernel.cu +7 -5
  98. data/ext/cumo/narray/gen/tmpl/real_accum_kernel.cu +83 -97
  99. data/ext/cumo/narray/gen/tmpl/sort.c +1 -1
  100. data/ext/cumo/narray/gen/tmpl/sort_index.c +6 -3
  101. data/ext/cumo/narray/gen/tmpl/store_array.c +21 -22
  102. data/ext/cumo/narray/gen/tmpl/store_bit.c +1 -1
  103. data/ext/cumo/narray/gen/tmpl/store_from.c +1 -1
  104. data/ext/cumo/narray/gen/tmpl/store_from_kernel.cu +37 -2
  105. data/ext/cumo/narray/gen/tmpl/to_a.c +1 -1
  106. data/ext/cumo/narray/gen/tmpl/unary.c +20 -3
  107. data/ext/cumo/narray/gen/tmpl/unary2.c +1 -1
  108. data/ext/cumo/narray/gen/tmpl/unary_s.c +1 -1
  109. data/ext/cumo/narray/gen/tmpl_bit/allocate.c +2 -3
  110. data/ext/cumo/narray/gen/tmpl_bit/aref_cpu.c +1 -1
  111. data/ext/cumo/narray/gen/tmpl_bit/bit_count_cpu.c +1 -1
  112. data/ext/cumo/narray/gen/tmpl_bit/each.c +2 -2
  113. data/ext/cumo/narray/gen/tmpl_bit/each_with_index.c +5 -20
  114. data/ext/cumo/narray/gen/tmpl_bit/extract_cpu.c +1 -1
  115. data/ext/cumo/narray/gen/tmpl_bit/format.c +1 -1
  116. data/ext/cumo/narray/gen/tmpl_bit/format_to_a.c +1 -1
  117. data/ext/cumo/narray/gen/tmpl_bit/inspect.c +1 -1
  118. data/ext/cumo/narray/gen/tmpl_bit/mask.c +38 -17
  119. data/ext/cumo/narray/gen/tmpl_bit/mask_kernel.cu +4 -4
  120. data/ext/cumo/narray/gen/tmpl_bit/store_array.c +10 -11
  121. data/ext/cumo/narray/gen/tmpl_bit/store_from.c +1 -1
  122. data/ext/cumo/narray/gen/tmpl_bit/to_a.c +1 -1
  123. data/ext/cumo/narray/gen/tmpl_bit/where.c +66 -13
  124. data/ext/cumo/narray/gen/tmpl_bit/where2.c +57 -20
  125. data/ext/cumo/narray/gen/tmpl_bit/where_kernel.cu +6 -6
  126. data/ext/cumo/narray/index.c +63 -18
  127. data/ext/cumo/narray/index_kernel.cu +15 -0
  128. data/ext/cumo/narray/math.c +2 -0
  129. data/ext/cumo/narray/narray.c +289 -183
  130. data/ext/cumo/narray/ndloop.c +91 -26
  131. data/ext/cumo/narray/sort_kernel.cu +39 -18
  132. data/ext/cumo/narray/struct.c +14 -17
  133. data/lib/cumo/cuda/cudnn.rb +1 -1
  134. data/lib/cumo/narray/extra.rb +11 -2
  135. data/test/bit_test.rb +147 -0
  136. data/test/cudnn_test.rb +132 -46
  137. data/test/extra_test.rb +1 -1
  138. data/test/hfloat_test.rb +717 -0
  139. data/test/math_test.rb +4 -1
  140. data/test/narray_alt_coverage_test.rb +7 -0
  141. data/test/narray_test.rb +1018 -11
  142. data/test/test_helper.rb +73 -0
  143. metadata +11 -1
@@ -1,17 +1,5 @@
1
1
  #ifdef CUDNN_FOUND
2
2
 
3
- <%
4
- cudnn_dtype =
5
- case type_name
6
- when 'sfloat'
7
- 'CUDNN_DATA_FLOAT'
8
- when 'dfloat'
9
- 'CUDNN_DATA_DOUBLE'
10
- else
11
- # CUDNN_DATA_HALF
12
- raise 'not supported'
13
- end
14
- %>
15
3
 
16
4
  // cover_all=true is not supported with CUDNN
17
5
  // x.pooling_forward(mode, kernel_size, stride: 1, pad: 0, y: nil)
@@ -25,8 +13,8 @@ static VALUE
25
13
  cudnnDataType_t cudnn_dtype = <%= cudnn_dtype %>;
26
14
  cudnnStatus_t status = 0;
27
15
  cudnnHandle_t handle = 0;
28
- dtype alpha = 1;
29
- dtype beta = 0;
16
+ <%=cudnn_scalar_t%> alpha = 1;
17
+ <%=cudnn_scalar_t%> beta = 0;
30
18
 
31
19
  VALUE x=self, mode, kernel_size, stride, pad, y;
32
20
  VALUE kw_hash = Qnil;
@@ -126,7 +126,6 @@ static void
126
126
  r,
127
127
  swaptype,
128
128
  presorted;
129
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
130
129
 
131
130
  loop:SWAPINIT(a, es);
132
131
  if (n < 7)
@@ -58,6 +58,11 @@ inline static dtype m_rand(uint32_t max, int shift)
58
58
  }
59
59
  <% end %>
60
60
  <%
61
+ elsif is_half
62
+ m_rand = "m_rand(max)"
63
+ shift_def = ""
64
+ shift_set = ""
65
+ rand_type = "float"
61
66
  else
62
67
  m_rand = "m_rand(max)"
63
68
  shift_def = ""
@@ -102,7 +107,7 @@ static void
102
107
  dtype x;
103
108
 
104
109
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
105
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
110
+ cumo_cuda_runtime_device_synchronize();
106
111
  if (idx1) {
107
112
  for (; i--;) {
108
113
  x = m_add(<%=m_rand%>,low);
@@ -157,7 +162,13 @@ static VALUE
157
162
  {
158
163
  rand_opt_t g;
159
164
  VALUE v1=Qnil, v2=Qnil;
165
+ <% if is_half %>
166
+ // The span is read in float: half cannot hold one wider than 65504, and
167
+ // subtracting the bounds in half would answer infinity for it.
168
+ float high;
169
+ <% else %>
160
170
  dtype high;
171
+ <% end %>
161
172
  cumo_ndfunc_arg_in_t ain[1] = {{CUMO_OVERWRITE,0}};
162
173
  cumo_ndfunc_t ndf = {<%=c_iter%>, CUMO_FULL_LOOP, 1,0, ain,0};
163
174
 
@@ -173,17 +184,24 @@ static VALUE
173
184
  if (v1==Qnil) {
174
185
  <% if is_complex %>
175
186
  g.max = high = c_new(1,1);
187
+ <% elsif is_half %>
188
+ g.max = high = 1.0f;
176
189
  <% else %>
177
190
  g.max = high = m_one;
178
191
  <% end %>
179
192
  } else {
180
- g.max = high = m_num_to_data(v1);
193
+ g.max = high = <% if is_half %>(float)NUM2DBL(v1)<% else %>m_num_to_data(v1)<% end %>;
181
194
  }
182
195
  <% end %>
183
196
  } else {
184
197
  g.low = m_num_to_data(v1);
198
+ <% if is_half %>
199
+ high = (float)NUM2DBL(v2);
200
+ g.max = high - cumo_half2float(g.low);
201
+ <% else %>
185
202
  high = m_num_to_data(v2);
186
203
  g.max = m_sub(high,g.low);
204
+ <% end %>
187
205
  }
188
206
  <% if is_int && !is_object %>
189
207
  if (high <= g.low) {
@@ -2,6 +2,8 @@
2
2
  if is_int && !is_object
3
3
  rand_bit = (/Int64$/ =~ class_name) ? 64 : 32
4
4
  rand_type = "uint#{rand_bit}_t"
5
+ elsif is_half
6
+ rand_type = "float"
5
7
  else
6
8
  rand_type = "dtype"
7
9
  end
@@ -45,6 +47,17 @@ __device__ static dtype
45
47
  CUMO_REAL(z) = cumo_rand_uniform(st) * CUMO_REAL(max) + CUMO_REAL(low);
46
48
  CUMO_IMAG(z) = cumo_rand_uniform(st) * CUMO_IMAG(max) + CUMO_IMAG(low);
47
49
  return z;
50
+ //<% elsif is_half %>
51
+ // Rounding the float into a half can land on the upper bound, which rand
52
+ // does not include, and the neighbour below it is the nearest value that
53
+ // does. A bound half cannot hold has no neighbour to step to.
54
+ float flow = cumo_half2float(low);
55
+ float high = flow + max;
56
+ dtype h = cumo_float2half(cumo_rand_uniform(st) * max + flow);
57
+ if (isfinite(high) && cumo_half2float(h) >= high) {
58
+ h = cumo_half_step_down(h);
59
+ }
60
+ return h;
48
61
  //<% else %>
49
62
  return (dtype)(cumo_rand_uniform(st) * max) + low;
50
63
  //<% end %>
@@ -1,12 +1,12 @@
1
1
  typedef struct {
2
2
  dtype mu;
3
- rtype sigma;
3
+ <% if is_half %>float<% else %>rtype<% end %> sigma;
4
4
  u_int64_t seed;
5
5
  u_int64_t offset;
6
6
  } randn_opt_t;
7
7
 
8
- void <%="cumo_#{c_iter}_index_kernel_launch"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n);
9
- void <%="cumo_#{c_iter}_stride_kernel_launch"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n);
8
+ void <%="cumo_#{c_iter}_index_kernel_launch"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n);
9
+ void <%="cumo_#{c_iter}_stride_kernel_launch"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n);
10
10
 
11
11
  static void
12
12
  <%=c_iter%>(cumo_na_loop_t *const lp)
@@ -16,7 +16,7 @@ static void
16
16
  ssize_t s1;
17
17
  size_t *idx1;
18
18
  dtype mu;
19
- rtype sigma;
19
+ <% if is_half %>float<% else %>rtype<% end %> sigma;
20
20
  randn_opt_t *g;
21
21
 
22
22
  CUMO_INIT_COUNTER(lp, i);
@@ -16,19 +16,21 @@
16
16
  <% end %>
17
17
 
18
18
  __device__ static dtype
19
- <%="cumo_#{c_iter}_value"%>(curandStatePhilox4_32_10_t *st, dtype mu, rtype sigma)
19
+ <%="cumo_#{c_iter}_value"%>(curandStatePhilox4_32_10_t *st, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma)
20
20
  {
21
21
  //<% if is_complex %>
22
22
  dtype z;
23
23
  CUMO_REAL(z) = cumo_rand_normal(st) * sigma + CUMO_REAL(mu);
24
24
  CUMO_IMAG(z) = cumo_rand_normal(st) * sigma + CUMO_IMAG(mu);
25
25
  return z;
26
+ //<% elsif is_half %>
27
+ return cumo_float2half(cumo_rand_normal(st) * sigma + cumo_half2float(mu));
26
28
  //<% else %>
27
29
  return cumo_rand_normal(st) * sigma + mu;
28
30
  //<% end %>
29
31
  }
30
32
 
31
- __global__ void <%="cumo_#{c_iter}_index_kernel"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n)
33
+ __global__ void <%="cumo_#{c_iter}_index_kernel"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n)
32
34
  {
33
35
  for (uint64_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) {
34
36
  curandStatePhilox4_32_10_t st;
@@ -37,7 +39,7 @@ __global__ void <%="cumo_#{c_iter}_index_kernel"%>(char *p1, size_t *idx1, uint6
37
39
  }
38
40
  }
39
41
 
40
- __global__ void <%="cumo_#{c_iter}_stride_kernel"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n)
42
+ __global__ void <%="cumo_#{c_iter}_stride_kernel"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n)
41
43
  {
42
44
  for (uint64_t i = blockIdx.x * blockDim.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) {
43
45
  curandStatePhilox4_32_10_t st;
@@ -55,7 +57,7 @@ extern "C" {
55
57
  #endif
56
58
  #endif
57
59
 
58
- void <%="cumo_#{c_iter}_index_kernel_launch"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n)
60
+ void <%="cumo_#{c_iter}_index_kernel_launch"%>(char *p1, size_t *idx1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n)
59
61
  {
60
62
  size_t grid_dim = cumo_get_grid_dim(n);
61
63
  size_t block_dim = cumo_get_block_dim(n);
@@ -63,7 +65,7 @@ void <%="cumo_#{c_iter}_index_kernel_launch"%>(char *p1, size_t *idx1, uint64_t
63
65
  cumo_cuda_runtime_check_kernel_launch();
64
66
  }
65
67
 
66
- void <%="cumo_#{c_iter}_stride_kernel_launch"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, rtype sigma, uint64_t n)
68
+ void <%="cumo_#{c_iter}_stride_kernel_launch"%>(char *p1, ssize_t s1, uint64_t seed, uint64_t offset, dtype mu, <% if is_half %>float<% else %>rtype<% end %> sigma, uint64_t n)
67
69
  {
68
70
  size_t grid_dim = cumo_get_grid_dim(n);
69
71
  size_t block_dim = cumo_get_block_dim(n);
@@ -5,102 +5,103 @@
5
5
  } /* extern "C" { */
6
6
  #endif
7
7
 
8
+ <% acc = is_half ? 'float' : dtype %>
9
+ <% to_acc = is_half ? 'cumo_half2float' : '' %>
10
+ <% from_acc = is_half ? 'cumo_float2half' : '' %>
11
+ <% acc_zero = is_half ? '0.0f' : 'm_zero' %>
12
+
13
+
8
14
  // Reduce also combines two accumulators, so next has to be the accumulator type:
9
- // the integer types widen to 64 bits and taking dtype there truncates every
10
- // partial the shared-memory tree merges.
15
+ // the integer types widen to 64 bits and half to float, and taking dtype there
16
+ // truncates every partial the shared-memory tree merges.
11
17
  struct cumo_<%=type_name%>_sum_impl {
12
- __device__ <%=dtype%> Identity(int64_t /*index*/) { return m_zero; }
13
- __device__ <%=dtype%> MapIn(dtype in, int64_t /*index*/) { return in; }
14
- __device__ void Reduce(<%=dtype%> next, <%=dtype%>& accum) { accum += next; }
15
- __device__ <%=dtype%> MapOut(<%=dtype%> accum) { return accum; }
18
+ __device__ <%=acc%> Identity(int64_t /*index*/) { return <%=acc_zero%>; }
19
+ __device__ <%=acc%> MapIn(dtype in, int64_t /*index*/) { return <%=to_acc%>(in); }
20
+ __device__ void Reduce(<%=acc%> next, <%=acc%>& accum) { accum = accum + next; }
21
+ __device__ <%=dtype%> MapOut(<%=acc%> accum) { return <%=from_acc%>(accum); }
16
22
  };
17
23
 
18
24
  struct cumo_<%=type_name%>_prod_impl {
19
25
  __device__ <%=dtype%> Identity(int64_t /*index*/) { return m_one; }
20
26
  __device__ <%=dtype%> MapIn(dtype in, int64_t /*index*/) { return in; }
21
- __device__ void Reduce(<%=dtype%> next, <%=dtype%>& accum) { accum *= next; }
27
+ __device__ void Reduce(<%=dtype%> next, <%=dtype%>& accum) { accum = m_mul(accum, next); }
22
28
  __device__ <%=dtype%> MapOut(<%=dtype%> accum) { return accum; }
23
29
  };
24
30
 
25
- struct cumo_<%=type_name%>_min_impl {
31
+ // One set of rules answers for both components, and min, max, minmax and ptp
32
+ // all name a whole set rather than a rule each. A pair that mixes two sets --
33
+ // the nan-aware min beside the default max -- is then not expressible, which
34
+ // is the shape #372 had: #219 changed the identity in min and max and left the
35
+ // copies inside the fused pair behind.
36
+ struct cumo_<%=type_name%>_extremum_rules {
37
+ struct Min {
26
38
  <% if is_float %>
27
- // A NaN loses the comparison, so it is skipped unless the accumulator is
28
- // still the identity: the reduction then answers NaN only when every
29
- // element was NaN, and equal elements keep the earlier one as numo does.
30
- __device__ dtype Identity(int64_t /*index*/) { return (dtype)nan(""); }
31
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
32
- __device__ void Reduce(dtype next, dtype& accum) { if (next < accum || !not_nan(accum)) { accum = next; } }
39
+ // A NaN loses the comparison, so it is skipped unless the accumulator
40
+ // is still the identity: the reduction then answers NaN only when every
41
+ // element was NaN, and equal elements keep the earlier one as numo does.
42
+ __device__ static dtype Identity() { return (dtype)nan(""); }
43
+ __device__ static void Reduce(dtype next, dtype& accum) { if (m_lt(next, accum) || !not_nan(accum)) { accum = next; } }
33
44
  <% else %>
34
- __device__ dtype Identity(int64_t /*index*/) { return DATA_MAX; }
35
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
36
- __device__ void Reduce(dtype next, dtype& accum) { accum = next < accum ? next : accum; }
45
+ __device__ static dtype Identity() { return DATA_MAX; }
46
+ __device__ static void Reduce(dtype next, dtype& accum) { accum = m_lt(next, accum) ? next : accum; }
37
47
  <% end %>
38
- __device__ dtype MapOut(dtype accum) { return accum; }
39
- };
40
-
41
- struct cumo_<%=type_name%>_max_impl {
48
+ };
49
+ struct Max {
42
50
  <% if is_float %>
43
- __device__ dtype Identity(int64_t /*index*/) { return (dtype)nan(""); }
44
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
45
- __device__ void Reduce(dtype next, dtype& accum) { if (accum < next || !not_nan(accum)) { accum = next; } }
51
+ __device__ static dtype Identity() { return (dtype)nan(""); }
52
+ __device__ static void Reduce(dtype next, dtype& accum) { if (m_lt(accum, next) || !not_nan(accum)) { accum = next; } }
46
53
  <% else %>
47
- __device__ dtype Identity(int64_t /*index*/) { return DATA_MIN; }
48
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
49
- __device__ void Reduce(dtype next, dtype& accum) { accum = next < accum ? accum : next; }
54
+ __device__ static dtype Identity() { return DATA_MIN; }
55
+ __device__ static void Reduce(dtype next, dtype& accum) { accum = m_lt(next, accum) ? accum : next; }
50
56
  <% end %>
57
+ };
58
+ };
59
+
60
+ template <typename Rule>
61
+ struct cumo_<%=type_name%>_extremum_of {
62
+ __device__ dtype Identity(int64_t /*index*/) { return Rule::Identity(); }
63
+ __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
64
+ __device__ void Reduce(dtype next, dtype& accum) { Rule::Reduce(next, accum); }
51
65
  __device__ dtype MapOut(dtype accum) { return accum; }
52
66
  };
53
67
 
54
- // min and max fused, so that minmax reads the input once. The rules per
55
- // component are the two above verbatim: minmax must not answer differently
56
- // from min and max.
57
- struct cumo_<%=type_name%>_minmax_impl {
68
+ // What minmax and ptp accumulate, and how it goes in and out. No Identity and
69
+ // no Reduce: those come from the rules, so a form that names none does not
70
+ // compile.
71
+ struct cumo_<%=type_name%>_minmax_pair {
58
72
  struct MinAndMax {
59
73
  dtype min;
60
74
  dtype max;
61
75
  };
62
- <% if is_float %>
63
- __device__ MinAndMax Identity(int64_t /*index*/) { return {(dtype)nan(""), (dtype)nan("")}; }
64
- __device__ MinAndMax MapIn(dtype in, int64_t /*index*/) { return {in, in}; }
65
- __device__ void Reduce(MinAndMax next, MinAndMax& accum) {
66
- if (next.min < accum.min || !not_nan(accum.min)) { accum.min = next.min; }
67
- if (accum.max < next.max || !not_nan(accum.max)) { accum.max = next.max; }
68
- }
69
- <% else %>
70
- __device__ MinAndMax Identity(int64_t /*index*/) { return {DATA_MAX, DATA_MIN}; }
71
76
  __device__ MinAndMax MapIn(dtype in, int64_t /*index*/) { return {in, in}; }
72
- __device__ void Reduce(MinAndMax next, MinAndMax& accum) {
73
- accum.min = next.min < accum.min ? next.min : accum.min;
74
- accum.max = next.max < accum.max ? accum.max : next.max;
75
- }
76
- <% end %>
77
77
  __device__ void MapOut(MinAndMax accum, dtype* out_min, dtype* out_max) {
78
78
  *out_min = accum.min;
79
79
  *out_max = accum.max;
80
80
  }
81
81
  };
82
82
 
83
- struct cumo_<%=type_name%>_ptp_impl {
84
- struct MinAndMax {
85
- dtype min;
86
- dtype max;
87
- };
88
- __device__ MinAndMax Identity(int64_t /*index*/) { return {DATA_MAX, DATA_MIN}; }
89
- __device__ MinAndMax MapIn(dtype in, int64_t /*index*/) { return {in, in}; }
83
+ // min and max fused, so that minmax reads the input once.
84
+ template <typename Rules>
85
+ struct cumo_<%=type_name%>_minmax_of : cumo_<%=type_name%>_minmax_pair {
86
+ __device__ MinAndMax Identity(int64_t /*index*/) { return {Rules::Min::Identity(), Rules::Max::Identity()}; }
90
87
  __device__ void Reduce(MinAndMax next, MinAndMax& accum) {
91
- if (next.min < accum.min) { accum.min = next.min; }
92
- if (accum.max < next.max) { accum.max = next.max; }
93
- }
94
- __device__ dtype MapOut(MinAndMax accum) {
95
- <% if is_float %>
96
- // A NaN loses both comparisons above, so every element being NaN leaves
97
- // the identity untouched. An empty reduction raises before it gets here.
98
- if (accum.max < accum.min) { return (dtype)nan(""); }
99
- <% end %>
100
- return m_sub(accum.max, accum.min);
88
+ Rules::Min::Reduce(next.min, accum.min);
89
+ Rules::Max::Reduce(next.max, accum.max);
101
90
  }
102
91
  };
103
92
 
93
+ // ptp is its minmax with the pair subtracted. The one-argument MapOut hides
94
+ // the two-output one it inherits.
95
+ template <typename Rules>
96
+ struct cumo_<%=type_name%>_ptp_of : cumo_<%=type_name%>_minmax_of<Rules> {
97
+ __device__ dtype MapOut(cumo_<%=type_name%>_minmax_pair::MinAndMax accum) { return m_sub(accum.max, accum.min); }
98
+ };
99
+
100
+ using cumo_<%=type_name%>_min_impl = cumo_<%=type_name%>_extremum_of<cumo_<%=type_name%>_extremum_rules::Min>;
101
+ using cumo_<%=type_name%>_max_impl = cumo_<%=type_name%>_extremum_of<cumo_<%=type_name%>_extremum_rules::Max>;
102
+ using cumo_<%=type_name%>_minmax_impl = cumo_<%=type_name%>_minmax_of<cumo_<%=type_name%>_extremum_rules>;
103
+ using cumo_<%=type_name%>_ptp_impl = cumo_<%=type_name%>_ptp_of<cumo_<%=type_name%>_extremum_rules>;
104
+
104
105
  <% unless is_float %>
105
106
  // mean, var, stddev and rms answer in double for the integer types, so they
106
107
  // accumulate in double too rather than in dtype.
@@ -155,11 +156,15 @@ struct cumo_<%=type_name%>_rms_impl {
155
156
  // NaN, so it maps to the identity and the tree never sees it. min and max go
156
157
  // the other way: numo answers NaN as soon as one element is NaN, and a NaN
157
158
  // absorbs in Reduce because it loses every comparison.
159
+ //
160
+ // min and max take an infinity for their identity rather than the largest
161
+ // finite value, so that an element which is itself an infinity still wins.
162
+ // numo seeds from the first element and never faces the question.
158
163
  struct cumo_<%=type_name%>_sum_nan_impl {
159
- __device__ dtype Identity(int64_t /*index*/) { return m_zero; }
160
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return not_nan(in) ? in : m_zero; }
161
- __device__ void Reduce(dtype next, dtype& accum) { accum = m_add(next, accum); }
162
- __device__ dtype MapOut(dtype accum) { return accum; }
164
+ __device__ <%=acc%> Identity(int64_t /*index*/) { return <%=acc_zero%>; }
165
+ __device__ <%=acc%> MapIn(dtype in, int64_t /*index*/) { return not_nan(in) ? <%=to_acc%>(in) : <%=acc_zero%>; }
166
+ __device__ void Reduce(<%=acc%> next, <%=acc%>& accum) { accum = next + accum; }
167
+ __device__ dtype MapOut(<%=acc%> accum) { return <%=from_acc%>(accum); }
163
168
  };
164
169
 
165
170
  struct cumo_<%=type_name%>_prod_nan_impl {
@@ -169,40 +174,21 @@ struct cumo_<%=type_name%>_prod_nan_impl {
169
174
  __device__ dtype MapOut(dtype accum) { return accum; }
170
175
  };
171
176
 
172
- struct cumo_<%=type_name%>_min_nan_impl {
173
- __device__ dtype Identity(int64_t /*index*/) { return DATA_MAX; }
174
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
175
- __device__ void Reduce(dtype next, dtype& accum) { if (!not_nan(next) || next < accum) { accum = next; } }
176
- __device__ dtype MapOut(dtype accum) { return accum; }
177
- };
178
-
179
- struct cumo_<%=type_name%>_max_nan_impl {
180
- __device__ dtype Identity(int64_t /*index*/) { return DATA_MIN; }
181
- __device__ dtype MapIn(dtype in, int64_t /*index*/) { return in; }
182
- __device__ void Reduce(dtype next, dtype& accum) { if (!not_nan(next) || accum < next) { accum = next; } }
183
- __device__ dtype MapOut(dtype accum) { return accum; }
184
- };
185
-
186
- struct cumo_<%=type_name%>_minmax_nan_impl {
187
- struct MinAndMax {
188
- dtype min;
189
- dtype max;
177
+ struct cumo_<%=type_name%>_extremum_nan_rules {
178
+ struct Min {
179
+ __device__ static dtype Identity() { return (dtype)INFINITY; }
180
+ __device__ static void Reduce(dtype next, dtype& accum) { if (!not_nan(next) || m_lt(next, accum)) { accum = next; } }
181
+ };
182
+ struct Max {
183
+ __device__ static dtype Identity() { return (dtype)(-INFINITY); }
184
+ __device__ static void Reduce(dtype next, dtype& accum) { if (!not_nan(next) || m_lt(accum, next)) { accum = next; } }
190
185
  };
191
- __device__ MinAndMax Identity(int64_t /*index*/) { return {DATA_MAX, DATA_MIN}; }
192
- __device__ MinAndMax MapIn(dtype in, int64_t /*index*/) { return {in, in}; }
193
- __device__ void Reduce(MinAndMax next, MinAndMax& accum) {
194
- if (!not_nan(next.min) || next.min < accum.min) { accum.min = next.min; }
195
- if (!not_nan(next.max) || accum.max < next.max) { accum.max = next.max; }
196
- }
197
- __device__ void MapOut(MinAndMax accum, dtype* out_min, dtype* out_max) {
198
- *out_min = accum.min;
199
- *out_max = accum.max;
200
- }
201
186
  };
202
187
 
203
- struct cumo_<%=type_name%>_ptp_nan_impl : cumo_<%=type_name%>_minmax_nan_impl {
204
- __device__ dtype MapOut(MinAndMax accum) { return m_sub(accum.max, accum.min); }
205
- };
188
+ using cumo_<%=type_name%>_min_nan_impl = cumo_<%=type_name%>_extremum_of<cumo_<%=type_name%>_extremum_nan_rules::Min>;
189
+ using cumo_<%=type_name%>_max_nan_impl = cumo_<%=type_name%>_extremum_of<cumo_<%=type_name%>_extremum_nan_rules::Max>;
190
+ using cumo_<%=type_name%>_minmax_nan_impl = cumo_<%=type_name%>_minmax_of<cumo_<%=type_name%>_extremum_nan_rules>;
191
+ using cumo_<%=type_name%>_ptp_nan_impl = cumo_<%=type_name%>_ptp_of<cumo_<%=type_name%>_extremum_nan_rules>;
206
192
  <% end %>
207
193
 
208
194
  #if defined(__cplusplus)
@@ -42,7 +42,7 @@ static void
42
42
  CUMO_INIT_COUNTER(lp, n);
43
43
  CUMO_INIT_PTR(lp, 0, ptr, step);
44
44
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
45
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
45
+ cumo_cuda_runtime_device_synchronize();
46
46
  <%=type_name%>_qsort<%=j%>(ptr, n, step);
47
47
  }
48
48
  <% end %>
@@ -50,6 +50,12 @@ static void
50
50
 
51
51
  ptr = (char**)(lp->opt_ptr);
52
52
 
53
+ // The row is managed memory that ndloop may have just filled with a copy
54
+ // kernel, and everything below reads it on the host. Per row rather than
55
+ // once for the call, because that copy happens per row.
56
+ CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
57
+ cumo_cuda_runtime_device_synchronize();
58
+
53
59
  //printf("(ptr=%lx, d_ptr=%lx,d_step=%ld, i_ptr=%lx,i_step=%ld, o_ptr=%lx,o_step=%ld)\n",(size_t)ptr,(size_t)d_ptr,(ssize_t)d_step,(size_t)i_ptr,(ssize_t)i_step,(size_t)o_ptr,(ssize_t)o_step);
54
60
 
55
61
  if (n==1) {
@@ -153,9 +159,6 @@ static VALUE
153
159
  size = na->size*sizeof(void*); // max capa
154
160
  buf = rb_alloc_tmp_buffer(&tmp, size);
155
161
 
156
- CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
157
- cudaDeviceSynchronize();
158
-
159
162
  res = cumo_na_ndloop3(&ndf, buf, 3, self, idx, reduce);
160
163
  rb_free_tmp_buffer(&tmp);
161
164
  return res;
@@ -24,29 +24,29 @@ static void
24
24
  v1 = lp->args[1].value;
25
25
  i = 0;
26
26
 
27
- if (lp->args[1].ptr) {
28
- if (v1 == Qtrue) {
29
- // The loop counter is the destination length, but the sub-narray
30
- // may be shorter. Copy only what the source actually holds, or the
31
- // kernel would read past its end; the rest is zero-filled below.
32
- i = lp->args[1].shape[0];
33
- if (i > n) {
34
- i = n;
35
- }
27
+ if (CumoIsNArray(v1)) {
28
+ // shape[0] is how many elements the sub-narray offers at this row,
29
+ // which is zero where it does not reach. The loop counter is the
30
+ // destination length, so copying more would read past the row's end.
31
+ i = lp->args[1].shape[0];
32
+ if (i > n) {
33
+ i = n;
34
+ }
35
+ if (i > 0) {
36
36
  CUMO_NDL_CNT(lp) = i;
37
37
  iter_<%=type_name%>_store_<%=type_name%>(lp);
38
38
  CUMO_NDL_CNT(lp) = n;
39
- //<% if c_iter.include? 'robject' %>
40
- // The zero fill below walks from here. The kernel the other branch
41
- // launches takes the offset of the first element left to fill as an
42
- // argument instead, so advancing for it would count i twice.
43
- if (idx1) {
44
- idx1 += i;
45
- } else {
46
- p1 += s1 * i;
47
- }
48
- //<% end %>
49
39
  }
40
+ //<% if c_iter.include? 'robject' %>
41
+ // The zero fill below walks from here. The kernel the other branch
42
+ // launches takes the offset of the first element left to fill as an
43
+ // argument instead, so advancing for it would count i twice.
44
+ if (idx1) {
45
+ idx1 += i;
46
+ } else {
47
+ p1 += s1 * i;
48
+ }
49
+ //<% end %>
50
50
  goto loop_end;
51
51
  }
52
52
 
@@ -55,8 +55,7 @@ static void
55
55
  n1 = RARRAY_LEN(v1);
56
56
  break;
57
57
  case T_NIL:
58
- n1 = 0;
59
- break;
58
+ goto loop_end;
60
59
  default:
61
60
  n1 = 1;
62
61
  }
@@ -64,7 +63,7 @@ static void
64
63
  //<% if c_iter.include? 'robject' %>
65
64
  {
66
65
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("store_<%=name%>", "<%=type_name%>");
67
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
66
+ cumo_cuda_runtime_device_synchronize();
68
67
 
69
68
  if (idx1) {
70
69
  for (i=i1=0; i1<n1 && i<n; i1++) {
@@ -23,7 +23,7 @@ static void
23
23
  CUMO_SHOW_SYNCHRONIZE_FIXME_WARNING_ONCE("<%=name%>", "<%=type_name%>");
24
24
  // The index arrays are filled by a kernel; wait for it before reading
25
25
  // them from here.
26
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
26
+ cumo_cuda_runtime_device_synchronize();
27
27
  if (idx2) {
28
28
  if (idx1) {
29
29
  for (; i--;) {
@@ -34,7 +34,7 @@ static void
34
34
  // until that has finished. Cumo::RObject is the only dtype whose loop runs
35
35
  // on the host and still sees the index; every other one hands it to a
36
36
  // kernel of its own, which the stream already orders.
37
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
37
+ cumo_cuda_runtime_device_synchronize();
38
38
  {
39
39
  <%=dtype%> x;
40
40
  dtype y;
@@ -40,10 +40,45 @@ void <%="cumo_#{c_iter}_stridx_kernel_launch"%>(cumo_na_iarray_stridx_t* a1, cum
40
40
  cumo_cuda_runtime_check_kernel_launch();
41
41
  }
42
42
 
43
+ __global__ void <%="cumo_#{c_iter}_transpose_kernel"%>(char *p1, char *p2, uint64_t rows, uint64_t cols)
44
+ {
45
+ CUMO_TRANSPOSE_TILE_DECL(dtype, tile);
46
+
47
+ CUMO_TRANSPOSE_TILE_LOOP(tile, rows, cols,
48
+ <%=macro%>(*(<%=dtype%>*)(p2 + cumo_tile_src * sizeof(<%=dtype%>))),
49
+ *(dtype*)(p1 + cumo_tile_dst * sizeof(dtype)) = cumo_tile_val;
50
+ );
51
+ }
52
+
53
+ // True when the destination runs along its rows and the source along its
54
+ // columns, which is what a copy of a transposed view looks like. A side shorter
55
+ // than a tile leaves most of each warp idle, and the plain loop already reads or
56
+ // writes such a side in one go, so the tiles only pay off once both sides reach
57
+ // one.
58
+ static int
59
+ <%="cumo_#{c_iter}_is_transpose"%>(cumo_na_iarray_t* a1, cumo_na_iarray_t* a2, cumo_na_indexer_t* indexer)
60
+ {
61
+ return CUMO_TRANSPOSE_TILE_FITS(indexer) &&
62
+ a1->step[1] == (ssize_t)sizeof(dtype) &&
63
+ a1->step[0] == (ssize_t)(sizeof(dtype) * indexer->shape[1]) &&
64
+ a2->step[0] == (ssize_t)sizeof(<%=dtype%>) &&
65
+ a2->step[1] == (ssize_t)(sizeof(<%=dtype%>) * indexer->shape[0]);
66
+ }
67
+
43
68
  void <%="cumo_#{c_iter}_kernel_launch"%>(cumo_na_iarray_t* a1, cumo_na_iarray_t* a2, cumo_na_indexer_t* indexer)
44
69
  {
45
- size_t grid_dim = cumo_get_grid_dim(indexer->total_size);
46
- size_t block_dim = cumo_get_block_dim(indexer->total_size);
70
+ size_t grid_dim, block_dim;
71
+
72
+ if (<%="cumo_#{c_iter}_is_transpose"%>(a1, a2, indexer)) {
73
+ CUMO_TRANSPOSE_LAUNCH(<%="cumo_#{c_iter}_transpose_kernel"%>,
74
+ indexer->shape[0], indexer->shape[1],
75
+ a1->ptr, a2->ptr);
76
+ cumo_cuda_runtime_check_kernel_launch();
77
+ return;
78
+ }
79
+
80
+ grid_dim = cumo_get_grid_dim(indexer->total_size);
81
+ block_dim = cumo_get_block_dim(indexer->total_size);
47
82
  switch (indexer->ndim) {
48
83
  <% (0..opt_indexer_ndim).each do |idim| %>
49
84
  case <%=idim%>:
@@ -38,6 +38,6 @@ static VALUE
38
38
  cumo_ndfunc_arg_out_t aout[1] = {{rb_cArray,0}}; // dummy?
39
39
  cumo_ndfunc_t ndf = { <%=c_iter%>, CUMO_FULL_LOOP_NIP, 3, 1, ain, aout };
40
40
  CUMO_SHOW_SYNCHRONIZE_WARNING_ONCE("<%=name%>", "<%=type_name%>");
41
- cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
41
+ cumo_cuda_runtime_device_synchronize();
42
42
  return cumo_na_ndloop_cast_narray_to_rarray(&ndf, self, Qnil);
43
43
  }