cumo 0.12.0 → 0.13.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -2
  3. data/README.md +1 -0
  4. data/ext/cumo/cuda/cudnn_impl.cpp +23 -30
  5. data/ext/cumo/cuda/cusolver.c +193 -24
  6. data/ext/cumo/cuda/nvrtc.c +77 -11
  7. data/ext/cumo/cuda/runtime.c +15 -4
  8. data/ext/cumo/depend.erb +1 -1
  9. data/ext/cumo/extconf.rb +2 -2
  10. data/ext/cumo/include/cumo/atomic.h +27 -0
  11. data/ext/cumo/include/cumo/cuda/runtime.h +26 -5
  12. data/ext/cumo/include/cumo/intern.h +2 -2
  13. data/ext/cumo/include/cumo/narray.h +2 -2
  14. data/ext/cumo/include/cumo/types/int_macro_kernel.h +1 -1
  15. data/ext/cumo/include/cumo/types/uint_macro_kernel.h +1 -2
  16. data/ext/cumo/include/cumo.h +2 -2
  17. data/ext/cumo/narray/array.c +4 -4
  18. data/ext/cumo/narray/data.c +14 -16
  19. data/ext/cumo/narray/gen/cogen_kernel.rb +24 -2
  20. data/ext/cumo/narray/gen/erbpp2.rb +7 -1
  21. data/ext/cumo/narray/gen/kernel_parts.rb +11 -1
  22. data/ext/cumo/narray/gen/narray_def.rb +2 -2
  23. data/ext/cumo/narray/gen/spec.rb +10 -0
  24. data/ext/cumo/narray/gen/tmpl/accum.c +9 -8
  25. data/ext/cumo/narray/gen/tmpl/accum_arg.c +3 -3
  26. data/ext/cumo/narray/gen/tmpl/accum_binary.c +5 -5
  27. data/ext/cumo/narray/gen/tmpl/accum_index.c +3 -3
  28. data/ext/cumo/narray/gen/tmpl/allocate.c +3 -1
  29. data/ext/cumo/narray/gen/tmpl/aref.c +2 -2
  30. data/ext/cumo/narray/gen/tmpl/aref_cpu.c +2 -2
  31. data/ext/cumo/narray/gen/tmpl/aset.c +3 -3
  32. data/ext/cumo/narray/gen/tmpl/at.c +2 -2
  33. data/ext/cumo/narray/gen/tmpl/binary.c +2 -2
  34. data/ext/cumo/narray/gen/tmpl/binary2.c +2 -2
  35. data/ext/cumo/narray/gen/tmpl/binary_s.c +3 -3
  36. data/ext/cumo/narray/gen/tmpl/bincount.c +7 -7
  37. data/ext/cumo/narray/gen/tmpl/cast.c +4 -2
  38. data/ext/cumo/narray/gen/tmpl/clip.c +3 -3
  39. data/ext/cumo/narray/gen/tmpl/coerce_cast.c +1 -1
  40. data/ext/cumo/narray/gen/tmpl/complex_accum_kernel.cu +44 -0
  41. data/ext/cumo/narray/gen/tmpl/cond_binary.c +2 -2
  42. data/ext/cumo/narray/gen/tmpl/cond_unary.c +1 -1
  43. data/ext/cumo/narray/gen/tmpl/cum.c +3 -3
  44. data/ext/cumo/narray/gen/tmpl/each.c +1 -1
  45. data/ext/cumo/narray/gen/tmpl/ewcomp.c +6 -5
  46. data/ext/cumo/narray/gen/tmpl/extract.c +1 -1
  47. data/ext/cumo/narray/gen/tmpl/extract_cpu.c +1 -1
  48. data/ext/cumo/narray/gen/tmpl/eye.c +5 -5
  49. data/ext/cumo/narray/gen/tmpl/fill.c +2 -2
  50. data/ext/cumo/narray/gen/tmpl/float_accum_kernel.cu +30 -0
  51. data/ext/cumo/narray/gen/tmpl/format.c +2 -2
  52. data/ext/cumo/narray/gen/tmpl/format_to_a.c +2 -2
  53. data/ext/cumo/narray/gen/tmpl/frexp.c +2 -2
  54. data/ext/cumo/narray/gen/tmpl/gemm.c +3 -3
  55. data/ext/cumo/narray/gen/tmpl/inspect.c +1 -1
  56. data/ext/cumo/narray/gen/tmpl/layer_norm.c +5 -5
  57. data/ext/cumo/narray/gen/tmpl/logseq.c +4 -4
  58. data/ext/cumo/narray/gen/tmpl/median.c +5 -5
  59. data/ext/cumo/narray/gen/tmpl/minmax.c +4 -4
  60. data/ext/cumo/narray/gen/tmpl/poly.c +2 -2
  61. data/ext/cumo/narray/gen/tmpl/pow.c +60 -15
  62. data/ext/cumo/narray/gen/tmpl/pow_kernel.cu +28 -16
  63. data/ext/cumo/narray/gen/tmpl/rand.c +7 -9
  64. data/ext/cumo/narray/gen/tmpl/rand_norm.c +6 -9
  65. data/ext/cumo/narray/gen/tmpl/rms_norm.c +4 -4
  66. data/ext/cumo/narray/gen/tmpl/seq.c +3 -3
  67. data/ext/cumo/narray/gen/tmpl/sort.c +4 -4
  68. data/ext/cumo/narray/gen/tmpl/sort_index.c +3 -3
  69. data/ext/cumo/narray/gen/tmpl/store.c +2 -2
  70. data/ext/cumo/narray/gen/tmpl/store_kernel.cu +1 -1
  71. data/ext/cumo/narray/gen/tmpl/to_a.c +1 -1
  72. data/ext/cumo/narray/gen/tmpl/unary.c +1 -1
  73. data/ext/cumo/narray/gen/tmpl/unary2.c +1 -1
  74. data/ext/cumo/narray/gen/tmpl/unary_ret2.c +1 -1
  75. data/ext/cumo/narray/gen/tmpl/unary_s.c +2 -2
  76. data/ext/cumo/narray/gen/tmpl_bit/aref.c +2 -2
  77. data/ext/cumo/narray/gen/tmpl_bit/aref_cpu.c +2 -2
  78. data/ext/cumo/narray/gen/tmpl_bit/aset.c +3 -3
  79. data/ext/cumo/narray/gen/tmpl_bit/binary.c +2 -2
  80. data/ext/cumo/narray/gen/tmpl_bit/bit_count.c +3 -3
  81. data/ext/cumo/narray/gen/tmpl_bit/bit_count_cpu.c +3 -3
  82. data/ext/cumo/narray/gen/tmpl_bit/bit_reduce.c +3 -3
  83. data/ext/cumo/narray/gen/tmpl_bit/bit_stat.c +3 -3
  84. data/ext/cumo/narray/gen/tmpl_bit/each.c +1 -1
  85. data/ext/cumo/narray/gen/tmpl_bit/each_with_index.c +1 -1
  86. data/ext/cumo/narray/gen/tmpl_bit/extract.c +1 -1
  87. data/ext/cumo/narray/gen/tmpl_bit/extract_cpu.c +1 -1
  88. data/ext/cumo/narray/gen/tmpl_bit/fill.c +2 -2
  89. data/ext/cumo/narray/gen/tmpl_bit/format.c +2 -2
  90. data/ext/cumo/narray/gen/tmpl_bit/format_to_a.c +2 -2
  91. data/ext/cumo/narray/gen/tmpl_bit/inspect.c +1 -1
  92. data/ext/cumo/narray/gen/tmpl_bit/mask.c +2 -2
  93. data/ext/cumo/narray/gen/tmpl_bit/to_a.c +1 -1
  94. data/ext/cumo/narray/gen/tmpl_bit/unary.c +1 -1
  95. data/ext/cumo/narray/gen/tmpl_bit/where.c +1 -1
  96. data/ext/cumo/narray/gen/tmpl_bit/where2.c +1 -1
  97. data/ext/cumo/narray/index.c +7 -4
  98. data/ext/cumo/narray/math.c +6 -6
  99. data/ext/cumo/narray/narray.c +54 -43
  100. data/ext/cumo/narray/ndloop.c +7 -7
  101. data/ext/cumo/narray/rand.c +7 -12
  102. data/ext/cumo/narray/struct.c +3 -3
  103. data/lib/cumo/cuda/compiler.rb +1 -1
  104. data/lib/cumo/cuda/elementwise_kernel.rb +7 -6
  105. data/lib/cumo/cuda/reduction_kernel.rb +7 -4
  106. data/lib/cumo/cuda/user_kernel.rb +5 -2
  107. data/lib/cumo/linalg.rb +313 -77
  108. data/lib/cumo/narray/extra.rb +5 -5
  109. metadata +2 -1
@@ -2,6 +2,10 @@
2
2
  #include <ruby/thread.h>
3
3
  #include <assert.h>
4
4
  #include <limits.h>
5
+ #include <cuda.h>
6
+ #include <dlfcn.h>
7
+ #include <pthread.h>
8
+ #include <stdio.h>
5
9
  #include <nvrtc.h>
6
10
  #include "cumo/cuda/nvrtc.h"
7
11
  #include "cumo/cuda/handle.h"
@@ -13,11 +17,70 @@ VALUE cumo_cuda_mNVRTC;
13
17
 
14
18
  static cumo_cuda_handle_set_t programs;
15
19
 
20
+ #define NVRTC_FUNCTIONS(X) \
21
+ X(nvrtcGetErrorString) X(nvrtcVersion) X(nvrtcCreateProgram) X(nvrtcDestroyProgram) \
22
+ X(nvrtcCompileProgram) X(nvrtcGetPTXSize) X(nvrtcGetPTX) X(nvrtcAddNameExpression) \
23
+ X(nvrtcGetLoweredName) X(nvrtcGetProgramLogSize) X(nvrtcGetProgramLog)
24
+
25
+ static struct {
26
+ #define NVRTC_POINTER(f) __typeof__(&f) f;
27
+ NVRTC_FUNCTIONS(NVRTC_POINTER)
28
+ #undef NVRTC_POINTER
29
+ } nvrtc_functions;
30
+ static pthread_once_t nvrtc_once = PTHREAD_ONCE_INIT;
31
+ static char nvrtc_error[256];
32
+
33
+ static const char*
34
+ dl_error(void)
35
+ {
36
+ const char *e = dlerror();
37
+ return e ? e : "unknown error";
38
+ }
39
+
40
+ static void
41
+ load_nvrtc(void)
42
+ {
43
+ char soname[32];
44
+ void *lib;
45
+
46
+ if (CUDA_VERSION >= 12000) {
47
+ snprintf(soname, sizeof(soname), "libnvrtc.so.%d", CUDA_VERSION / 1000);
48
+ } else if (CUDA_VERSION >= 11020) {
49
+ snprintf(soname, sizeof(soname), "libnvrtc.so.11.2");
50
+ } else {
51
+ snprintf(soname, sizeof(soname), "libnvrtc.so.%d.%d", CUDA_VERSION / 1000, CUDA_VERSION % 1000 / 10);
52
+ }
53
+ lib = dlopen(soname, RTLD_NOW | RTLD_LOCAL);
54
+ if (lib == NULL) {
55
+ snprintf(nvrtc_error, sizeof(nvrtc_error), "cannot load %s: %s", soname, dl_error());
56
+ return;
57
+ }
58
+ #define NVRTC_RESOLVE(f) \
59
+ nvrtc_functions.f = (__typeof__(&f))dlsym(lib, #f); \
60
+ if (nvrtc_functions.f == NULL) { \
61
+ snprintf(nvrtc_error, sizeof(nvrtc_error), "%s has no %s: %s", soname, #f, dl_error()); \
62
+ dlclose(lib); \
63
+ return; \
64
+ }
65
+ NVRTC_FUNCTIONS(NVRTC_RESOLVE)
66
+ #undef NVRTC_RESOLVE
67
+ }
68
+
69
+ static const __typeof__(nvrtc_functions)*
70
+ nvrtc(void)
71
+ {
72
+ pthread_once(&nvrtc_once, load_nvrtc);
73
+ if (nvrtc_error[0] != '\0') {
74
+ rb_raise(cumo_cuda_eNVRTCError, "%s", nvrtc_error);
75
+ }
76
+ return &nvrtc_functions;
77
+ }
78
+
16
79
  static void
17
80
  check_status(nvrtcResult status)
18
81
  {
19
82
  if (status != 0) {
20
- rb_raise(cumo_cuda_eNVRTCError, "%s (error=%d)", nvrtcGetErrorString(status), status);
83
+ rb_raise(cumo_cuda_eNVRTCError, "%s (error=%d)", nvrtc()->nvrtcGetErrorString(status), status);
21
84
  }
22
85
  }
23
86
 
@@ -28,7 +91,7 @@ rb_nvrtcVersion(VALUE self)
28
91
  nvrtcResult status;
29
92
  VALUE major, minor;
30
93
 
31
- status = nvrtcVersion(&_major, &_minor);
94
+ status = nvrtc()->nvrtcVersion(&_major, &_minor);
32
95
 
33
96
  check_status(status);
34
97
  major = INT2NUM(_major);
@@ -105,7 +168,7 @@ nvrtcCreateProgram_without_gvl_cb(void *param)
105
168
  {
106
169
  struct nvrtcCreateProgramParam *p = param;
107
170
  nvrtcResult status;
108
- status = nvrtcCreateProgram(p->prog, p->src, p->name, p->numHeaders, p->headers, p->includeNames);
171
+ status = nvrtc_functions.nvrtcCreateProgram(p->prog, p->src, p->name, p->numHeaders, p->headers, p->includeNames);
109
172
  return (void *)status;
110
173
  }
111
174
 
@@ -126,6 +189,7 @@ rb_nvrtcCreateProgram(
126
189
  const char** _includeNames = NULL;
127
190
  VALUE strings;
128
191
 
192
+ nvrtc();
129
193
  // nvrtcCreateProgram reads numHeaders entries out of includeNames as well,
130
194
  // so a shorter array would be read past its end.
131
195
  if (ary_len(includeNames, "include_names") != _numHeaders) {
@@ -168,7 +232,7 @@ nvrtcDestroyProgram_without_gvl_cb(void *param)
168
232
  {
169
233
  struct nvrtcDestroyProgramParam *p = param;
170
234
  nvrtcResult status;
171
- status = nvrtcDestroyProgram(p->prog);
235
+ status = nvrtc_functions.nvrtcDestroyProgram(p->prog);
172
236
  return (void *)status;
173
237
  }
174
238
 
@@ -179,6 +243,7 @@ rb_nvrtcDestroyProgram(VALUE self, VALUE prog)
179
243
  nvrtcProgram _prog = (nvrtcProgram)cumo_cuda_handle_take(&programs, prog, "nvrtcProgram");
180
244
 
181
245
  struct nvrtcDestroyProgramParam param = {&_prog};
246
+ nvrtc();
182
247
  status = (nvrtcResult)rb_thread_call_without_gvl(nvrtcDestroyProgram_without_gvl_cb, &param, NULL, NULL);
183
248
 
184
249
  check_status(status);
@@ -196,7 +261,7 @@ nvrtcCompileProgram_without_gvl_cb(void *param)
196
261
  {
197
262
  struct nvrtcCompileProgramParam *p = param;
198
263
  nvrtcResult status;
199
- status = nvrtcCompileProgram(p->prog, p->numOptions, p->options);
264
+ status = nvrtc_functions.nvrtcCompileProgram(p->prog, p->numOptions, p->options);
200
265
  return (void *)status;
201
266
  }
202
267
 
@@ -209,6 +274,7 @@ rb_nvrtcCompileProgram(VALUE self, VALUE prog, VALUE options)
209
274
  const char** _options = NULL;
210
275
  VALUE strings;
211
276
 
277
+ nvrtc();
212
278
  strings = rb_ary_new_capa(_numOptions);
213
279
  push_strings(strings, options, _numOptions);
214
280
 
@@ -237,12 +303,12 @@ rb_nvrtcGetPTX(VALUE self, VALUE prog)
237
303
  char *_ptx;
238
304
  VALUE ptx;
239
305
 
240
- status = nvrtcGetPTXSize(_prog, &_ptxSizeRet);
306
+ status = nvrtc()->nvrtcGetPTXSize(_prog, &_ptxSizeRet);
241
307
  check_status(status);
242
308
 
243
309
  ptx = rb_str_new(NULL, _ptxSizeRet);
244
310
  _ptx = RSTRING_PTR(ptx);
245
- status = nvrtcGetPTX(_prog, _ptx);
311
+ status = nvrtc()->nvrtcGetPTX(_prog, _ptx);
246
312
  check_status(status);
247
313
 
248
314
  return ptx;
@@ -262,7 +328,7 @@ rb_nvrtcAddNameExpression(VALUE self, VALUE prog, VALUE name_expression)
262
328
  nvrtcProgram _prog;
263
329
  StringValueCStr(name_expression);
264
330
  _prog = (nvrtcProgram)cumo_cuda_handle_get(&programs, prog, "nvrtcProgram");
265
- check_status(nvrtcAddNameExpression(_prog, RSTRING_PTR(name_expression)));
331
+ check_status(nvrtc()->nvrtcAddNameExpression(_prog, RSTRING_PTR(name_expression)));
266
332
  RB_GC_GUARD(name_expression);
267
333
  return Qnil;
268
334
  }
@@ -283,7 +349,7 @@ rb_nvrtcGetLoweredName(VALUE self, VALUE prog, VALUE name_expression)
283
349
  const char *lowered = NULL;
284
350
  StringValueCStr(name_expression);
285
351
  _prog = (nvrtcProgram)cumo_cuda_handle_get(&programs, prog, "nvrtcProgram");
286
- check_status(nvrtcGetLoweredName(_prog, RSTRING_PTR(name_expression), &lowered));
352
+ check_status(nvrtc()->nvrtcGetLoweredName(_prog, RSTRING_PTR(name_expression), &lowered));
287
353
  RB_GC_GUARD(name_expression);
288
354
  return rb_str_new_cstr(lowered);
289
355
  }
@@ -297,12 +363,12 @@ rb_nvrtcGetProgramLog(VALUE self, VALUE prog)
297
363
  char *_log;
298
364
  VALUE log;
299
365
 
300
- status = nvrtcGetProgramLogSize(_prog, &_logSizeRet);
366
+ status = nvrtc()->nvrtcGetProgramLogSize(_prog, &_logSizeRet);
301
367
  check_status(status);
302
368
 
303
369
  log = rb_str_new(NULL, _logSizeRet);
304
370
  _log = RSTRING_PTR(log);
305
- status = nvrtcGetProgramLog(_prog, _log);
371
+ status = nvrtc()->nvrtcGetProgramLog(_prog, _log);
306
372
  check_status(status);
307
373
 
308
374
  return log;
@@ -11,8 +11,8 @@
11
11
 
12
12
  VALUE cumo_cuda_eRuntimeError;
13
13
  VALUE cumo_cuda_mRuntime;
14
- uint64_t cumo_cuda_sync_epoch = 0;
15
- uint64_t cumo_cuda_launch_epoch = 0;
14
+ size_t cumo_cuda_sync_epoch = 0;
15
+ size_t cumo_cuda_launch_epoch = 0;
16
16
 
17
17
  static ID id_current_stream;
18
18
 
@@ -63,7 +63,7 @@ cumo_cuda_stream_set(cudaStream_t stream)
63
63
  void
64
64
  cumo_cuda_runtime_check_kernel_launch(void)
65
65
  {
66
- cumo_cuda_launch_epoch++;
66
+ cumo_atomic_size_fetch_add(&cumo_cuda_launch_epoch, 1);
67
67
  check_status(cudaGetLastError());
68
68
  }
69
69
 
@@ -215,7 +215,7 @@ cumo_cuda_runtime_check_taken_status_holding(int status, char *p0, char *p1, cha
215
215
  void
216
216
  cumo_cuda_runtime_check_kernel_launch_holding(char *p0, char *p1, char *p2, char *p3, char *p4)
217
217
  {
218
- cumo_cuda_launch_epoch++;
218
+ cumo_atomic_size_fetch_add(&cumo_cuda_launch_epoch, 1);
219
219
  cumo_cuda_runtime_check_taken_status_holding((int)cudaGetLastError(), p0, p1, p2, p3, p4);
220
220
  }
221
221
 
@@ -866,6 +866,10 @@ thread_table_free(void *ptr)
866
866
  char *entry = t->entries + i * local->entry_size;
867
867
  spare_entry_t *spare;
868
868
  if (!entry_filled(entry, local->entry_size)) { continue; }
869
+ if (local->release != NULL) {
870
+ local->release(entry);
871
+ if (!entry_filled(entry, local->entry_size)) { continue; }
872
+ }
869
873
  spare = (spare_entry_t*)malloc(sizeof(spare_entry_t) + local->entry_size);
870
874
  if (spare == NULL) { continue; }
871
875
  spare->device = i;
@@ -883,11 +887,18 @@ cumo_cuda_thread_local_init(cumo_cuda_thread_local_t *local, size_t entry_size)
883
887
  {
884
888
  local->entry_size = entry_size;
885
889
  local->spare = NULL;
890
+ local->release = NULL;
886
891
  if (pthread_mutex_init(&local->lock, NULL) != 0 || pthread_key_create(&local->key, thread_table_free) != 0) {
887
892
  rb_raise(rb_eRuntimeError, "cannot set up a per-thread table");
888
893
  }
889
894
  }
890
895
 
896
+ void
897
+ cumo_cuda_thread_local_on_exit(cumo_cuda_thread_local_t *local, void (*release)(void *entry))
898
+ {
899
+ local->release = release;
900
+ }
901
+
891
902
  static int
892
903
  take_spare(cumo_cuda_thread_local_t *local, size_t device, char *entry)
893
904
  {
data/ext/cumo/depend.erb CHANGED
@@ -40,7 +40,7 @@ list_type_rb = Dir.glob("#{__dir__}/narray/gen/def/*.rb")
40
40
  list_type_rb.each do |type_rb|
41
41
  type_name = File.basename(type_rb, ".rb")
42
42
  next if ENV['DTYPE'] and !type_name.downcase.include?(ENV['DTYPE'].downcase)
43
- KERNEL_PARTS.each do |part|
43
+ kernel_parts_of(type_name).each do |part|
44
44
  list_type_cu << type_cu = "narray/types/" + kernel_part_basename(type_name, part) + ".cu"
45
45
  %>
46
46
  <%=type_cu%>: <%=type_rb%> $(CU_DEPENDS)
data/ext/cumo/extconf.rb CHANGED
@@ -72,7 +72,7 @@ cuda/cusolver
72
72
  )
73
73
  %w[bit int8 int16 int32 int64 uint8 uint16 uint32 uint64 hfloat bfloat sfloat dfloat scomplex dcomplex robject].each do |type_name|
74
74
  srcs << "narray/types/#{type_name}"
75
- srcs.concat(KERNEL_PARTS.map { |part| "narray/types/" + kernel_part_basename(type_name, part) })
75
+ srcs.concat(kernel_parts_of(type_name).map { |part| "narray/types/" + kernel_part_basename(type_name, part) })
76
76
  end
77
77
 
78
78
  $objs = srcs.map { |src| "#{src}.o" }
@@ -138,7 +138,7 @@ dir_config('cumo', HEADER_DIRS, LIB_DIRS)
138
138
 
139
139
  have_library('cuda')
140
140
  have_library('cudart')
141
- have_library('nvrtc')
141
+ have_header('nvrtc.h')
142
142
  have_library('cublas')
143
143
  # have_library('curand')
144
144
 
@@ -0,0 +1,27 @@
1
+ #ifndef CUMO_ATOMIC_H
2
+ #define CUMO_ATOMIC_H
3
+
4
+ #include <stddef.h>
5
+
6
+ // The compiler builtins rather than ruby/atomic.h: the size_t fetch-add
7
+ // there arrived after the oldest Ruby cumo builds with, and this header is
8
+ // read without ruby.h as well.
9
+ static inline size_t
10
+ cumo_atomic_size_fetch_add(size_t *var, size_t n)
11
+ {
12
+ return __atomic_fetch_add(var, n, __ATOMIC_SEQ_CST);
13
+ }
14
+
15
+ static inline size_t
16
+ cumo_atomic_size_load(const size_t *var)
17
+ {
18
+ return __atomic_load_n(var, __ATOMIC_SEQ_CST);
19
+ }
20
+
21
+ static inline void
22
+ cumo_atomic_size_store(size_t *var, size_t value)
23
+ {
24
+ __atomic_store_n(var, value, __ATOMIC_SEQ_CST);
25
+ }
26
+
27
+ #endif /* ifndef CUMO_ATOMIC_H */
@@ -2,6 +2,7 @@
2
2
  #define CUMO_CUDA_RUNTIME_H
3
3
 
4
4
  #include "cumo/narray.h"
5
+ #include "cumo/atomic.h"
5
6
  #include <pthread.h>
6
7
  #include <cuda_runtime.h>
7
8
 
@@ -25,17 +26,17 @@ cudaStream_t cumo_cuda_stream_get(VALUE stream);
25
26
  // the moment the count has moved on. cumo_cuda_runtime_device_synchronize
26
27
  // advances it, and so does a host read under a stream of the caller's, and a
27
28
  // count that is behind costs a wait rather than correctness.
28
- extern uint64_t cumo_cuda_sync_epoch;
29
+ extern size_t cumo_cuda_sync_epoch;
29
30
 
30
31
  // How many times device memory was written from here: every kernel launch,
31
32
  // copy into it and library call counts. A host loop reads a staged copy,
32
33
  // and reads again once the count moves.
33
- extern uint64_t cumo_cuda_launch_epoch;
34
+ extern size_t cumo_cuda_launch_epoch;
34
35
 
35
36
  static inline void
36
37
  cumo_cuda_runtime_note_device_write(void)
37
38
  {
38
- cumo_cuda_launch_epoch++;
39
+ cumo_atomic_size_fetch_add(&cumo_cuda_launch_epoch, 1);
39
40
  }
40
41
 
41
42
  // A pinned host buffer for reading device memory back: a copy into pinned
@@ -67,11 +68,27 @@ cumo_cuda_runtime_check_status(cudaError_t status)
67
68
 
68
69
  void cumo_cuda_runtime_return_scratch(char *ptr, int wait_for_stream, cudaError_t *status);
69
70
 
71
+ // The counts move before the wait, not after it. A view that records the
72
+ // count while another thread is already waiting then waits itself, where
73
+ // recording the count the wait left behind would have let it skip a wait
74
+ // that never covered its fill.
70
75
  static inline void
71
76
  cumo_cuda_runtime_device_synchronize(void)
72
77
  {
78
+ cumo_atomic_size_fetch_add(&cumo_cuda_sync_epoch, 1);
73
79
  cumo_cuda_runtime_check_status(cudaDeviceSynchronize());
74
- cumo_cuda_sync_epoch++;
80
+ }
81
+
82
+ static inline size_t
83
+ cumo_cuda_sync_epoch_now(void)
84
+ {
85
+ return cumo_atomic_size_load(&cumo_cuda_sync_epoch);
86
+ }
87
+
88
+ static inline size_t
89
+ cumo_cuda_launch_epoch_now(void)
90
+ {
91
+ return cumo_atomic_size_load(&cumo_cuda_launch_epoch);
75
92
  }
76
93
 
77
94
  // Asking costs less than half of waiting, and there is nothing to wait for
@@ -114,9 +131,9 @@ cumo_cuda_runtime_memcpy_to_pinned(void *dst, const void *src, size_t bytes)
114
131
  {
115
132
  cudaError_t status;
116
133
  if (cumo_cuda_stream() != 0 && !cumo_cuda_runtime_streams_idle()) {
134
+ cumo_atomic_size_fetch_add(&cumo_cuda_sync_epoch, 1);
117
135
  status = cudaDeviceSynchronize();
118
136
  if (status != cudaSuccess) { return status; }
119
- cumo_cuda_sync_epoch++;
120
137
  }
121
138
  status = cudaMemcpyAsync(dst, src, bytes, cudaMemcpyDeviceToHost, cumo_cuda_stream());
122
139
  if (status != cudaSuccess) { return status; }
@@ -131,9 +148,13 @@ typedef struct {
131
148
  size_t entry_size;
132
149
  pthread_mutex_t lock;
133
150
  void *spare;
151
+ void (*release)(void *entry);
134
152
  } cumo_cuda_thread_local_t;
135
153
 
136
154
  void cumo_cuda_thread_local_init(cumo_cuda_thread_local_t *local, size_t entry_size);
155
+ // Called on each filled entry of a thread that ends, without the GVL. An
156
+ // entry it empties is dropped instead of being kept for the next thread.
157
+ void cumo_cuda_thread_local_on_exit(cumo_cuda_thread_local_t *local, void (*release)(void *entry));
137
158
  void *cumo_cuda_thread_local_get(cumo_cuda_thread_local_t *local);
138
159
 
139
160
  static inline int
@@ -22,6 +22,7 @@ unsigned int cumo_na_element_stride(VALUE nary);
22
22
  size_t cumo_na_dtype_element_stride(VALUE klass);
23
23
 
24
24
  char *cumo_na_get_pointer(VALUE);
25
+ int cumo_na_has_pending_scalar(VALUE);
25
26
  char *cumo_na_get_pointer_for_write(VALUE);
26
27
  char *cumo_na_get_pointer_for_read(VALUE);
27
28
  char *cumo_na_get_pointer_for_read_write(VALUE);
@@ -113,8 +114,7 @@ VALUE cumo_na_ary_composition_dtype(VALUE ary);
113
114
 
114
115
  // defined in rand, used by the generated rand and rand_norm
115
116
  u_int64_t cumo_cuda_rand_seed(void);
116
- u_int64_t cumo_cuda_rand_offset(void);
117
- void cumo_cuda_rand_set_offset(u_int64_t offset);
117
+ u_int64_t cumo_cuda_rand_reserve_offset(u_int64_t n);
118
118
 
119
119
  #include "ruby/version.h"
120
120
 
@@ -253,8 +253,8 @@ typedef struct {
253
253
  // until it has been worked out. What a view reaches never changes, so it
254
254
  // is worked out once, and it stays right even after the base has moved on.
255
255
  size_t reach_end;
256
- uint64_t index_sync_epoch; // synchronizes counted when the index fills were
257
- // issued; UINT64_MAX when that is not known
256
+ size_t index_sync_epoch; // synchronizes counted when the index fills were
257
+ // issued; SIZE_MAX when that is not known
258
258
  // A view made from another one can point at the index arrays it already
259
259
  // has rather than copy them. index_owner keeps that view alive for as long
260
260
  // as this one borrows from it, and index_owned says which dimensions are
@@ -43,7 +43,7 @@ __host__ __device__ static inline dtype int_reciprocal(dtype x) {
43
43
  }
44
44
  }
45
45
 
46
- __device__ static dtype pow_int(dtype x, int p)
46
+ __device__ static dtype pow_int(dtype x, int64_t p)
47
47
  {
48
48
  dtype r = m_one;
49
49
  switch(p) {
@@ -18,7 +18,7 @@ __host__ __device__ static inline dtype int_reciprocal(dtype x) {
18
18
  }
19
19
  }
20
20
 
21
- __device__ static dtype pow_int(dtype x, int p)
21
+ __device__ static dtype pow_int(dtype x, uint64_t p)
22
22
  {
23
23
  dtype r = m_one;
24
24
  switch(p) {
@@ -27,7 +27,6 @@ __device__ static dtype pow_int(dtype x, int p)
27
27
  case 2: return x*x;
28
28
  case 3: return x*x*x;
29
29
  }
30
- if (p<0) return 0;
31
30
  while (p) {
32
31
  if (p&1) r *= x;
33
32
  x *= x;
@@ -10,8 +10,8 @@ extern "C" {
10
10
  #endif
11
11
  #endif
12
12
 
13
- #define CUMO_VERSION "0.12.0"
14
- #define CUMO_VERSION_CODE 1200
13
+ #define CUMO_VERSION "0.13.0"
14
+ #define CUMO_VERSION_CODE 1300
15
15
 
16
16
  bool cumo_compatible_mode_enabled_p();
17
17
  bool cumo_show_warning_enabled_p();
@@ -449,8 +449,8 @@ cumo_na_s_array_shape(VALUE mod, VALUE ary)
449
449
  Cumo::DFloat.new_like(obj) returns DFloat instance.
450
450
 
451
451
  @overload new_like(obj)
452
- @param [Numeric,Array,Cumo::NArray] obj
453
- @return [Cumo::NArray]
452
+ @param [Numeric,Array,Cumo::NArray] obj
453
+ @return [Cumo::NArray]
454
454
  @example
455
455
  Cumo::NArray.new_like([[1,2,3],[4,5,6]])
456
456
  # => Cumo::Int32#shape=[2,3](empty)
@@ -490,8 +490,8 @@ cumo_na_s_array_type(VALUE mod, VALUE ary)
490
490
  /*
491
491
  Generate NArray object. NArray datatype is automatically selected.
492
492
  @overload [](elements)
493
- @param [Numeric,Array] elements
494
- @return [NArray]
493
+ @param [Numeric,Array] elements
494
+ @return [NArray]
495
495
  */
496
496
  static VALUE
497
497
  cumo_na_s_bracket(VALUE klass, VALUE ary)
@@ -230,9 +230,9 @@ check_axis(int axis, int ndim)
230
230
  /*
231
231
  Interchange two axes.
232
232
  @overload swapaxes(axis1,axis2)
233
- @param [Integer] axis1
234
- @param [Integer] axis2
235
- @return [Cumo::NArray] view of NArray.
233
+ @param [Integer] axis1
234
+ @param [Integer] axis2
235
+ @return [Cumo::NArray] view of NArray.
236
236
  @example
237
237
  x = Cumo::Int32[[1,2,3]]
238
238
 
@@ -452,9 +452,8 @@ cumo_na_check_reshape(int argc, VALUE *argv, VALUE self, size_t *shape)
452
452
  Raise exception if self is non-contiguous.
453
453
 
454
454
  @overload reshape!(size0,size1,...)
455
- @param sizeN [Integer] new shape
456
- @return [Cumo::NArray] return self.
457
- @example
455
+ @param sizeN [Integer] new shape
456
+ @return [Cumo::NArray] return self.
458
457
  */
459
458
  static VALUE
460
459
  cumo_na_reshape_bang(int argc, VALUE *argv, VALUE self)
@@ -510,9 +509,8 @@ cumo_na_reshape_bang(int argc, VALUE *argv, VALUE self)
510
509
  Returns a copied NArray.
511
510
 
512
511
  @overload reshape(size0,size1,...)
513
- @param sizeN [Integer] new shape
514
- @return [Cumo::NArray] a copy carrying the new shape.
515
- @example
512
+ @param sizeN [Integer] new shape
513
+ @return [Cumo::NArray] a copy carrying the new shape.
516
514
  */
517
515
  static VALUE
518
516
  cumo_na_reshape(int argc, VALUE *argv, VALUE self)
@@ -673,13 +671,13 @@ void cumo_na_diagonal_stride_index_kernel_launch(size_t *idx, ssize_t s0, size_t
673
671
  /*
674
672
  Returns a diagonal view of NArray
675
673
  @overload diagonal([offset,axes])
676
- @param [Integer] offset Diagonal offset from the main diagonal.
677
- The default is 0. k>0 for diagonals above the main diagonal,
678
- and k<0 for diagonals below the main diagonal.
679
- @param [Array] axes Array of axes to be used as the 2-d sub-arrays
680
- from which the diagonals should be taken. Defaults to last-two
681
- axes ([-2,-1]).
682
- @return [Cumo::NArray] diagonal view of NArray.
674
+ @param [Integer] offset Diagonal offset from the main diagonal.
675
+ The default is 0. k>0 for diagonals above the main diagonal,
676
+ and k<0 for diagonals below the main diagonal.
677
+ @param [Array] axes Array of axes to be used as the 2-d sub-arrays
678
+ from which the diagonals should be taken. Defaults to last-two
679
+ axes ([-2,-1]).
680
+ @return [Cumo::NArray] diagonal view of NArray.
683
681
  @example
684
682
  a = Cumo::DFloat.new(4,5).seq
685
683
  # => Cumo::DFloat#shape=[4,5]
@@ -52,13 +52,35 @@ TEMPLATE_KERNEL_PARTS = {
52
52
  "bit_count" => %w[reduce],
53
53
  "bit_reduce" => %w[reduce],
54
54
  "bit_stat" => %w[reduce],
55
+ "cond_binary" => %w[compare],
56
+ "cond_unary" => %w[compare],
57
+ "rand" => %w[misc],
58
+ "rand_norm" => %w[misc],
59
+ "poly" => %w[misc],
60
+ "quantize_symmetric" => %w[misc],
61
+ "unary_ret2" => %w[misc],
55
62
  }.freeze
56
63
 
64
+ MATH_TEMPLATES = %w[unary_s binary_s ternary_s frexp].freeze
65
+ COMMON_MATH = %w[sqrt log log2 log10 exp exp2 sin cos tan tanh erf gelu gelu_tanh silu sigmoid softplus].freeze
66
+
57
67
  abort "unknown kernel part: #{$kernel_part}" unless KERNEL_PARTS.include?($kernel_part)
68
+ undeclared_math = COMMON_MATH - File.read(File.join(thisdir, "spec.rb")).scan(/^\s*math "(\w+)"/).flatten
69
+ abort "COMMON_MATH names no math function in spec.rb: #{undeclared_math.join(', ')}" unless undeclared_math.empty?
58
70
 
59
71
  class ErbPP
60
72
  def kernel_parts
61
- TEMPLATE_KERNEL_PARTS.fetch(get(:erb_base), %w[main])
73
+ erb_base = get(:erb_base)
74
+ return TEMPLATE_KERNEL_PARTS[erb_base] if TEMPLATE_KERNEL_PARTS.key?(erb_base)
75
+ return [COMMON_MATH.include?(@opts[:name]) ? "math" : "math_rare"] if MATH_TEMPLATES.include?(erb_base)
76
+ return @children.flat_map(&:kernel_parts).uniq unless @children.empty?
77
+ return [@opts[:type_name] == get(:class_name) ? "main" : "cast"] if %w[store_from store_bit].include?(erb_base)
78
+
79
+ %w[main]
80
+ end
81
+
82
+ def kernel_children
83
+ @children.select { |c| c.kernel_parts.include?($kernel_part) }
62
84
  end
63
85
 
64
86
  def kernel_part?(nan: false, from: nil)
@@ -87,7 +109,7 @@ end
87
109
 
88
110
  class DefModule
89
111
  def method_code
90
- @children.select { |c| c.kernel_parts.include?($kernel_part) }.map { |c| c.result }.join("\n")
112
+ kernel_children.map { |c| c.result }.join("\n")
91
113
  end
92
114
  end
93
115
 
@@ -266,7 +266,13 @@ class DefMethod < ErbPP
266
266
 
267
267
  def init_def
268
268
  return if n_arg == :nodef
269
- s = (singleton) ? "_singleton" : ""
269
+ s = if singleton
270
+ "_singleton"
271
+ elsif @opts[:private]
272
+ "_private"
273
+ else
274
+ ""
275
+ end
270
276
  "rb_define#{s}_method(#{define_method_args});"
271
277
  end
272
278
 
@@ -1,6 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- KERNEL_PARTS = %w[main reduce reduce_nan reduce_from].freeze
3
+ KERNEL_PARTS = %w[main reduce reduce_nan reduce_from math math_rare compare cast misc].freeze
4
+ MATH_TYPES = %w[hfloat bfloat sfloat dfloat scomplex dcomplex].freeze
5
+
6
+ def kernel_parts_of(type_name)
7
+ return KERNEL_PARTS.first(4) if type_name == "robject"
8
+
9
+ KERNEL_PARTS.reject do |part|
10
+ (%w[math math_rare].include?(part) && !MATH_TYPES.include?(type_name)) ||
11
+ (%w[compare misc].include?(part) && type_name == "bit")
12
+ end
13
+ end
4
14
 
5
15
  def kernel_part_basename(type_name, part)
6
16
  part == "main" ? "#{type_name}_kernel" : "#{type_name}_kernel_#{part}"
@@ -59,8 +59,8 @@ module NArrayMethod
59
59
  def_method(meth, "bit_reduce", **h)
60
60
  end
61
61
 
62
- def accum(meth, dtype, result_class)
63
- h = {dtype:dtype, result_class:result_class}
62
+ def accum(meth, dtype, result_class, **opts)
63
+ h = {dtype:dtype, result_class:result_class, **opts}
64
64
  def_method(meth, "accum", **h)
65
65
  end
66
66
 
@@ -341,6 +341,16 @@ elsif is_int && !is_object
341
341
  accum "rms", "double", "cumo_cDFloat"
342
342
  end
343
343
 
344
+ if %w[sfloat dfloat scomplex dcomplex].include?(get(:type_name))
345
+ %w[abs_sum abs_max abs_min].each do |name|
346
+ if is_complex
347
+ accum name, "rtype", "cRT", private: true, nan_forms: false
348
+ else
349
+ accum name, "dtype", "cT", private: true, nan_forms: false
350
+ end
351
+ end
352
+ end
353
+
344
354
  if is_comparable
345
355
  accum "min", "dtype", "cT"
346
356
  accum "max", "dtype", "cT"