cumo 0.13.0 → 0.14.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.
@@ -0,0 +1,605 @@
1
+ #include "cumo/cuda/cufft.h"
2
+
3
+ #include <ruby.h>
4
+ #include <dlfcn.h>
5
+ #include <pthread.h>
6
+ #include <stdio.h>
7
+ #include "cumo/narray.h"
8
+ #include "cumo/intern.h"
9
+ #include "cumo/cuda/memory_pool.h"
10
+ #include "cumo/cuda/runtime.h"
11
+
12
+ VALUE cumo_cuda_eCufftError;
13
+ VALUE cumo_cuda_mCufft;
14
+ #define eCufftError cumo_cuda_eCufftError
15
+ #define mCufft cumo_cuda_mCufft
16
+
17
+ #ifdef CUFFT_FOUND
18
+
19
+ #define CUFFT_FUNCTIONS(X) \
20
+ X(cufftGetVersion) X(cufftCreate) X(cufftSetAutoAllocation) X(cufftMakePlanMany64) \
21
+ X(cufftDestroy) X(cufftSetWorkArea) X(cufftSetStream) X(cufftExecC2C) X(cufftExecZ2Z) \
22
+ X(cufftExecR2C) X(cufftExecD2Z) X(cufftExecC2R) X(cufftExecZ2D)
23
+
24
+ static struct {
25
+ #define CUFFT_POINTER(f) __typeof__(&f) f;
26
+ CUFFT_FUNCTIONS(CUFFT_POINTER)
27
+ #undef CUFFT_POINTER
28
+ } cufft_functions;
29
+ static pthread_once_t cufft_once = PTHREAD_ONCE_INIT;
30
+ static char cufft_error[256];
31
+
32
+ static const char*
33
+ dl_error(void)
34
+ {
35
+ const char *e = dlerror();
36
+ return e ? e : "unknown error";
37
+ }
38
+
39
+ static void
40
+ load_cufft(void)
41
+ {
42
+ char soname[32];
43
+ void *lib;
44
+
45
+ snprintf(soname, sizeof(soname), "libcufft.so.%d", CUFFT_VER_MAJOR);
46
+ lib = dlopen(soname, RTLD_NOW | RTLD_LOCAL);
47
+ if (lib == NULL) {
48
+ snprintf(cufft_error, sizeof(cufft_error), "cannot load %s: %s", soname, dl_error());
49
+ return;
50
+ }
51
+ #define CUFFT_RESOLVE(f) \
52
+ cufft_functions.f = (__typeof__(&f))dlsym(lib, #f); \
53
+ if (cufft_functions.f == NULL) { \
54
+ snprintf(cufft_error, sizeof(cufft_error), "%s has no %s: %s", soname, #f, dl_error()); \
55
+ dlclose(lib); \
56
+ return; \
57
+ }
58
+ CUFFT_FUNCTIONS(CUFFT_RESOLVE)
59
+ #undef CUFFT_RESOLVE
60
+ }
61
+
62
+ static const __typeof__(cufft_functions)*
63
+ cufft_api(void)
64
+ {
65
+ pthread_once(&cufft_once, load_cufft);
66
+ if (cufft_error[0] != '\0') {
67
+ rb_raise(cumo_cuda_eCufftError, "%s", cufft_error);
68
+ }
69
+ return &cufft_functions;
70
+ }
71
+
72
+ static const char*
73
+ get_cufft_error_msg(cufftResult error)
74
+ {
75
+ switch (error) {
76
+ #define RETURN_MSG(msg) \
77
+ case msg: \
78
+ return #msg
79
+
80
+ RETURN_MSG(CUFFT_SUCCESS);
81
+ RETURN_MSG(CUFFT_INVALID_PLAN);
82
+ RETURN_MSG(CUFFT_ALLOC_FAILED);
83
+ RETURN_MSG(CUFFT_INVALID_TYPE);
84
+ RETURN_MSG(CUFFT_INVALID_VALUE);
85
+ RETURN_MSG(CUFFT_INTERNAL_ERROR);
86
+ RETURN_MSG(CUFFT_EXEC_FAILED);
87
+ RETURN_MSG(CUFFT_SETUP_FAILED);
88
+ RETURN_MSG(CUFFT_INVALID_SIZE);
89
+ RETURN_MSG(CUFFT_UNALIGNED_DATA);
90
+ RETURN_MSG(CUFFT_INVALID_DEVICE);
91
+ RETURN_MSG(CUFFT_NO_WORKSPACE);
92
+ RETURN_MSG(CUFFT_NOT_IMPLEMENTED);
93
+ RETURN_MSG(CUFFT_NOT_SUPPORTED);
94
+
95
+ #undef RETURN_MSG
96
+ default:
97
+ return "CUFFT_UNKNOWN";
98
+ }
99
+ }
100
+
101
+ NORETURN(static void raise_cufft_error(cufftResult status));
102
+
103
+ static void
104
+ raise_cufft_error(cufftResult status)
105
+ {
106
+ rb_raise(cumo_cuda_eCufftError, "%s (error=%d)", get_cufft_error_msg(status), status);
107
+ }
108
+
109
+ void
110
+ cumo_cuda_cufft_check_status(cufftResult status)
111
+ {
112
+ cumo_cuda_runtime_note_device_write();
113
+ if (status != CUFFT_SUCCESS) {
114
+ raise_cufft_error(status);
115
+ }
116
+ }
117
+
118
+ static void
119
+ check_call(cufftResult status)
120
+ {
121
+ if (status != CUFFT_SUCCESS) {
122
+ raise_cufft_error(status);
123
+ }
124
+ }
125
+
126
+ /*
127
+ Returns the version of the cuFFT library cumo is linked with.
128
+
129
+ @return [Integer] the version cufftGetVersion reports
130
+ */
131
+ static VALUE
132
+ rb_cufft_version(VALUE self)
133
+ {
134
+ int version;
135
+ check_call(cufft_api()->cufftGetVersion(&version));
136
+ return INT2NUM(version);
137
+ }
138
+
139
+ #define CUFFT_PLAN_CACHE_SIZE 16
140
+ #define CUFFT_MAX_RANK 3
141
+
142
+ typedef struct {
143
+ cufftHandle handle;
144
+ cudaEvent_t done;
145
+ cufftType type;
146
+ int rank;
147
+ long long n[CUFFT_MAX_RANK];
148
+ long long batch;
149
+ size_t work_size;
150
+ unsigned long long used;
151
+ } cufft_plan_t;
152
+
153
+ typedef struct {
154
+ cufft_plan_t plans[CUFFT_PLAN_CACHE_SIZE];
155
+ int count;
156
+ unsigned long long clock;
157
+ unsigned long long hits;
158
+ unsigned long long misses;
159
+ } cufft_plan_cache_t;
160
+
161
+ static cumo_cuda_thread_local_t caches;
162
+
163
+ static void
164
+ destroy_plan(const cufft_plan_t *p)
165
+ {
166
+ cudaEventSynchronize(p->done);
167
+ cufft_functions.cufftDestroy(p->handle);
168
+ cudaEventDestroy(p->done);
169
+ }
170
+
171
+ static void
172
+ drop_plans(cufft_plan_cache_t *cache)
173
+ {
174
+ int i;
175
+
176
+ for (i = 0; i < cache->count; i++) {
177
+ destroy_plan(&cache->plans[i]);
178
+ }
179
+ cudaGetLastError();
180
+ cache->count = 0;
181
+ }
182
+
183
+ static void
184
+ cufft_plan_cache_release(void *entry)
185
+ {
186
+ drop_plans((cufft_plan_cache_t*)entry);
187
+ memset(entry, 0, sizeof(cufft_plan_cache_t));
188
+ }
189
+
190
+ static cufftResult
191
+ make_plan(const __typeof__(cufft_functions) *api, cufft_plan_t *p)
192
+ {
193
+ cufftResult status = api->cufftCreate(&p->handle);
194
+
195
+ if (status != CUFFT_SUCCESS) {
196
+ return status;
197
+ }
198
+ status = api->cufftSetAutoAllocation(p->handle, 0);
199
+ if (status == CUFFT_SUCCESS) {
200
+ status = api->cufftMakePlanMany64(p->handle, p->rank, p->n, NULL, 1, 0, NULL, 1, 0, p->type, p->batch, &p->work_size);
201
+ }
202
+ if (status != CUFFT_SUCCESS) {
203
+ api->cufftDestroy(p->handle);
204
+ }
205
+ return status;
206
+ }
207
+
208
+ static cufft_plan_t
209
+ take_plan(cufft_plan_cache_t *cache, cufftType type, int rank, const long long *n, long long batch)
210
+ {
211
+ const __typeof__(cufft_functions) *api = cufft_api();
212
+ cufft_plan_t p;
213
+ cufftResult status;
214
+ cudaError_t event_status;
215
+ int i;
216
+
217
+ for (i = 0; i < cache->count; i++) {
218
+ cufft_plan_t *q = &cache->plans[i];
219
+ if (q->type == type && q->rank == rank && q->batch == batch &&
220
+ memcmp(q->n, n, sizeof(long long) * rank) == 0) {
221
+ p = *q;
222
+ *q = cache->plans[--cache->count];
223
+ cache->hits++;
224
+ return p;
225
+ }
226
+ }
227
+
228
+ cache->misses++;
229
+ memset(&p, 0, sizeof(p));
230
+ p.type = type;
231
+ p.rank = rank;
232
+ memcpy(p.n, n, sizeof(long long) * rank);
233
+ p.batch = batch;
234
+ status = make_plan(api, &p);
235
+ if (status == CUFFT_ALLOC_FAILED || status == CUFFT_INTERNAL_ERROR) {
236
+ drop_plans(cache);
237
+ rb_funcall(rb_define_module("GC"), rb_intern("start"), 0);
238
+ rb_funcall(rb_path2class("Cumo::CUDA::MemoryPool"), rb_intern("free_all_blocks"), 0);
239
+ status = make_plan(api, &p);
240
+ }
241
+ if (status != CUFFT_SUCCESS) {
242
+ raise_cufft_error(status);
243
+ }
244
+ event_status = cudaEventCreateWithFlags(&p.done, cudaEventDisableTiming);
245
+ if (event_status != cudaSuccess) {
246
+ api->cufftDestroy(p.handle);
247
+ cumo_cuda_runtime_check_status(event_status);
248
+ }
249
+ return p;
250
+ }
251
+
252
+ static void
253
+ put_back_plan(cufft_plan_cache_t *cache, cufft_plan_t *p)
254
+ {
255
+ int i, oldest = 0;
256
+
257
+ if (cache->count == CUFFT_PLAN_CACHE_SIZE) {
258
+ for (i = 1; i < cache->count; i++) {
259
+ if (cache->plans[i].used < cache->plans[oldest].used) {
260
+ oldest = i;
261
+ }
262
+ }
263
+ destroy_plan(&cache->plans[oldest]);
264
+ cudaGetLastError();
265
+ cache->plans[oldest] = cache->plans[--cache->count];
266
+ }
267
+ p->used = ++cache->clock;
268
+ cache->plans[cache->count++] = *p;
269
+ }
270
+
271
+ typedef enum {
272
+ CUFFT_KIND_C2C,
273
+ CUFFT_KIND_R2C,
274
+ CUFFT_KIND_C2R,
275
+ } cufft_kind_t;
276
+
277
+ typedef struct {
278
+ VALUE x;
279
+ VALUE y;
280
+ int ndim;
281
+ const size_t *shape;
282
+ const size_t *out_shape;
283
+ cufftType type;
284
+ int rank;
285
+ long long n[CUFFT_MAX_RANK];
286
+ long long batch;
287
+ int direction;
288
+ size_t in_bytes;
289
+ int copy_in;
290
+ cufft_plan_cache_t *cache;
291
+ cufft_plan_t plan;
292
+ int have_plan;
293
+ char *in_copy;
294
+ char *work;
295
+ } cufft_call_t;
296
+
297
+ static int
298
+ same_shape(VALUE a, int ndim, const size_t *shape)
299
+ {
300
+ cumo_narray_t *na;
301
+
302
+ CumoGetNArray(a, na);
303
+ return CUMO_NA_NDIM(na) == ndim && memcmp(CUMO_NA_SHAPE(na), shape, sizeof(size_t) * ndim) == 0;
304
+ }
305
+
306
+ static VALUE
307
+ exec_body(VALUE arg)
308
+ {
309
+ cufft_call_t *c = (cufft_call_t*)arg;
310
+ const __typeof__(cufft_functions) *api = cufft_api();
311
+ void *in, *out;
312
+ cufftHandle h;
313
+ cufftResult status;
314
+
315
+ c->cache = (cufft_plan_cache_t*)cumo_cuda_thread_local_get(&caches);
316
+ c->plan = take_plan(c->cache, c->type, c->rank, c->n, c->batch);
317
+ c->have_plan = 1;
318
+ if (c->plan.work_size > 0) {
319
+ c->work = cumo_cuda_runtime_malloc(c->plan.work_size);
320
+ }
321
+ if (c->copy_in) {
322
+ c->in_copy = cumo_cuda_runtime_malloc(c->in_bytes);
323
+ }
324
+ out = cumo_na_get_pointer_for_write(c->y);
325
+ in = cumo_na_get_offset_pointer_for_read(c->x);
326
+ if (!same_shape(c->x, c->ndim, c->shape) || cumo_na_check_contiguous(c->x) != Qtrue ||
327
+ !same_shape(c->y, c->ndim, c->out_shape)) {
328
+ rb_raise(rb_eRuntimeError, "NArray changed while preparing the transform");
329
+ }
330
+ if (c->copy_in) {
331
+ cumo_cuda_runtime_check_status(
332
+ cudaMemcpyAsync(c->in_copy, in, c->in_bytes, cudaMemcpyDeviceToDevice, cumo_cuda_stream()));
333
+ in = c->in_copy;
334
+ }
335
+
336
+ h = c->plan.handle;
337
+ check_call(api->cufftSetWorkArea(h, c->work));
338
+ check_call(api->cufftSetStream(h, cumo_cuda_stream()));
339
+ switch (c->type) {
340
+ case CUFFT_C2C:
341
+ status = api->cufftExecC2C(h, (cufftComplex*)in, (cufftComplex*)out, c->direction);
342
+ break;
343
+ case CUFFT_Z2Z:
344
+ status = api->cufftExecZ2Z(h, (cufftDoubleComplex*)in, (cufftDoubleComplex*)out, c->direction);
345
+ break;
346
+ case CUFFT_R2C:
347
+ status = api->cufftExecR2C(h, (cufftReal*)in, (cufftComplex*)out);
348
+ break;
349
+ case CUFFT_D2Z:
350
+ status = api->cufftExecD2Z(h, (cufftDoubleReal*)in, (cufftDoubleComplex*)out);
351
+ break;
352
+ case CUFFT_C2R:
353
+ status = api->cufftExecC2R(h, (cufftComplex*)in, (cufftReal*)out);
354
+ break;
355
+ case CUFFT_Z2D:
356
+ status = api->cufftExecZ2D(h, (cufftDoubleComplex*)in, (cufftDoubleReal*)out);
357
+ break;
358
+ default:
359
+ status = CUFFT_INVALID_TYPE;
360
+ }
361
+ cumo_cuda_cufft_check_status(status);
362
+ cumo_cuda_runtime_check_status(cudaEventRecord(c->plan.done, cumo_cuda_stream()));
363
+ return Qnil;
364
+ }
365
+
366
+ static VALUE
367
+ exec_ensure(VALUE arg)
368
+ {
369
+ cufft_call_t *c = (cufft_call_t*)arg;
370
+ if (c->have_plan) {
371
+ put_back_plan(c->cache, &c->plan);
372
+ }
373
+ cumo_cuda_runtime_return_scratch(c->work, 0, NULL);
374
+ cumo_cuda_runtime_return_scratch(c->in_copy, 0, NULL);
375
+ return Qnil;
376
+ }
377
+
378
+ static VALUE
379
+ transform(VALUE x, VALUE vrank, cufft_kind_t kind, int direction, VALUE vn, int overwrite)
380
+ {
381
+ VALUE in_class = rb_obj_class(x);
382
+ VALUE out_class;
383
+ cumo_narray_t *na;
384
+ size_t *shape, *out_shape;
385
+ size_t in_elmsz;
386
+ int single, i;
387
+ cufft_call_t c;
388
+
389
+ memset(&c, 0, sizeof(c));
390
+ if (kind == CUFFT_KIND_R2C) {
391
+ if (in_class == cumo_cSFloat) {
392
+ single = 1;
393
+ } else if (in_class == cumo_cDFloat) {
394
+ single = 0;
395
+ } else {
396
+ rb_raise(rb_eTypeError, "invalid data type for cuFFT: %"PRIsVALUE, in_class);
397
+ }
398
+ } else if (in_class == cumo_cSComplex) {
399
+ single = 1;
400
+ } else if (in_class == cumo_cDComplex) {
401
+ single = 0;
402
+ } else {
403
+ rb_raise(rb_eTypeError, "invalid data type for cuFFT: %"PRIsVALUE, in_class);
404
+ }
405
+ switch (kind) {
406
+ case CUFFT_KIND_C2C:
407
+ c.type = single ? CUFFT_C2C : CUFFT_Z2Z;
408
+ out_class = in_class;
409
+ in_elmsz = single ? sizeof(cufftComplex) : sizeof(cufftDoubleComplex);
410
+ break;
411
+ case CUFFT_KIND_R2C:
412
+ c.type = single ? CUFFT_R2C : CUFFT_D2Z;
413
+ out_class = single ? cumo_cSComplex : cumo_cDComplex;
414
+ in_elmsz = single ? sizeof(cufftReal) : sizeof(cufftDoubleReal);
415
+ break;
416
+ default:
417
+ c.type = single ? CUFFT_C2R : CUFFT_Z2D;
418
+ out_class = single ? cumo_cSFloat : cumo_cDFloat;
419
+ in_elmsz = single ? sizeof(cufftComplex) : sizeof(cufftDoubleComplex);
420
+ break;
421
+ }
422
+
423
+ c.rank = NUM2INT(vrank);
424
+ if (c.rank < 1 || c.rank > CUFFT_MAX_RANK) {
425
+ rb_raise(rb_eArgError, "rank must be 1 to %d", CUFFT_MAX_RANK);
426
+ }
427
+ if (kind == CUFFT_KIND_C2R) {
428
+ c.n[c.rank - 1] = NUM2LL(vn);
429
+ }
430
+ CumoGetNArray(x, na);
431
+ c.ndim = CUMO_NA_NDIM(na);
432
+ if (c.ndim < c.rank) {
433
+ rb_raise(cumo_na_eDimensionError, "a %d-dimensional array has no %d axes to transform", c.ndim, c.rank);
434
+ }
435
+ if (cumo_na_check_contiguous(x) != Qtrue) {
436
+ rb_raise(rb_eArgError, "array must be contiguous");
437
+ }
438
+ shape = ALLOCA_N(size_t, c.ndim);
439
+ out_shape = ALLOCA_N(size_t, c.ndim);
440
+ for (i = 0; i < c.ndim; i++) {
441
+ shape[i] = out_shape[i] = CUMO_NA_SHAPE(na)[i];
442
+ }
443
+ for (i = 0; i < c.rank; i++) {
444
+ if (kind != CUFFT_KIND_C2R || i < c.rank - 1) {
445
+ c.n[i] = (long long)shape[c.ndim - c.rank + i];
446
+ }
447
+ }
448
+ if (kind == CUFFT_KIND_R2C) {
449
+ out_shape[c.ndim - 1] = shape[c.ndim - 1] / 2 + 1;
450
+ } else if (kind == CUFFT_KIND_C2R) {
451
+ long long last = c.n[c.rank - 1];
452
+ if (last < 1 || shape[c.ndim - 1] != (size_t)(last / 2 + 1)) {
453
+ rb_raise(rb_eArgError, "a last axis of %"SZF"u does not hold the half spectrum of %lld points",
454
+ shape[c.ndim - 1], last);
455
+ }
456
+ out_shape[c.ndim - 1] = (size_t)last;
457
+ }
458
+ for (i = 0; i < c.rank; i++) {
459
+ if (c.n[i] < 1) {
460
+ rb_raise(rb_eArgError, "Invalid number of FFT data points (%lld) specified.", c.n[i]);
461
+ }
462
+ }
463
+ c.batch = 1;
464
+ for (i = 0; i < c.ndim - c.rank; i++) {
465
+ c.batch *= (long long)shape[i];
466
+ }
467
+
468
+ c.y = cumo_na_new(out_class, c.ndim, out_shape);
469
+ if (c.batch == 0) {
470
+ return c.y;
471
+ }
472
+ c.x = x;
473
+ c.shape = shape;
474
+ c.out_shape = out_shape;
475
+ c.direction = direction;
476
+ c.in_bytes = in_elmsz * (size_t)c.batch;
477
+ for (i = c.ndim - c.rank; i < c.ndim; i++) {
478
+ c.in_bytes *= shape[i];
479
+ }
480
+ c.copy_in = (kind == CUFFT_KIND_C2R && !overwrite) ||
481
+ (kind == CUFFT_KIND_R2C && cumo_na_get_offset(x) % (2 * in_elmsz) != 0);
482
+ rb_ensure(exec_body, (VALUE)&c, exec_ensure, (VALUE)&c);
483
+ RB_GC_GUARD(x);
484
+ return c.y;
485
+ }
486
+
487
+ static VALUE
488
+ rb_cufft_c2c(VALUE self, VALUE x, VALUE rank, VALUE inverse)
489
+ {
490
+ return transform(x, rank, CUFFT_KIND_C2C, RTEST(inverse) ? CUFFT_INVERSE : CUFFT_FORWARD, Qnil, 0);
491
+ }
492
+
493
+ static VALUE
494
+ rb_cufft_r2c(VALUE self, VALUE x, VALUE rank)
495
+ {
496
+ return transform(x, rank, CUFFT_KIND_R2C, CUFFT_FORWARD, Qnil, 0);
497
+ }
498
+
499
+ static VALUE
500
+ rb_cufft_c2r(VALUE self, VALUE x, VALUE rank, VALUE n, VALUE overwrite)
501
+ {
502
+ return transform(x, rank, CUFFT_KIND_C2R, CUFFT_INVERSE, n, RTEST(overwrite));
503
+ }
504
+
505
+ #endif // CUFFT_FOUND
506
+
507
+ /*
508
+ Returns availability of cuFFT.
509
+
510
+ @return [Boolean] Returns true if cuFFT is available
511
+ */
512
+ static VALUE
513
+ rb_cufft_available_p(VALUE self)
514
+ {
515
+ #ifdef CUFFT_FOUND
516
+ pthread_once(&cufft_once, load_cufft);
517
+ return cufft_error[0] == '\0' ? Qtrue : Qfalse;
518
+ #else
519
+ return Qfalse;
520
+ #endif
521
+ }
522
+
523
+ void
524
+ cumo_cuda_cufft_drop_plans(void)
525
+ {
526
+ #ifdef CUFFT_FOUND
527
+ if (cufft_functions.cufftDestroy != NULL) {
528
+ drop_plans((cufft_plan_cache_t*)cumo_cuda_thread_local_get(&caches));
529
+ }
530
+ #endif // CUFFT_FOUND
531
+ }
532
+
533
+ /*
534
+ Returns how the cuFFT plan cache of the current thread and device has
535
+ been used since clear_plan_cache last emptied it.
536
+
537
+ @return [Hash] size (plans held), capacity, hits and misses
538
+ */
539
+ static VALUE
540
+ rb_cufft_plan_cache_info(VALUE self)
541
+ {
542
+ VALUE info = rb_hash_new();
543
+ #ifdef CUFFT_FOUND
544
+ cufft_plan_cache_t *cache = (cufft_plan_cache_t*)cumo_cuda_thread_local_get(&caches);
545
+
546
+ rb_hash_aset(info, ID2SYM(rb_intern("size")), INT2NUM(cache->count));
547
+ rb_hash_aset(info, ID2SYM(rb_intern("capacity")), INT2NUM(CUFFT_PLAN_CACHE_SIZE));
548
+ rb_hash_aset(info, ID2SYM(rb_intern("hits")), ULL2NUM(cache->hits));
549
+ rb_hash_aset(info, ID2SYM(rb_intern("misses")), ULL2NUM(cache->misses));
550
+ #else
551
+ rb_hash_aset(info, ID2SYM(rb_intern("size")), INT2FIX(0));
552
+ rb_hash_aset(info, ID2SYM(rb_intern("capacity")), INT2FIX(0));
553
+ rb_hash_aset(info, ID2SYM(rb_intern("hits")), INT2FIX(0));
554
+ rb_hash_aset(info, ID2SYM(rb_intern("misses")), INT2FIX(0));
555
+ #endif // CUFFT_FOUND
556
+ return info;
557
+ }
558
+
559
+ /*
560
+ Destroys the cuFFT plans the current thread holds on the current device,
561
+ and sets the counts of plan_cache_info back to zero.
562
+
563
+ @return [nil]
564
+ */
565
+ static VALUE
566
+ rb_cufft_clear_plan_cache(VALUE self)
567
+ {
568
+ #ifdef CUFFT_FOUND
569
+ cufft_plan_cache_t *cache = (cufft_plan_cache_t*)cumo_cuda_thread_local_get(&caches);
570
+
571
+ if (cache->count > 0) {
572
+ drop_plans(cache);
573
+ }
574
+ cache->hits = 0;
575
+ cache->misses = 0;
576
+ #endif // CUFFT_FOUND
577
+ return Qnil;
578
+ }
579
+
580
+ void
581
+ Init_cumo_cuda_cufft(void)
582
+ {
583
+ VALUE mCumo = rb_define_module("Cumo");
584
+ VALUE mCUDA = rb_define_module_under(mCumo, "CUDA");
585
+
586
+ /*
587
+ Document-module: Cumo::CUDA::Cufft
588
+ */
589
+ mCufft = rb_define_module_under(mCUDA, "Cufft");
590
+ eCufftError = rb_define_class_under(mCUDA, "CufftError", rb_eStandardError);
591
+
592
+ rb_define_singleton_method(mCufft, "available?", rb_cufft_available_p, 0);
593
+ rb_define_singleton_method(mCufft, "plan_cache_info", rb_cufft_plan_cache_info, 0);
594
+ rb_define_singleton_method(mCufft, "clear_plan_cache", rb_cufft_clear_plan_cache, 0);
595
+ #ifdef CUFFT_FOUND
596
+ cumo_cuda_thread_local_init(&caches, sizeof(cufft_plan_cache_t));
597
+ cumo_cuda_thread_local_on_exit(&caches, cufft_plan_cache_release);
598
+ rb_define_singleton_method(mCufft, "version", rb_cufft_version, 0);
599
+ rb_define_singleton_method(mCufft, "c2c", rb_cufft_c2c, 3);
600
+ rb_define_singleton_method(mCufft, "r2c", rb_cufft_r2c, 2);
601
+ rb_define_singleton_method(mCufft, "c2r", rb_cufft_c2r, 4);
602
+ rb_funcall(mCufft, rb_intern("private_class_method"), 3,
603
+ ID2SYM(rb_intern("c2c")), ID2SYM(rb_intern("r2c")), ID2SYM(rb_intern("c2r")));
604
+ #endif // CUFFT_FOUND
605
+ }
@@ -617,6 +617,7 @@ rb_cuLaunchKernel(VALUE self, VALUE hfunc,
617
617
  ALLOCV_END(slots_buf);
618
618
  RB_GC_GUARD(args);
619
619
  check_status(status);
620
+ cumo_cuda_runtime_note_device_write();
620
621
  return Qnil;
621
622
  }
622
623
 
@@ -2,6 +2,7 @@
2
2
  #include <cuda_runtime.h>
3
3
  #include "memory_pool_impl.hpp"
4
4
  #include "cumo.h"
5
+ #include "cumo/cuda/cufft.h"
5
6
  #include "cumo/cuda/memory_pool.h"
6
7
  #include "cumo/cuda/runtime.h"
7
8
 
@@ -74,6 +75,7 @@ cumo_cuda_runtime_malloc(size_t size)
74
75
 
75
76
  outcome = pool_malloc(size, &ptr, &err);
76
77
  if (outcome == POOL_OUT_OF_MEMORY) {
78
+ cumo_cuda_cufft_drop_plans();
77
79
  // retry after GC
78
80
  rb_funcall(rb_define_module("GC"), rb_intern("start"), 0);
79
81
  outcome = pool_malloc(size, &ptr, &err);
data/ext/cumo/cumo.c CHANGED
@@ -72,6 +72,7 @@ void Init_cumo_cuda_nvrtc();
72
72
  void Init_cumo_cuda_cublas();
73
73
  void Init_cumo_cuda_cudnn();
74
74
  void Init_cumo_cuda_cusolver();
75
+ void Init_cumo_cuda_cufft();
75
76
 
76
77
  void
77
78
  cumo_debug_breakpoint(void)
@@ -176,8 +177,6 @@ Init_cumo()
176
177
 
177
178
  mCumo = rb_define_module("Cumo");
178
179
 
179
- rb_define_const(mCumo, "VERSION", rb_str_new2(CUMO_VERSION));
180
-
181
180
  rb_define_singleton_method(mCumo, "enable_compatible_mode", rb_enable_compatible_mode, 0);
182
181
  rb_define_singleton_method(mCumo, "disable_compatible_mode", rb_disable_compatible_mode, 0);
183
182
  rb_define_singleton_method(mCumo, "compatible_mode_enabled?", rb_compatible_mode_enabled_p, 0);
@@ -247,4 +246,5 @@ Init_cumo()
247
246
  Init_cumo_cuda_cublas();
248
247
  Init_cumo_cuda_cudnn();
249
248
  Init_cumo_cuda_cusolver();
249
+ Init_cumo_cuda_cufft();
250
250
  }
data/ext/cumo/extconf.rb CHANGED
@@ -69,6 +69,7 @@ cuda/nvrtc
69
69
  cuda/cudnn
70
70
  cuda/cudnn_impl
71
71
  cuda/cusolver
72
+ cuda/cufft
72
73
  )
73
74
  %w[bit int8 int16 int32 int64 uint8 uint16 uint32 uint64 hfloat bfloat sfloat dfloat scomplex dcomplex robject].each do |type_name|
74
75
  srcs << "narray/types/#{type_name}"
@@ -205,6 +206,13 @@ if have_cusolver?
205
206
  have_func('cusolverDnXhetrf', 'cusolverDn.h')
206
207
  end
207
208
 
209
+ if have_header('cufft.h')
210
+ $CFLAGS << " -DCUFFT_FOUND"
211
+ $CXXFLAGS << " -DCUFFT_FOUND"
212
+ else
213
+ message("cuFFT header not found; building without Cumo::FFT\n")
214
+ end
215
+
208
216
  have_library('stdc++')
209
217
 
210
218
  create_makefile('cumo')
@@ -0,0 +1,38 @@
1
+ #ifndef CUMO_CUDA_CUFFT_H
2
+ #define CUMO_CUDA_CUFFT_H
3
+
4
+ #include <ruby.h>
5
+ #ifdef CUFFT_FOUND
6
+ #pragma GCC diagnostic push
7
+ #pragma GCC diagnostic ignored "-Wundef"
8
+ #include <cufft.h>
9
+ #pragma GCC diagnostic pop
10
+ #endif // CUFFT_FOUND
11
+
12
+ #if defined(__cplusplus)
13
+ extern "C" {
14
+ #if 0
15
+ } /* satisfy cc-mode */
16
+ #endif
17
+ #endif
18
+
19
+ extern VALUE cumo_cuda_eCufftError;
20
+
21
+ void
22
+ cumo_cuda_cufft_drop_plans(void);
23
+
24
+ #ifdef CUFFT_FOUND
25
+
26
+ void
27
+ cumo_cuda_cufft_check_status(cufftResult status);
28
+
29
+ #endif // CUFFT_FOUND
30
+
31
+ #if defined(__cplusplus)
32
+ #if 0
33
+ { /* satisfy cc-mode */
34
+ #endif
35
+ } /* extern "C" { */
36
+ #endif
37
+
38
+ #endif /* ifndef CUMO_CUDA_CUFFT_H */