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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +50 -0
- data/cumo.gemspec +10 -1
- data/ext/cumo/cuda/cufft.c +605 -0
- data/ext/cumo/cuda/driver.c +1 -0
- data/ext/cumo/cuda/memory_pool.cpp +2 -0
- data/ext/cumo/cumo.c +2 -2
- data/ext/cumo/extconf.rb +8 -0
- data/ext/cumo/include/cumo/cuda/cufft.h +38 -0
- data/ext/cumo/include/cumo.h +0 -3
- data/ext/cumo/narray/narray.c +0 -2
- data/lib/cumo/fft.rb +482 -0
- data/lib/cumo/linalg.rb +19 -8
- data/lib/cumo/narray/extra.rb +31 -0
- data/lib/cumo/pocketfft.rb +134 -0
- data/lib/cumo/version.rb +8 -0
- data/lib/cumo.rb +1 -0
- metadata +7 -1
|
@@ -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
|
+
}
|
data/ext/cumo/cuda/driver.c
CHANGED
|
@@ -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 */
|