Created
June 7, 2011 14:18
-
-
Save bayerj/1012340 to your computer and use it in GitHub Desktop.
theano run warning
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mod.cu: In function ‘PyObject* CudaNdarray_ptr_int_size(PyObject*, PyObject*)’: | |
| mod.cu:1928: warning: comparison between ‘enum cudaError’ and ‘enum cublasStatus_t’ | |
| mod.cu: In function ‘int CudaNdarray_gemm(float, const CudaNdarray*, const CudaNdarray*, float, CudaNdarray*)’: | |
| mod.cu:2753: warning: comparison between ‘enum cublasStatus_t’ and ‘enum cudaError_t’ | |
| mod.cu: In function ‘int CudaNdarray_sger(float, CudaNdarray*, CudaNdarray*, CudaNdarray*)’: | |
| mod.cu:2795: warning: comparison between ‘enum cublasStatus_t’ and ‘enum cudaError_t’ | |
| i686-apple-darwin10-gcc-4.2.1: python: No such file or directory | |
| i686-apple-darwin10-gcc-4.2.1: warning: '-x c++' after last input file has no effect | |
| i686-apple-darwin10-gcc-4.2.1: no input files | |
| =============================== | |
| 1 #include <Python.h> | |
| 2 #include <structmember.h> | |
| 3 | |
| 4 #include <numpy/arrayobject.h> | |
| 5 #include <iostream> | |
| 6 | |
| 7 #include "cuda_ndarray.cuh" | |
| 8 | |
| 9 //If true, when there is a gpu malloc or free error, we print the size of allocated memory on the device. | |
| 10 #define COMPUTE_GPU_MEM_USED 0 | |
| 11 | |
| 12 //If true, we fill with NAN allocated device memory. | |
| 13 #define ALLOC_MEMSET 0 | |
| 14 | |
| 15 ///////////////////////// | |
| 16 // Alloc and Free | |
| 17 ///////////////////////// | |
| 18 | |
| 19 static int g_gpu_context_active = 0; | |
| 20 | |
| 21 | |
| 22 PyObject * | |
| 23 CudaNdarray_Dimshuffle(PyObject* _unused, PyObject* args); | |
| 24 | |
| 25 /** | |
| 26 * | |
| 27 * In the test program I'm using, the _outstanding_mallocs decreases with every call. | |
| 28 * This suggests there are more free() calls being made than alloc(), but I can't figure out why. | |
| 29 * | |
| 30 */ | |
| 31 int _outstanding_mallocs[] = {0,0}; | |
| 32 #if COMPUTE_GPU_MEM_USED | |
| 33 int _allocated_size = 0; | |
| 34 const int TABLE_SIZE = 10000; | |
| 35 struct table_struct{ | |
| 36 void* ptr; | |
| 37 int size; | |
| 38 }; | |
| 39 table_struct _alloc_size_table[TABLE_SIZE]; | |
| 40 #endif | |
| 41 void * device_malloc(size_t size) | |
| 42 { | |
| 43 void * rval=NULL; | |
| 44 cudaError_t err = cudaMalloc(&rval, size); | |
| 45 if (cudaSuccess != err) | |
| 46 { | |
| 47 #if COMPUTE_GPU_MEM_USED | |
| 48 fprintf(stderr, "Error allocating %li bytes of device memory (%s). %d already allocated\n", (long)size, cudaGetErrorString(err),_allocated_size); | |
| 49 #else | |
| 50 fprintf(stderr, "Error allocating %li bytes of device memory (%s).\n", (long)size, cudaGetErrorString(err)); | |
| 51 #endif | |
| 52 PyErr_Format(PyExc_MemoryError, "Error allocating %li bytes of device memory (%s).", (long)size, cudaGetErrorString(err)); | |
| 53 return NULL; | |
| 54 } | |
| 55 _outstanding_mallocs[0] += (rval != NULL); | |
| 56 #if COMPUTE_GPU_MEM_USED | |
| 57 for(int i=0;i<TABLE_SIZE;i++){ | |
| 58 if(NULL==_alloc_size_table[i].ptr){ | |
| 59 _alloc_size_table[i].ptr=rval; | |
| 60 _alloc_size_table[i].size=size; | |
| 61 break; | |
| 62 } | |
| 63 } | |
| 64 _allocated_size += size; | |
| 65 #endif | |
| 66 //fprintf(stderr, "allocated %li bytes of device memory (%s). %d already allocated, ptr: %p\n", (long)size, cudaGetErrorString(err),_allocated_size,rval); | |
| 67 | |
| 68 if(ALLOC_MEMSET){ | |
| 69 //We init them to nan to make sure we catch more debug case. | |
| 70 cudaMemset(rval, 0xFF, size); | |
| 71 //printf("MEMSET\n"); | |
| 72 } | |
| 73 return rval; | |
| 74 } | |
| 75 int device_free(void *ptr) | |
| 76 { | |
| 77 // if there is no gpu context, the call to cudaFree will fail; skip it entirely | |
| 78 if(!g_gpu_context_active) { | |
| 79 return 0; | |
| 80 } | |
| 81 cudaError_t err = cudaFree(ptr); | |
| 82 if (cudaSuccess != err) | |
| 83 { | |
| 84 #if COMPUTE_GPU_MEM_USED | |
| 85 fprintf(stderr, "Error freeing device pointer %p (%s).%d byte already allocated\n", ptr, cudaGetErrorString(err), _allocated_size); | |
| 86 #else | |
| 87 fprintf(stderr, "Error freeing device pointer %p (%s).\n", ptr, cudaGetErrorString(err)); | |
| 88 #endif | |
| 89 PyErr_Format(PyExc_MemoryError, "error freeing device pointer %p (%s)", ptr, cudaGetErrorString(err)); | |
| 90 return -1; | |
| 91 } | |
| 92 _outstanding_mallocs[0] -= (ptr != NULL); | |
| 93 #if COMPUTE_GPU_MEM_USED | |
| 94 int i=0; | |
| 95 size_t total_freed = 0; | |
| 96 for(;i<TABLE_SIZE;i++) | |
| 97 if(_alloc_size_table[i].ptr==ptr){ | |
| 98 _allocated_size -= _alloc_size_table[i].size; | |
| 99 total_freed += _alloc_size_table[i].size; | |
| 100 _alloc_size_table[i].ptr=0; | |
| 101 _alloc_size_table[i].size=0; | |
| 102 | |
| 103 break; | |
| 104 } | |
| 105 if(i==TABLE_SIZE) | |
| 106 printf("Unallocated unknow size!\n"); | |
| 107 //fprintf(stderr, "freed %li bytes of device memory (%s). %d already allocated, ptr=%p\n", (long)total_freed, cudaGetErrorString(err),_allocated_size,ptr); | |
| 108 #endif | |
| 109 return 0; | |
| 110 } | |
| 111 static PyObject * | |
| 112 outstanding_mallocs(PyObject* self, PyObject * args) | |
| 113 { | |
| 114 return PyInt_FromLong(_outstanding_mallocs[0]); | |
| 115 } | |
| 116 | |
| 117 ///////////////////////// | |
| 118 // Static helper methods | |
| 119 ///////////////////////// | |
| 120 | |
| 121 static void | |
| 122 CudaNdarray_null_init(CudaNdarray*self) | |
| 123 { | |
| 124 self->base = NULL; | |
| 125 self->nd = -1; | |
| 126 self->host_structure = NULL; | |
| 127 self->data_allocated = 0; | |
| 128 self->dev_structure_fresh = 1; | |
| 129 self->dev_structure = NULL; | |
| 130 self->devdata = NULL; | |
| 131 } | |
| 132 | |
| 133 static int | |
| 134 CudaNdarray_uninit(CudaNdarray*self) | |
| 135 { | |
| 136 int rval = 0; | |
| 137 if (self->data_allocated) { | |
| 138 assert(self->devdata); | |
| 139 if (device_free(self->devdata)) | |
| 140 { | |
| 141 fprintf(stderr, | |
| 142 "!!!! error freeing device memory %p (self=%p)\n", | |
| 143 self->devdata, self); | |
| 144 rval = -1; | |
| 145 } | |
| 146 self->devdata = NULL; | |
| 147 self->data_allocated = 0; | |
| 148 } | |
| 149 if (self->dev_structure) | |
| 150 { | |
| 151 if (device_free(self->dev_structure)) | |
| 152 { | |
| 153 fprintf(stderr, | |
| 154 "!!!! error freeing dev_structure memory %p (self=%p)\n", | |
| 155 self->dev_structure, self); | |
| 156 rval = -1; | |
| 157 } | |
| 158 self->dev_structure = NULL; | |
| 159 } | |
| 160 if (self->host_structure) | |
| 161 { | |
| 162 free(self->host_structure); | |
| 163 self->host_structure = NULL; | |
| 164 } | |
| 165 self->nd = -1; | |
| 166 Py_XDECREF(self->base); | |
| 167 self->base = NULL; | |
| 168 return rval; | |
| 169 } | |
| 170 | |
| 171 | |
| 172 //make the rightmost coords change fastest | |
| 173 //TODO: why does a downward for-loop not work???? | |
| 174 //TODO: use the log2_dims and driver code to remove / and % | |
| 175 //TODO: skip the last division (when d == 0) | |
| 176 #define decl_k_elemwise_unary_rowmajor(name, F) \ | |
| 177 __global__ void name (unsigned int numEls, \ | |
| 178 unsigned int nd, \ | |
| 179 const int * dim, \ | |
| 180 const float * a_data, const int * a_str, \ | |
| 181 float * z_data, const int * z_str) \ | |
| 182 { \ | |
| 183 const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x; \ | |
| 184 const unsigned int numThreads = blockDim.x * gridDim.x; \ | |
| 185 \ | |
| 186 for (unsigned int i = idx; i < numEls; i += numThreads) \ | |
| 187 { \ | |
| 188 unsigned int ii = i; \ | |
| 189 const float * a_i = a_data; \ | |
| 190 float * z_i = z_data; \ | |
| 191 for (unsigned int _d = 0; _d < nd; ++_d) \ | |
| 192 { \ | |
| 193 unsigned int d = nd - _d-1; \ | |
| 194 /* i_d used to be unsigned, but their is a bug in nvcc 3.0. making it signed fix the bug.*/\ | |
| 195 int i_d = ii % dim[d]; /* i_d is our position in the d'th dimension */ \ | |
| 196 ii = ii / dim[d]; \ | |
| 197 a_i += i_d * a_str[d]; /* increment our a and z pointers by i_d elements */ \ | |
| 198 z_i += i_d * z_str[d]; \ | |
| 199 } \ | |
| 200 z_i[0] = F(a_i[0]); \ | |
| 201 } \ | |
| 202 } | |
| 203 | |
| 204 template<typename T> __device__ T unary_copy(T a) { return a; } | |
| 205 decl_k_elemwise_unary_rowmajor(k_elemwise_unary_rowmajor_copy, unary_copy<float>) | |
| 206 | |
| 207 template<typename T> __device__ T unary_exp(T a) { return exp(a); } | |
| 208 decl_k_elemwise_unary_rowmajor(k_elemwise_unary_rowmajor_exp, unary_exp<float>) | |
| 209 | |
| 210 ///////////////////////////// | |
| 211 // Satisfying reqs to be Type | |
| 212 ///////////////////////////// | |
| 213 | |
| 214 //DON'T use directly(if their is other CudaNdarray that point to it, it will cause problem)! use Py_DECREF() instead | |
| 215 static void | |
| 216 CudaNdarray_dealloc(CudaNdarray* self) | |
| 217 { | |
| 218 if (0) std::cerr << "CudaNdarray dealloc " << self << " " << self->devdata << '\n'; | |
| 219 if(self->ob_refcnt>1) | |
| 220 printf("WARNING:CudaNdarray_dealloc called when their is still active reference to it.\n"); | |
| 221 CudaNdarray_uninit(self); | |
| 222 self->ob_type->tp_free((PyObject*)self); | |
| 223 --_outstanding_mallocs[1]; | |
| 224 if (0) | |
| 225 { | |
| 226 fprintf(stderr, "device_malloc_counts: (device) %i (obj) %i\n", | |
| 227 _outstanding_mallocs[0], | |
| 228 _outstanding_mallocs[1]); | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 static PyObject * | |
| 233 CudaNdarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
| 234 { | |
| 235 CudaNdarray *self; | |
| 236 | |
| 237 self = (CudaNdarray *)type->tp_alloc(type, 0); | |
| 238 if (self != NULL) | |
| 239 { | |
| 240 CudaNdarray_null_init(self); | |
| 241 ++_outstanding_mallocs[1]; | |
| 242 } | |
| 243 return (PyObject *)self; | |
| 244 } | |
| 245 static int | |
| 246 CudaNdarray_init(CudaNdarray *self, PyObject *args, PyObject *kwds) | |
| 247 { | |
| 248 PyObject *arr=NULL; | |
| 249 | |
| 250 if (! PyArg_ParseTuple(args, "O", &arr)) | |
| 251 return -1; | |
| 252 if (! PyArray_Check(arr)) | |
| 253 { | |
| 254 PyErr_SetString(PyExc_TypeError, "PyArray arg required"); | |
| 255 return -1; | |
| 256 } | |
| 257 int rval = CudaNdarray_CopyFromArray(self, (PyArrayObject*)arr); | |
| 258 return rval; | |
| 259 } | |
| 260 static PyMemberDef CudaNdarray_members[] = | |
| 261 { | |
| 262 /* | |
| 263 {"first", T_OBJECT_EX, offsetof(CudaNdarray, first), 0, | |
| 264 "first name"}, | |
| 265 {"last", T_OBJECT_EX, offsetof(CudaNdarray, last), 0, | |
| 266 "last name"}, | |
| 267 {"number", T_INT, offsetof(CudaNdarray, number), 0, | |
| 268 "noddy number"}, | |
| 269 */ | |
| 270 {NULL} /* Sentinel */ | |
| 271 }; | |
| 272 | |
| 273 PyObject * CudaNdarray_CreateArrayObj(CudaNdarray * self) | |
| 274 { | |
| 275 int verbose = 0; | |
| 276 if(self->nd>=0 && CudaNdarray_SIZE(self)==0){ | |
| 277 npy_intp * npydims = (npy_intp*)malloc(self->nd * sizeof(npy_intp)); | |
| 278 assert (npydims); | |
| 279 for (int i = 0; i < self->nd; ++i) npydims[i] = (npy_intp)(CudaNdarray_HOST_DIMS(self)[i]); | |
| 280 PyObject * rval = PyArray_SimpleNew(self->nd, npydims, REAL_TYPENUM); | |
| 281 free(npydims); | |
| 282 if (!rval){ | |
| 283 return NULL; | |
| 284 } | |
| 285 assert (PyArray_ITEMSIZE(rval) == sizeof(real)); | |
| 286 return rval; | |
| 287 } | |
| 288 if ((self->nd < 0) || (self->devdata == 0)) | |
| 289 { | |
| 290 PyErr_SetString(PyExc_ValueError, "can't copy from un-initialized CudaNdarray"); | |
| 291 return NULL; | |
| 292 } | |
| 293 CudaNdarray * contiguous_self = NULL; | |
| 294 if (CudaNdarray_is_c_contiguous(self)) | |
| 295 { | |
| 296 contiguous_self = self; | |
| 297 Py_INCREF(contiguous_self); | |
| 298 if (verbose) std::cerr << "CreateArrayObj already contiguous" << contiguous_self << '\n'; | |
| 299 } | |
| 300 else | |
| 301 { | |
| 302 contiguous_self = (CudaNdarray*)CudaNdarray_Copy(self); | |
| 303 if (verbose) std::cerr << "CreateArrayObj created contiguous" << contiguous_self << '\n'; | |
| 304 } | |
| 305 if (!contiguous_self) | |
| 306 { | |
| 307 return NULL; | |
| 308 } | |
| 309 | |
| 310 npy_intp * npydims = (npy_intp*)malloc(self->nd * sizeof(npy_intp)); | |
| 311 assert (npydims); | |
| 312 for (int i = 0; i < self->nd; ++i) | |
| 313 npydims[i] = (npy_intp)(CudaNdarray_HOST_DIMS(self)[i]); | |
| 314 PyObject * rval = PyArray_SimpleNew(self->nd, npydims, REAL_TYPENUM); | |
| 315 free(npydims); | |
| 316 if (!rval) | |
| 317 { | |
| 318 Py_DECREF(contiguous_self); | |
| 319 return NULL; | |
| 320 } | |
| 321 | |
| 322 assert (PyArray_ITEMSIZE(rval) == sizeof(real)); | |
| 323 | |
| 324 cublasGetVector(PyArray_SIZE(rval), sizeof(real), | |
| 325 contiguous_self->devdata, 1, | |
| 326 PyArray_DATA(rval), 1); | |
| 327 CNDA_THREAD_SYNC; | |
| 328 | |
| 329 if (CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 330 { | |
| 331 PyErr_SetString(PyExc_RuntimeError, "error copying data to host"); | |
| 332 Py_DECREF(rval); | |
| 333 rval = NULL; | |
| 334 } | |
| 335 | |
| 336 Py_DECREF(contiguous_self); | |
| 337 return rval; | |
| 338 } | |
| 339 | |
| 340 // TODO-- we have two functions here, ZEROS and Zeros. | |
| 341 // ZEROS is meant to be called just from C code (you don't need to pass it PyObject * s) | |
| 342 // but this naming is very weird, makes it look like a macro | |
| 343 // we should figure out the correct convention and change to that | |
| 344 PyObject* CudaNdarray_ZEROS(int n, int * dims) | |
| 345 { | |
| 346 | |
| 347 int total_elements = 1; | |
| 348 for(int i=0;i<n;i++) | |
| 349 total_elements*=dims[i]; | |
| 350 | |
| 351 // total_elements now contains the size of the array, in reals | |
| 352 int total_size = total_elements * sizeof(real); | |
| 353 | |
| 354 CudaNdarray* rval = (CudaNdarray*)CudaNdarray_New(); | |
| 355 if (!rval) | |
| 356 { | |
| 357 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray_ZEROS: call to New failed"); | |
| 358 return NULL; | |
| 359 } | |
| 360 | |
| 361 if (CudaNdarray_alloc_contiguous(rval, n, dims)) | |
| 362 { | |
| 363 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray_ZEROS: allocation failed."); | |
| 364 Py_DECREF(rval); | |
| 365 return NULL; | |
| 366 } | |
| 367 | |
| 368 // Fill with zeros | |
| 369 //fprintf(stdout, "Sizeof: %d\n", total_size); | |
| 370 if (cudaSuccess != cudaMemset(rval->devdata, 0, total_size)) | |
| 371 { | |
| 372 PyErr_Format(PyExc_MemoryError, "CudaNdarray_ZEROS: Error memsetting %d bytes of device memory.", total_size); | |
| 373 Py_DECREF(rval); | |
| 374 return NULL; | |
| 375 } | |
| 376 | |
| 377 if (cnda_copy_structure_to_device(rval)) | |
| 378 { | |
| 379 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray_ZEROS: syncing structure to device failed"); | |
| 380 Py_DECREF(rval); | |
| 381 return NULL; | |
| 382 } | |
| 383 return (PyObject*) rval; | |
| 384 } | |
| 385 | |
| 386 // declared as a static method (hence "dummy" is not used) | |
| 387 // Based on _Copy and _dimshuffle | |
| 388 PyObject* CudaNdarray_Zeros(PyObject* dummy, PyObject* shape) | |
| 389 { | |
| 390 if(!PySequence_Check(shape)) | |
| 391 { | |
| 392 PyErr_SetString(PyExc_TypeError, "shape argument must be a sequence"); | |
| 393 return NULL; | |
| 394 } | |
| 395 | |
| 396 int shplen = PySequence_Length(shape); | |
| 397 | |
| 398 if (shplen == 0) | |
| 399 { | |
| 400 PyErr_SetString(PyExc_ValueError, | |
| 401 "CudaNdarray_Zeros: empty shape not allowed"); | |
| 402 return NULL; | |
| 403 } | |
| 404 | |
| 405 int* newdims = (int *)malloc(sizeof(int) * shplen); | |
| 406 | |
| 407 if (!newdims) | |
| 408 { | |
| 409 PyErr_SetString(PyExc_MemoryError, | |
| 410 "CudaNdarray_Zeros: Failed to allocate temporary space"); | |
| 411 return NULL; | |
| 412 } | |
| 413 | |
| 414 // start from the end to compute strides | |
| 415 for (int i = shplen-1; i >= 0; --i) | |
| 416 { | |
| 417 PyObject* shp_el_obj = PySequence_GetItem(shape, i); | |
| 418 if(shp_el_obj == NULL) | |
| 419 { | |
| 420 // shouldn't happen since we checked length before... | |
| 421 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray_Zeros: Index out of bound in sequence"); | |
| 422 free(newdims); | |
| 423 return NULL; | |
| 424 } | |
| 425 | |
| 426 int shp_el = PyInt_AsLong(shp_el_obj); | |
| 427 Py_DECREF(shp_el_obj); | |
| 428 | |
| 429 if (shp_el <= 0) | |
| 430 { | |
| 431 PyErr_SetString(PyExc_ValueError, "CudaNdarray_Zeros: shape must not contain 0 (or negative value) for size of a dimension"); | |
| 432 free(newdims); | |
| 433 return NULL; | |
| 434 } | |
| 435 | |
| 436 newdims[i] = shp_el; | |
| 437 } | |
| 438 | |
| 439 PyObject* rval = CudaNdarray_ZEROS(shplen,newdims); | |
| 440 | |
| 441 free(newdims); | |
| 442 | |
| 443 return (PyObject*)rval; | |
| 444 } | |
| 445 | |
| 446 | |
| 447 | |
| 448 | |
| 449 | |
| 450 PyObject * CudaNdarray_Copy(CudaNdarray * self) | |
| 451 { | |
| 452 PyObject * rval = CudaNdarray_New(); | |
| 453 if ((!rval) || (-1 == self->nd)) | |
| 454 { | |
| 455 return rval; | |
| 456 } | |
| 457 if (CudaNdarray_alloc_contiguous((CudaNdarray*)rval, self->nd, CudaNdarray_HOST_DIMS(self))) | |
| 458 { | |
| 459 Py_DECREF(rval); | |
| 460 return NULL; | |
| 461 } | |
| 462 if (CudaNdarray_CopyFromCudaNdarray((CudaNdarray*)rval, self)) | |
| 463 { | |
| 464 Py_DECREF(rval); | |
| 465 return NULL; | |
| 466 } | |
| 467 return rval; | |
| 468 } | |
| 469 PyObject * CudaNdarray_DeepCopy(CudaNdarray * self, PyObject * memo) | |
| 470 { | |
| 471 assert(PyDict_Check(memo)); | |
| 472 PyObject * selfkey = PyInt_FromLong((long)self); | |
| 473 assert(selfkey); | |
| 474 if (PyDict_Contains(memo, selfkey)) | |
| 475 { | |
| 476 PyObject * rval = PyDict_GetItem(memo, selfkey); | |
| 477 Py_DECREF(selfkey); | |
| 478 Py_XINCREF(rval); | |
| 479 return rval; | |
| 480 } | |
| 481 else | |
| 482 { | |
| 483 PyObject * rval = CudaNdarray_Copy(self); | |
| 484 if (0) std::cerr << "DeepCopy created " << rval << " devdata " << ((CudaNdarray*)rval)->devdata << "\n"; | |
| 485 if (NULL == rval) | |
| 486 { | |
| 487 Py_DECREF(selfkey); | |
| 488 return NULL; | |
| 489 } | |
| 490 if (PyDict_SetItem(memo, selfkey, rval)) | |
| 491 { | |
| 492 Py_DECREF(rval); | |
| 493 Py_DECREF(selfkey); | |
| 494 return NULL; | |
| 495 } | |
| 496 Py_DECREF(selfkey); | |
| 497 return rval; | |
| 498 } | |
| 499 } | |
| 500 PyObject * CudaNdarray_ReduceSum(CudaNdarray * self, PyObject * py_reduce_mask) | |
| 501 { | |
| 502 if (!PySequence_Check(py_reduce_mask)) | |
| 503 { | |
| 504 PyErr_SetString(PyExc_TypeError, "reduce_mask must be sequence of ints"); | |
| 505 return NULL; | |
| 506 } | |
| 507 int len = PySequence_Length(py_reduce_mask); | |
| 508 if (len != self->nd) | |
| 509 { | |
| 510 PyErr_SetString(PyExc_TypeError, "length of reduce_mask must match self->nd"); | |
| 511 return NULL; | |
| 512 } | |
| 513 CudaNdarray * self_sum = (CudaNdarray*)CudaNdarray_New(); | |
| 514 if (!self_sum) | |
| 515 { | |
| 516 return NULL; | |
| 517 } | |
| 518 //TODO: allocate a fixed size dimshuffle_pattern_cache on the stack, | |
| 519 // and use it if it is big enough. | |
| 520 int * dimshuffle_pattern = (int*)malloc(len * 2 * sizeof(int)); | |
| 521 int * sum_dims = dimshuffle_pattern + len; | |
| 522 int n_remaining_dims = 0; | |
| 523 if (!dimshuffle_pattern) | |
| 524 { | |
| 525 Py_DECREF(self_sum); | |
| 526 PyErr_SetString(PyExc_MemoryError, "failed to alloc internal storage"); | |
| 527 return NULL; | |
| 528 } | |
| 529 for (int i = 0; i < len; ++i) | |
| 530 { | |
| 531 PyObject *o_i = PySequence_GetItem(py_reduce_mask, i); | |
| 532 int o_i_int = PyInt_AsLong(o_i); | |
| 533 Py_XDECREF(o_i); | |
| 534 if (PyErr_Occurred()) | |
| 535 { | |
| 536 Py_DECREF(self_sum); | |
| 537 free(dimshuffle_pattern); | |
| 538 return NULL; | |
| 539 } | |
| 540 if (o_i_int) // this is a dimension over which we are reducing | |
| 541 { | |
| 542 sum_dims[i] = 1; | |
| 543 } | |
| 544 else | |
| 545 { | |
| 546 sum_dims[i] = CudaNdarray_HOST_DIMS(self)[i]; | |
| 547 dimshuffle_pattern[n_remaining_dims++] = i; | |
| 548 } | |
| 549 } | |
| 550 if (0 || CudaNdarray_alloc_contiguous(self_sum, len, sum_dims) | |
| 551 || CudaNdarray_reduce_sum(self_sum, self) | |
| 552 || CudaNdarray_dimshuffle(self_sum, n_remaining_dims, dimshuffle_pattern)) | |
| 553 { | |
| 554 Py_DECREF(self_sum); | |
| 555 free(dimshuffle_pattern); | |
| 556 return NULL; | |
| 557 } | |
| 558 free(dimshuffle_pattern); | |
| 559 return (PyObject*)self_sum; | |
| 560 } | |
| 561 | |
| 562 __global__ void k_copy_reshape_rowmajor(unsigned int numEls, | |
| 563 unsigned int a_nd, const float * a_data, const int * a_dim, const int * a_str, | |
| 564 unsigned int z_nd, float * z_data, const int * z_dim, const int * z_str) | |
| 565 { | |
| 566 const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x; | |
| 567 const unsigned int numThreads = blockDim.x * gridDim.x; | |
| 568 | |
| 569 for (unsigned int i = idx; i < numEls; i += numThreads) | |
| 570 { | |
| 571 const float * a_i = a_data; | |
| 572 unsigned int a_ii = i; | |
| 573 for (unsigned int _d = 0; _d < a_nd; ++_d) //make the rightmost coords change fastest | |
| 574 { | |
| 575 unsigned int d = a_nd - _d-1; | |
| 576 unsigned int a_i_d = a_ii % a_dim[d]; | |
| 577 a_ii = a_ii / a_dim[d]; | |
| 578 a_i += a_i_d * a_str[d]; | |
| 579 } | |
| 580 unsigned int z_ii = i; | |
| 581 float * z_i = z_data; | |
| 582 for (unsigned int _d = 0; _d < z_nd; ++_d) //make the rightmost coords change fastest | |
| 583 { | |
| 584 unsigned int d = z_nd - _d-1; | |
| 585 //i tried to make the for loop count down, but it didn't work!? | |
| 586 unsigned int z_i_d = z_ii % z_dim[d]; | |
| 587 z_i += z_i_d * z_str[d]; | |
| 588 z_ii = z_ii / z_dim[d]; | |
| 589 } | |
| 590 z_i[0] = a_i[0]; //copy one lousy float! | |
| 591 } | |
| 592 } | |
| 593 PyObject * CudaNdarray_Reshape(CudaNdarray * self, PyObject * shape) | |
| 594 { | |
| 595 // check shape tuple | |
| 596 unsigned int rval_nd; | |
| 597 unsigned int * rval_dims; | |
| 598 unsigned int rval_size = 1; | |
| 599 | |
| 600 if (PyTuple_Check(shape)){ | |
| 601 // copy shape to integer array | |
| 602 rval_nd = PyTuple_Size(shape); | |
| 603 }else if (PyInt_Check(shape)){ | |
| 604 rval_nd = 1; | |
| 605 }else{ | |
| 606 PyErr_SetString(PyExc_TypeError, "shape must be tuple of integers or an integer"); | |
| 607 return NULL; | |
| 608 } | |
| 609 rval_dims = (unsigned int*)malloc(rval_nd * sizeof(int)); | |
| 610 | |
| 611 if(PyTuple_Check(shape)){ | |
| 612 for (int i = 0; i < rval_nd; ++i) | |
| 613 { | |
| 614 rval_dims[i] = PyInt_AsLong(PyTuple_GetItem(shape, i)); //GetItem returns borrowed reference | |
| 615 if (PyErr_Occurred()) //error in AsLong | |
| 616 { | |
| 617 free(rval_dims); | |
| 618 return NULL; | |
| 619 } | |
| 620 if(rval_dims[i]<=0){ | |
| 621 PyErr_Format(PyExc_ValueError, "Reshape has invalid dimension %i (must be >0)",rval_dims[i]); | |
| 622 free(rval_dims); | |
| 623 return NULL; | |
| 624 } | |
| 625 rval_size = rval_size * rval_dims[i]; | |
| 626 } | |
| 627 }else{ | |
| 628 rval_size = PyInt_AsLong(shape); | |
| 629 rval_dims[0] = rval_size; | |
| 630 } | |
| 631 // calculate new size, assert same as old size | |
| 632 if (rval_size != CudaNdarray_SIZE(self)) | |
| 633 { | |
| 634 PyErr_Format(PyExc_ValueError, "size must remain unchanged, changed from %i to %i", CudaNdarray_SIZE(self), rval_size); | |
| 635 free(rval_dims); | |
| 636 return NULL; | |
| 637 } | |
| 638 if (rval_size==0) | |
| 639 { | |
| 640 PyObject * rval = CudaNdarray_NewDims(rval_nd, rval_dims); | |
| 641 free(rval_dims); | |
| 642 return rval; | |
| 643 } | |
| 644 | |
| 645 if(CudaNdarray_is_c_contiguous(self)) | |
| 646 { | |
| 647 //return a view, not a copy | |
| 648 CudaNdarray * rval = (CudaNdarray * )CudaNdarray_New(rval_nd); | |
| 649 | |
| 650 if (!rval || 0 != rval->data_allocated | |
| 651 ||CudaNdarray_set_device_data(rval, CudaNdarray_DEV_DATA(self), self)) | |
| 652 { | |
| 653 Py_XDECREF(rval); | |
| 654 free(rval_dims); | |
| 655 return NULL; | |
| 656 } | |
| 657 //set dim and stride | |
| 658 int size = 1; | |
| 659 for (int i = rval_nd-1; i >= 0; --i) | |
| 660 { | |
| 661 CudaNdarray_set_stride(rval, i, (rval_dims[i] == 1) ? 0 : size); | |
| 662 CudaNdarray_set_dim(rval, i, rval_dims[i]); | |
| 663 size = size * rval_dims[i]; | |
| 664 } | |
| 665 free(rval_dims); | |
| 666 return (PyObject*)rval; | |
| 667 } | |
| 668 | |
| 669 // allocate new space (TODO: test to see if we can re-use old one) | |
| 670 CudaNdarray * rval = (CudaNdarray * )CudaNdarray_New(); | |
| 671 if (!rval || CudaNdarray_alloc_contiguous(rval, rval_nd, rval_dims)){ | |
| 672 Py_XDECREF(rval); | |
| 673 free(rval_dims); | |
| 674 return NULL; | |
| 675 } | |
| 676 | |
| 677 // call worker routine | |
| 678 unsigned int threads_per_block = std::min(rval_size, (unsigned int)NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 679 unsigned int n_blocks = std::min(ceil_intdiv(rval_size,threads_per_block), (unsigned int)NUM_VECTOR_OP_BLOCKS); | |
| 680 k_copy_reshape_rowmajor<<<n_blocks,threads_per_block>>>( | |
| 681 rval_size, | |
| 682 self->nd, | |
| 683 CudaNdarray_DEV_DATA(self), CudaNdarray_DEV_DIMS(self), CudaNdarray_DEV_STRIDES(self), | |
| 684 rval->nd, | |
| 685 CudaNdarray_DEV_DATA(rval), CudaNdarray_DEV_DIMS(rval), CudaNdarray_DEV_STRIDES(rval)); | |
| 686 | |
| 687 CNDA_THREAD_SYNC; | |
| 688 cudaError_t err = cudaGetLastError(); | |
| 689 if( cudaSuccess != err) | |
| 690 { | |
| 691 Py_DECREF(rval); | |
| 692 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_copy_reshape_rowmajor", cudaGetErrorString(err)); | |
| 693 free(rval_dims); | |
| 694 return NULL; | |
| 695 } | |
| 696 free(rval_dims); | |
| 697 return (PyObject*)rval; | |
| 698 } | |
| 699 PyObject * CudaNdarray_View(CudaNdarray * self) | |
| 700 { | |
| 701 CudaNdarray * rval = (CudaNdarray*)CudaNdarray_New(self->nd); | |
| 702 if (!rval || CudaNdarray_set_device_data(rval, CudaNdarray_DEV_DATA(self), self)) | |
| 703 { | |
| 704 Py_XDECREF(rval); | |
| 705 rval = NULL; | |
| 706 } | |
| 707 else | |
| 708 { | |
| 709 for (int i = 0; i < self->nd; ++i) | |
| 710 { | |
| 711 CudaNdarray_set_dim(rval, i, CudaNdarray_HOST_DIMS(self)[i]); | |
| 712 CudaNdarray_set_stride(rval, i, CudaNdarray_HOST_STRIDES(self)[i]); | |
| 713 } | |
| 714 } | |
| 715 return (PyObject*)rval; | |
| 716 } | |
| 717 PyObject * CudaNdarray_SetStride(CudaNdarray * self, PyObject *args) | |
| 718 { | |
| 719 int pos, stride; | |
| 720 if (! PyArg_ParseTuple(args, "ii", &pos, &stride)) | |
| 721 return NULL; | |
| 722 if ((pos < 0) || (pos >= self->nd)) | |
| 723 { | |
| 724 PyErr_Format(PyExc_ValueError, "position argument out of legal range [0, %i)", self->nd); | |
| 725 return NULL; | |
| 726 } | |
| 727 CudaNdarray_set_stride(self, pos, stride); | |
| 728 if (cnda_copy_structure_to_device(self)) | |
| 729 { | |
| 730 return NULL; | |
| 731 } | |
| 732 Py_INCREF(Py_None); | |
| 733 return Py_None; | |
| 734 } | |
| 735 PyObject * CudaNdarray_SetShapeI(CudaNdarray * self, PyObject *args) | |
| 736 { | |
| 737 int pos, dim; | |
| 738 if (! PyArg_ParseTuple(args, "ii", &pos, &dim)) | |
| 739 return NULL; | |
| 740 if ((pos < 0) || (pos >= self->nd)) | |
| 741 { | |
| 742 PyErr_Format(PyExc_ValueError, "position argument out of legal range [0, %i)", self->nd); | |
| 743 return NULL; | |
| 744 } | |
| 745 CudaNdarray_set_dim(self, pos, dim); | |
| 746 if (cnda_copy_structure_to_device(self)) | |
| 747 { | |
| 748 return NULL; | |
| 749 } | |
| 750 Py_INCREF(Py_None); | |
| 751 return Py_None; | |
| 752 } | |
| 753 | |
| 754 static PyObject * | |
| 755 CudaNdarray_exp(CudaNdarray* self) | |
| 756 { | |
| 757 CudaNdarray * rval = (CudaNdarray *)CudaNdarray_New(); | |
| 758 if ((NULL == rval) || CudaNdarray_alloc_contiguous(rval, self->nd, CudaNdarray_HOST_DIMS(self))) | |
| 759 { | |
| 760 Py_XDECREF(rval); | |
| 761 return NULL; | |
| 762 } | |
| 763 unsigned int size = 1; | |
| 764 for (int i = 0; i < self->nd; i++) | |
| 765 { | |
| 766 size *= (unsigned int) CudaNdarray_HOST_DIMS(self)[i]; | |
| 767 } | |
| 768 unsigned int threads_per_block = std::min(size, (unsigned int)NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 769 unsigned int n_blocks = std::min(ceil_intdiv(size,threads_per_block), (unsigned int)NUM_VECTOR_OP_BLOCKS); | |
| 770 k_elemwise_unary_rowmajor_exp<<<n_blocks,threads_per_block>>>(size, self->nd, CudaNdarray_DEV_DIMS(self), | |
| 771 CudaNdarray_DEV_DATA(self), CudaNdarray_DEV_STRIDES(self), | |
| 772 CudaNdarray_DEV_DATA(rval), CudaNdarray_DEV_STRIDES(rval)); | |
| 773 | |
| 774 //TODO: don't do this right away, do it when we need the result | |
| 775 CNDA_THREAD_SYNC; | |
| 776 cudaError_t err = cudaGetLastError(); | |
| 777 if( cudaSuccess != err) | |
| 778 { | |
| 779 Py_DECREF(rval); | |
| 780 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "kExp", cudaGetErrorString(err)); | |
| 781 return NULL; | |
| 782 } | |
| 783 | |
| 784 return (PyObject*)rval; | |
| 785 } | |
| 786 static PyMethodDef CudaNdarray_methods[] = | |
| 787 { | |
| 788 {"__array__", | |
| 789 (PyCFunction)CudaNdarray_CreateArrayObj, METH_NOARGS, | |
| 790 "Copy from the device to a numpy ndarray"}, | |
| 791 {"__copy__", | |
| 792 (PyCFunction)CudaNdarray_View, METH_NOARGS, | |
| 793 "Create a shallow copy of this object. used by module copy"}, | |
| 794 {"__deepcopy__", | |
| 795 (PyCFunction)CudaNdarray_DeepCopy, METH_O, | |
| 796 "Create a copy of this object"}, | |
| 797 {"zeros", | |
| 798 (PyCFunction)CudaNdarray_Zeros, METH_STATIC, | |
| 799 "Create a new CudaNdarray with specified shape, filled with zeros."}, | |
| 800 {"copy", | |
| 801 (PyCFunction)CudaNdarray_Copy, METH_NOARGS, | |
| 802 "Create a copy of this object"}, | |
| 803 {"reduce_sum", | |
| 804 (PyCFunction)CudaNdarray_ReduceSum, METH_O, | |
| 805 "Reduce over the given dimensions by summation"}, | |
| 806 {"exp", | |
| 807 (PyCFunction)CudaNdarray_exp, METH_NOARGS, | |
| 808 "Return the exponential of all elements"}, | |
| 809 {"reshape", | |
| 810 (PyCFunction)CudaNdarray_Reshape, METH_O, | |
| 811 "Return a reshaped view (or copy) of this ndarray\n\ | |
| 812 The required argument is a tuple of integers specifying the shape of the new ndarray."}, | |
| 813 {"view", | |
| 814 (PyCFunction)CudaNdarray_View, METH_NOARGS, | |
| 815 "Return an alias of this ndarray"}, | |
| 816 {"_set_stride", | |
| 817 (PyCFunction)CudaNdarray_SetStride, METH_VARARGS, | |
| 818 "For integer arguments (i, s), set the 'i'th stride to 's'"}, | |
| 819 {"_set_shape_i", | |
| 820 (PyCFunction)CudaNdarray_SetShapeI, METH_VARARGS, | |
| 821 "For integer arguments (i, s), set the 'i'th shape to 's'"}, | |
| 822 {NULL, NULL, NULL, NULL} /* Sentinel */ | |
| 823 }; | |
| 824 | |
| 825 | |
| 826 //////////////////// | |
| 827 // Number protocol | |
| 828 //////////////////// | |
| 829 | |
| 830 __global__ void kAdd_contiguous(float* a, float* b, float* dest, unsigned int numEls) { | |
| 831 const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x; | |
| 832 const unsigned int numThreads = blockDim.x * gridDim.x; | |
| 833 | |
| 834 for (unsigned int i = idx; i < numEls; i += numThreads) { | |
| 835 dest[i] = a[i] + b[i]; | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 // Will be called by __add__ in Python | |
| 840 static PyObject * | |
| 841 CudaNdarray_add(PyObject* py_self, PyObject * py_other) | |
| 842 { | |
| 843 if (! CudaNdarray_Check(py_self)) { | |
| 844 PyErr_SetString(PyExc_TypeError, "need a CudaNdarray on left"); | |
| 845 return NULL; | |
| 846 } | |
| 847 if (! CudaNdarray_Check(py_other)) { | |
| 848 PyErr_SetString(PyExc_TypeError, "need a CudaNdarray on right"); | |
| 849 return NULL; | |
| 850 } | |
| 851 CudaNdarray * self = (CudaNdarray *)py_self; | |
| 852 CudaNdarray * other = (CudaNdarray *)py_other; | |
| 853 if(!CudaNdarray_is_c_contiguous(self) || !CudaNdarray_is_c_contiguous(other)){ | |
| 854 PyErr_SetString(PyExc_TypeError, "We have implementet only the c_contiguous version for now."); | |
| 855 return NULL; | |
| 856 } | |
| 857 | |
| 858 //standard elemwise size checks | |
| 859 if (self->nd != other->nd) | |
| 860 { | |
| 861 PyErr_SetString(PyExc_TypeError, "CudaNdarray_add: need same number of dims"); | |
| 862 return NULL; | |
| 863 } | |
| 864 //standard elemwise dim checks | |
| 865 unsigned int size = 1; | |
| 866 for (int i = 0; i< self->nd; ++i) | |
| 867 { | |
| 868 if (CudaNdarray_HOST_DIMS(self)[i] != CudaNdarray_HOST_DIMS(other)[i]) | |
| 869 { | |
| 870 PyErr_SetString(PyExc_TypeError, "need same dimensions"); | |
| 871 return NULL; | |
| 872 } | |
| 873 size *= (unsigned int) CudaNdarray_HOST_DIMS(self)[i]; | |
| 874 } | |
| 875 CudaNdarray * rval = (CudaNdarray *)CudaNdarray_New(); | |
| 876 if (!rval || CudaNdarray_alloc_contiguous(rval, self->nd, CudaNdarray_HOST_DIMS(self))) | |
| 877 { | |
| 878 Py_XDECREF(rval); | |
| 879 return NULL; | |
| 880 } | |
| 881 | |
| 882 if(CudaNdarray_SIZE((CudaNdarray *)py_self)==0 && CudaNdarray_SIZE((CudaNdarray *)py_other)==0){ | |
| 883 return (PyObject *) rval; | |
| 884 } | |
| 885 | |
| 886 int threads_per_block = std::min(size, (unsigned int)NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 887 int n_blocks = std::min(ceil_intdiv(size,(unsigned int)threads_per_block), (unsigned int)NUM_VECTOR_OP_BLOCKS); | |
| 888 kAdd_contiguous<<<n_blocks,threads_per_block>>>( | |
| 889 self->devdata, other->devdata, rval->devdata, size); | |
| 890 CNDA_THREAD_SYNC; | |
| 891 cudaError_t err = cudaGetLastError(); | |
| 892 if( cudaSuccess != err) | |
| 893 { | |
| 894 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "kAdd", cudaGetErrorString(err)); | |
| 895 Py_DECREF(rval); | |
| 896 return NULL; | |
| 897 } | |
| 898 return (PyObject *) rval; | |
| 899 } | |
| 900 | |
| 901 /* | |
| 902 #define decl_k_elemwise_binary_inplace_rowmajor_3(name, F) \ | |
| 903 __global__ void name(const int d0, const int d1, const int d2,\ | |
| 904 float* a, const int sA0, const int sA1, const int sA2,\ | |
| 905 const float* b, const int sB0, const int sB1, const int sB2){\ | |
| 906 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 907 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 908 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 909 F(a[i0*sA0 + i1*sA1 + i2*sA2], b[i0*sB0 + i1*sB1 + i2*sB2]); \ | |
| 910 }\ | |
| 911 }\ | |
| 912 }\ | |
| 913 } | |
| 914 | |
| 915 #define decl_k_elemwise_binary_inplace_rowmajor_4(name, F) \ | |
| 916 __global__ void name(const int d0, const int d1, const int d2, const int d3,\ | |
| 917 float* a, const int sA0, const int sA1,\ | |
| 918 const int sA2, const int sA3,\ | |
| 919 const float* b, const int sB0, const int sB1,\ | |
| 920 const int sB2, const int sB3){\ | |
| 921 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 922 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 923 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 924 for (int i3 = threadIdx.y; i3 < d3; i3 += blockDim.y){\ | |
| 925 F(a[i0*sA0 + i1*sA1 + i2*sA2 + i3*sA3], b[i0*sB0 + i1*sB1 + i2*sB2 + i3*sB3]); \ | |
| 926 }\ | |
| 927 }\ | |
| 928 }\ | |
| 929 }\ | |
| 930 } | |
| 931 | |
| 932 template<typename T> __device__ T binary_iadd(T a, T b) { a = a+b; } | |
| 933 template<typename T> __device__ T binary_idiv(T a, T b) { a = a/b; } | |
| 934 | |
| 935 decl_k_elemwise_binary_inplace_rowmajor_3(k_iAdd_3, binary_iadd<float>) | |
| 936 decl_k_elemwise_binary_inplace_rowmajor_4(k_iAdd_4, binary_iadd<float>) | |
| 937 decl_k_elemwise_binary_inplace_rowmajor_3(k_iDiv_3, binary_idiv<float>) | |
| 938 decl_k_elemwise_binary_inplace_rowmajor_4(k_iDiv_4, binary_idiv<float>) | |
| 939 */ | |
| 940 __global__ void k_iAdd_3(const int d0, const int d1, const int d2,\ | |
| 941 float* a, const int sA0, const int sA1, const int sA2,\ | |
| 942 const float* b, const int sB0, const int sB1, const int sB2){\ | |
| 943 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 944 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 945 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 946 a[i0*sA0 + i1*sA1 + i2*sA2]+= b[i0*sB0 + i1*sB1 + i2*sB2]; \ | |
| 947 }\ | |
| 948 }\ | |
| 949 }\ | |
| 950 } | |
| 951 | |
| 952 __global__ void k_iAdd_4(const int d0, const int d1, const int d2, const int d3,\ | |
| 953 float* a, const int sA0, const int sA1,\ | |
| 954 const int sA2, const int sA3,\ | |
| 955 const float* b, const int sB0, const int sB1,\ | |
| 956 const int sB2, const int sB3){\ | |
| 957 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 958 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 959 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 960 for (int i3 = threadIdx.y; i3 < d3; i3 += blockDim.y){\ | |
| 961 a[i0*sA0 + i1*sA1 + i2*sA2 + i3*sA3] += b[i0*sB0 + i1*sB1 + i2*sB2 + i3*sB3]; \ | |
| 962 }\ | |
| 963 }\ | |
| 964 }\ | |
| 965 }\ | |
| 966 } | |
| 967 | |
| 968 __global__ void k_iDiv_3(const int d0, const int d1, const int d2,\ | |
| 969 float* a, const int sA0, const int sA1, const int sA2,\ | |
| 970 const float* b, const int sB0, const int sB1, const int sB2){\ | |
| 971 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 972 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 973 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 974 a[i0*sA0 + i1*sA1 + i2*sA2]/= b[i0*sB0 + i1*sB1 + i2*sB2]; \ | |
| 975 }\ | |
| 976 }\ | |
| 977 }\ | |
| 978 } | |
| 979 | |
| 980 __global__ void k_iDiv_4(const int d0, const int d1, const int d2, const int d3,\ | |
| 981 float* a, const int sA0, const int sA1,\ | |
| 982 const int sA2, const int sA3,\ | |
| 983 const float* b, const int sB0, const int sB1,\ | |
| 984 const int sB2, const int sB3){\ | |
| 985 for (int i0 = blockIdx.x; i0 < d0; i0 += gridDim.x){\ | |
| 986 for (int i1 = blockIdx.y; i1 < d1; i1 += gridDim.y){\ | |
| 987 for (int i2 = threadIdx.x; i2 < d2; i2 += blockDim.x){\ | |
| 988 for (int i3 = threadIdx.y; i3 < d3; i3 += blockDim.y){\ | |
| 989 a[i0*sA0 + i1*sA1 + i2*sA2 + i3*sA3] /= b[i0*sB0 + i1*sB1 + i2*sB2 + i3*sB3]; \ | |
| 990 }\ | |
| 991 }\ | |
| 992 }\ | |
| 993 }\ | |
| 994 } | |
| 995 | |
| 996 static PyObject * | |
| 997 CudaNdarray_inplace_add_div(PyObject* py_self, PyObject * py_other, int fct_nb) | |
| 998 { | |
| 999 int verbose = 0; | |
| 1000 if (! CudaNdarray_Check(py_self)) { | |
| 1001 PyErr_SetString(PyExc_TypeError, "CudaNdarray_inplace_add_div need a CudaNdarray on left"); | |
| 1002 return NULL; | |
| 1003 } | |
| 1004 if (! CudaNdarray_Check(py_other)) { | |
| 1005 PyErr_SetString(PyExc_TypeError, "CudaNdarray_inplace_add_div need a CudaNdarray on right"); | |
| 1006 return NULL; | |
| 1007 } | |
| 1008 if (fct_nb<0 || fct_nb>1){ | |
| 1009 PyErr_SetString(PyExc_TypeError, "CudaNdarray_inplace_add_div fct_nb param supported are only 0 and 1."); | |
| 1010 return NULL; | |
| 1011 } | |
| 1012 | |
| 1013 CudaNdarray * self = (CudaNdarray *)py_self; | |
| 1014 CudaNdarray * other = (CudaNdarray *)py_other; | |
| 1015 | |
| 1016 if (verbose) fprintf(stderr, "INPLACE ADD/DIV for self->nd=%d other->nd=%d\n", | |
| 1017 self->nd, other->nd); | |
| 1018 | |
| 1019 //standard elemwise size checks | |
| 1020 if (self->nd != other->nd) | |
| 1021 { | |
| 1022 PyErr_Format(PyExc_TypeError, "CudaNdarray_inplace_add_div: need same number of dims. Got %d and %d", self->nd, other->nd); | |
| 1023 return NULL; | |
| 1024 } | |
| 1025 //standard elemwise dim checks | |
| 1026 unsigned int size = 1; | |
| 1027 for (int i = 0; i< self->nd; ++i) | |
| 1028 { | |
| 1029 if ((CudaNdarray_HOST_DIMS(self)[i] != CudaNdarray_HOST_DIMS(other)[i]) | |
| 1030 && (CudaNdarray_HOST_DIMS(other)[i] != 1)) | |
| 1031 { | |
| 1032 PyErr_SetString(PyExc_TypeError, "need same dimensions (or broadcastable dimension)"); | |
| 1033 return NULL; | |
| 1034 } | |
| 1035 size *= (unsigned int) CudaNdarray_HOST_DIMS(self)[i]; | |
| 1036 } | |
| 1037 | |
| 1038 if(CudaNdarray_SIZE((CudaNdarray *)py_self)==0 && CudaNdarray_SIZE((CudaNdarray *)py_other)==0){ | |
| 1039 Py_INCREF(py_self); | |
| 1040 return py_self; | |
| 1041 } | |
| 1042 void (*k_iop_3)(const int, const int, const int, | |
| 1043 float*, const int, const int, const int, | |
| 1044 const float*, const int, const int, const int); | |
| 1045 void (*k_iop_4)(const int, const int, const int, const int, | |
| 1046 float*, const int, const int, | |
| 1047 const int, const int, | |
| 1048 const float*, const int, const int, | |
| 1049 const int, const int); | |
| 1050 if(fct_nb == 0){ | |
| 1051 k_iop_3 = k_iAdd_3; | |
| 1052 k_iop_4 = k_iAdd_4; | |
| 1053 }else if(fct_nb == 1){ | |
| 1054 k_iop_3 = k_iDiv_3; | |
| 1055 k_iop_4 = k_iDiv_4; | |
| 1056 } | |
| 1057 | |
| 1058 switch(self->nd) | |
| 1059 { | |
| 1060 case 0: | |
| 1061 { | |
| 1062 dim3 n_blocks(1, 1, 1); | |
| 1063 dim3 n_threads(1); | |
| 1064 k_iop_3<<<n_blocks, n_threads>>>(1, | |
| 1065 1, //CudaNdarray_HOST_DIMS(self)[0], | |
| 1066 1, //CudaNdarray_HOST_DIMS(self)[0], | |
| 1067 CudaNdarray_DEV_DATA(self), | |
| 1068 1, | |
| 1069 1, //CudaNdarray_HOST_STRIDES(self)[0], | |
| 1070 CudaNdarray_HOST_STRIDES(self)[0], | |
| 1071 CudaNdarray_DEV_DATA(other), | |
| 1072 1, | |
| 1073 1, //CudaNdarray_HOST_STRIDES(other)[0], | |
| 1074 CudaNdarray_HOST_STRIDES(other)[0]); | |
| 1075 CNDA_THREAD_SYNC; | |
| 1076 cudaError_t err = cudaGetLastError(); | |
| 1077 if( cudaSuccess != err) | |
| 1078 { | |
| 1079 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_3", cudaGetErrorString(err)); | |
| 1080 return NULL; | |
| 1081 } | |
| 1082 Py_INCREF(py_self); | |
| 1083 return py_self; | |
| 1084 } | |
| 1085 case 1: | |
| 1086 { | |
| 1087 dim3 n_blocks(1, 1, 1); | |
| 1088 dim3 n_threads( | |
| 1089 std::min(CudaNdarray_HOST_DIMS(self)[0], NUM_VECTOR_OP_THREADS_PER_BLOCK) | |
| 1090 ); | |
| 1091 k_iop_3<<<n_blocks, n_threads>>>(1, | |
| 1092 1, //CudaNdarray_HOST_DIMS(self)[0], | |
| 1093 CudaNdarray_HOST_DIMS(self)[0], | |
| 1094 CudaNdarray_DEV_DATA(self), | |
| 1095 1, | |
| 1096 1, //CudaNdarray_HOST_STRIDES(self)[0], | |
| 1097 CudaNdarray_HOST_STRIDES(self)[0], | |
| 1098 CudaNdarray_DEV_DATA(other), | |
| 1099 1, | |
| 1100 1, //CudaNdarray_HOST_STRIDES(other)[0], | |
| 1101 CudaNdarray_HOST_STRIDES(other)[0]); | |
| 1102 CNDA_THREAD_SYNC; | |
| 1103 cudaError_t err = cudaGetLastError(); | |
| 1104 if( cudaSuccess != err) | |
| 1105 { | |
| 1106 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_3", cudaGetErrorString(err)); | |
| 1107 return NULL; | |
| 1108 } | |
| 1109 Py_INCREF(py_self); | |
| 1110 return py_self; | |
| 1111 } | |
| 1112 case 2: | |
| 1113 { | |
| 1114 dim3 n_blocks(1, | |
| 1115 std::min(CudaNdarray_HOST_DIMS(self)[0], NUM_VECTOR_OP_BLOCKS) | |
| 1116 ); | |
| 1117 dim3 n_threads( | |
| 1118 std::min(CudaNdarray_HOST_DIMS(self)[1], NUM_VECTOR_OP_THREADS_PER_BLOCK) | |
| 1119 ); | |
| 1120 k_iop_3<<<n_blocks, n_threads>>>(1, | |
| 1121 CudaNdarray_HOST_DIMS(self)[0], | |
| 1122 CudaNdarray_HOST_DIMS(self)[1], | |
| 1123 CudaNdarray_DEV_DATA(self), | |
| 1124 1, | |
| 1125 CudaNdarray_HOST_STRIDES(self)[0], | |
| 1126 CudaNdarray_HOST_STRIDES(self)[1], | |
| 1127 CudaNdarray_DEV_DATA(other), | |
| 1128 1, | |
| 1129 CudaNdarray_HOST_STRIDES(other)[0], | |
| 1130 CudaNdarray_HOST_STRIDES(other)[1]); | |
| 1131 CNDA_THREAD_SYNC; | |
| 1132 cudaError_t err = cudaGetLastError(); | |
| 1133 if( cudaSuccess != err) | |
| 1134 { | |
| 1135 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_3", cudaGetErrorString(err)); | |
| 1136 return NULL; | |
| 1137 } | |
| 1138 Py_INCREF(py_self); | |
| 1139 return py_self; | |
| 1140 } | |
| 1141 case 3: | |
| 1142 { | |
| 1143 dim3 n_blocks( | |
| 1144 std::min(CudaNdarray_HOST_DIMS(self)[0], NUM_VECTOR_OP_BLOCKS), | |
| 1145 CudaNdarray_HOST_DIMS(self)[1] | |
| 1146 ); | |
| 1147 while (n_blocks.x * n_blocks.y > NUM_VECTOR_OP_BLOCKS) n_blocks.y /= 2; | |
| 1148 dim3 n_threads( | |
| 1149 std::min(CudaNdarray_HOST_DIMS(self)[2], NUM_VECTOR_OP_THREADS_PER_BLOCK) | |
| 1150 ); | |
| 1151 k_iop_3<<<n_blocks, n_threads>>>( | |
| 1152 CudaNdarray_HOST_DIMS(self)[0], | |
| 1153 CudaNdarray_HOST_DIMS(self)[1], | |
| 1154 CudaNdarray_HOST_DIMS(self)[2], | |
| 1155 CudaNdarray_DEV_DATA(self), | |
| 1156 CudaNdarray_HOST_STRIDES(self)[0], | |
| 1157 CudaNdarray_HOST_STRIDES(self)[1], | |
| 1158 CudaNdarray_HOST_STRIDES(self)[2], | |
| 1159 CudaNdarray_DEV_DATA(other), | |
| 1160 CudaNdarray_HOST_STRIDES(other)[0], | |
| 1161 CudaNdarray_HOST_STRIDES(other)[1], | |
| 1162 CudaNdarray_HOST_STRIDES(other)[2]); | |
| 1163 CNDA_THREAD_SYNC; | |
| 1164 cudaError_t err = cudaGetLastError(); | |
| 1165 if( cudaSuccess != err) | |
| 1166 { | |
| 1167 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_3", cudaGetErrorString(err)); | |
| 1168 return NULL; | |
| 1169 } | |
| 1170 Py_INCREF(py_self); | |
| 1171 return py_self; | |
| 1172 } | |
| 1173 case 4: | |
| 1174 { | |
| 1175 dim3 n_blocks( | |
| 1176 std::min(CudaNdarray_HOST_DIMS(self)[0], NUM_VECTOR_OP_BLOCKS), | |
| 1177 CudaNdarray_HOST_DIMS(self)[1] | |
| 1178 ); | |
| 1179 while (n_blocks.x * n_blocks.y > NUM_VECTOR_OP_BLOCKS) n_blocks.y /= 2; | |
| 1180 dim3 n_threads( | |
| 1181 std::min(CudaNdarray_HOST_DIMS(self)[2], NUM_VECTOR_OP_THREADS_PER_BLOCK) | |
| 1182 ); | |
| 1183 k_iop_4<<<n_blocks, n_threads>>>( | |
| 1184 CudaNdarray_HOST_DIMS(self)[0], | |
| 1185 CudaNdarray_HOST_DIMS(self)[1], | |
| 1186 CudaNdarray_HOST_DIMS(self)[2], | |
| 1187 CudaNdarray_HOST_DIMS(self)[3], | |
| 1188 CudaNdarray_DEV_DATA(self), | |
| 1189 CudaNdarray_HOST_STRIDES(self)[0], | |
| 1190 CudaNdarray_HOST_STRIDES(self)[1], | |
| 1191 CudaNdarray_HOST_STRIDES(self)[2], | |
| 1192 CudaNdarray_HOST_STRIDES(self)[3], | |
| 1193 CudaNdarray_DEV_DATA(other), | |
| 1194 CudaNdarray_HOST_STRIDES(other)[0], | |
| 1195 CudaNdarray_HOST_STRIDES(other)[1], | |
| 1196 CudaNdarray_HOST_STRIDES(other)[2], | |
| 1197 CudaNdarray_HOST_STRIDES(other)[3]); | |
| 1198 CNDA_THREAD_SYNC; | |
| 1199 cudaError_t err = cudaGetLastError(); | |
| 1200 if( cudaSuccess != err) | |
| 1201 { | |
| 1202 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_4", cudaGetErrorString(err)); | |
| 1203 return NULL; | |
| 1204 } | |
| 1205 Py_INCREF(py_self); | |
| 1206 return py_self; | |
| 1207 } | |
| 1208 case 5: | |
| 1209 { | |
| 1210 dim3 n_blocks( | |
| 1211 std::min(CudaNdarray_HOST_DIMS(self)[1], NUM_VECTOR_OP_BLOCKS), | |
| 1212 CudaNdarray_HOST_DIMS(self)[2] | |
| 1213 ); | |
| 1214 while (n_blocks.x * n_blocks.y > NUM_VECTOR_OP_BLOCKS) n_blocks.y /= 2; | |
| 1215 dim3 n_threads( | |
| 1216 std::min(CudaNdarray_HOST_DIMS(self)[3], NUM_VECTOR_OP_THREADS_PER_BLOCK) | |
| 1217 ); | |
| 1218 for (int i = 0; i < CudaNdarray_HOST_DIMS(self)[0]; ++i) | |
| 1219 { | |
| 1220 k_iop_4<<<n_blocks, n_threads>>>( | |
| 1221 CudaNdarray_HOST_DIMS(self)[1], | |
| 1222 CudaNdarray_HOST_DIMS(self)[2], | |
| 1223 CudaNdarray_HOST_DIMS(self)[3], | |
| 1224 CudaNdarray_HOST_DIMS(self)[4], | |
| 1225 CudaNdarray_DEV_DATA(self) + i * CudaNdarray_HOST_STRIDES(self)[0], | |
| 1226 CudaNdarray_HOST_STRIDES(self)[1], | |
| 1227 CudaNdarray_HOST_STRIDES(self)[2], | |
| 1228 CudaNdarray_HOST_STRIDES(self)[3], | |
| 1229 CudaNdarray_HOST_STRIDES(self)[4], | |
| 1230 CudaNdarray_DEV_DATA(other) + i * CudaNdarray_HOST_STRIDES(other)[0], | |
| 1231 CudaNdarray_HOST_STRIDES(other)[1], | |
| 1232 CudaNdarray_HOST_STRIDES(other)[2], | |
| 1233 CudaNdarray_HOST_STRIDES(other)[3], | |
| 1234 CudaNdarray_HOST_STRIDES(other)[4]); | |
| 1235 CNDA_THREAD_SYNC; | |
| 1236 cudaError_t err = cudaGetLastError(); | |
| 1237 if( cudaSuccess != err) | |
| 1238 { | |
| 1239 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "k_iop_4", cudaGetErrorString(err)); | |
| 1240 return NULL; | |
| 1241 } | |
| 1242 } | |
| 1243 Py_INCREF(py_self); | |
| 1244 return py_self; | |
| 1245 } | |
| 1246 } | |
| 1247 | |
| 1248 PyErr_Format(PyExc_NotImplementedError, "inplace_add w nd=%i\n", self->nd); | |
| 1249 return NULL; | |
| 1250 } | |
| 1251 | |
| 1252 /* | |
| 1253 * We need this inplace Add to support IncSubTensor | |
| 1254 */ | |
| 1255 // Will be called by __iadd__ in Python | |
| 1256 static PyObject * | |
| 1257 CudaNdarray_inplace_add(PyObject* py_self, PyObject * py_other){ | |
| 1258 PyObject * rval = CudaNdarray_inplace_add_div(py_self, py_other, 0); | |
| 1259 //We should not increment the refcount as we are doing inplace operation | |
| 1260 //And in this syntax, their is no additional reference created! | |
| 1261 return rval; | |
| 1262 } | |
| 1263 | |
| 1264 /* | |
| 1265 * We need this inplace div for cuda/tests/test_basic_ops.py:test_shared_options | |
| 1266 */ | |
| 1267 // Will be called by __idiv__ in Python | |
| 1268 static PyObject * | |
| 1269 CudaNdarray_inplace_div(PyObject* py_self, PyObject * py_other){ | |
| 1270 PyObject * rval = CudaNdarray_inplace_add_div(py_self, py_other, 1); | |
| 1271 //We should not increment the refcount as we are doing inplace operation | |
| 1272 //And in this syntax, their is no additional reference created! | |
| 1273 return rval; | |
| 1274 } | |
| 1275 | |
| 1276 static PyNumberMethods CudaNdarrayNumberMethods = | |
| 1277 { | |
| 1278 (binaryfunc)CudaNdarray_add, //binaryfunc nb_add; __add__ | |
| 1279 0, //binaryfunc nb_subtract; __sub__ | |
| 1280 0, //binaryfunc nb_multiply; __mul__ | |
| 1281 0, //binaryfunc nb_divide; __div__ | |
| 1282 0, //binaryfunc nb_remainder; __mod__ | |
| 1283 0, //binaryfunc nb_divmod; __divmod__ | |
| 1284 0, //ternaryfunc nb_power; __pow__ | |
| 1285 0, //unaryfunc nb_negative; __neg__ | |
| 1286 0, //unaryfunc nb_positive; __pos__ | |
| 1287 0, //unaryfunc nb_absolute; __abs__ | |
| 1288 0, //inquiry nb_nonzero; __nonzero__ /* Used by PyObject_IsTrue */ | |
| 1289 0, //unaryfunc nb_invert; __invert__ | |
| 1290 0, //binaryfunc nb_lshift; __lshift__ | |
| 1291 0, //binaryfunc nb_rshift; __rshift__ | |
| 1292 0, //binaryfunc nb_and; __and__ | |
| 1293 0, //binaryfunc nb_xor; __xor__ | |
| 1294 0, //binaryfunc nb_or; __or__ | |
| 1295 0, //coercion nb_coerce; __coerce__ /* Used by the coerce() function */ | |
| 1296 0, //unaryfunc nb_int; __int__ | |
| 1297 0, //unaryfunc nb_long; __long__ | |
| 1298 0, //unaryfunc nb_float; __float__ | |
| 1299 0, //unaryfunc nb_oct; __oct__ | |
| 1300 0, //unaryfunc nb_hex; __hex__ | |
| 1301 | |
| 1302 /* Added in release 2.0 */ | |
| 1303 (binaryfunc)CudaNdarray_inplace_add, //binaryfunc nb_inplace_add; __iadd__ | |
| 1304 0, //binaryfunc nb_inplace_subtract; __isub__ | |
| 1305 0, //binaryfunc nb_inplace_multiply; __imul__ | |
| 1306 (binaryfunc)CudaNdarray_inplace_div, //binaryfunc nb_inplace_divide; __idiv__ | |
| 1307 0, //binaryfunc nb_inplace_remainder; __imod__ | |
| 1308 0, //ternaryfunc nb_inplace_power; __ipow__ | |
| 1309 0, //binaryfunc nb_inplace_lshift; __ilshift__ | |
| 1310 0, //binaryfunc nb_inplace_rshift; __irshift__ | |
| 1311 0, //binaryfunc nb_inplace_and; __iand__ | |
| 1312 0, //binaryfunc nb_inplace_xor; __ixor__ | |
| 1313 0, //binaryfunc nb_inplace_or; __ior__ | |
| 1314 | |
| 1315 /* Added in release 2.2 */ | |
| 1316 0, //binaryfunc nb_floor_divide; __floordiv__ | |
| 1317 0, //binaryfunc nb_true_divide; __truediv__ | |
| 1318 0, //binaryfunc nb_inplace_floor_divide; __ifloordiv__ | |
| 1319 0, //binaryfunc nb_inplace_true_divide; __itruediv__ | |
| 1320 | |
| 1321 #if PY_MINOR_VERSION > 4 | |
| 1322 /* Added in release 2.5 */ | |
| 1323 0 //unaryfunc nb_index; __index__ | |
| 1324 #endif | |
| 1325 }; | |
| 1326 | |
| 1327 | |
| 1328 ///////////////////// | |
| 1329 // Mapping protocol | |
| 1330 ///////////////////// | |
| 1331 | |
| 1332 // Will by called by __len__ in Python | |
| 1333 static Py_ssize_t | |
| 1334 CudaNdarray_len(PyObject * py_self) | |
| 1335 { | |
| 1336 CudaNdarray * self = (CudaNdarray*) py_self; | |
| 1337 if (self->nd <= 0) | |
| 1338 { | |
| 1339 return (Py_ssize_t) 0; | |
| 1340 } | |
| 1341 else | |
| 1342 { | |
| 1343 return (Py_ssize_t) CudaNdarray_HOST_DIMS(self)[0]; | |
| 1344 } | |
| 1345 } | |
| 1346 | |
| 1347 // Will by called by __getitem__ in Python | |
| 1348 static PyObject * | |
| 1349 CudaNdarray_Subscript(PyObject * py_self, PyObject * key) | |
| 1350 { | |
| 1351 int verbose = 0; | |
| 1352 if (verbose) fprintf(stderr, "Subscript .... \n"); | |
| 1353 CudaNdarray * self = (CudaNdarray*) py_self; | |
| 1354 PyObject * py_rval = NULL; | |
| 1355 CudaNdarray * rval = NULL; | |
| 1356 PyObject * intobj = NULL; | |
| 1357 | |
| 1358 //PyObject_Print(key, stderr, 0); | |
| 1359 | |
| 1360 if (key == Py_Ellipsis) | |
| 1361 { | |
| 1362 Py_INCREF(py_self); | |
| 1363 return py_self; | |
| 1364 } | |
| 1365 if ((intobj=PyNumber_Int(key))) //INDEXING BY INTEGER | |
| 1366 //else if (PyInt_Check(key)) //INDEXING BY INTEGER | |
| 1367 { | |
| 1368 int d_idx = PyInt_AsLong(intobj); | |
| 1369 Py_DECREF(intobj); intobj=NULL; | |
| 1370 //int d_idx = PyInt_AsLong(key); | |
| 1371 if (self->nd == 0) | |
| 1372 { | |
| 1373 PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed"); | |
| 1374 return NULL; | |
| 1375 } | |
| 1376 int d_dim = CudaNdarray_HOST_DIMS(self)[0]; | |
| 1377 int offset = 0; | |
| 1378 | |
| 1379 if ((d_idx >= 0) && (d_idx < d_dim)) | |
| 1380 { | |
| 1381 //normal indexing | |
| 1382 offset += d_idx * CudaNdarray_HOST_STRIDES(self)[0]; | |
| 1383 } | |
| 1384 else if ((d_idx < 0) && (d_idx >= -d_dim)) | |
| 1385 { | |
| 1386 //end-based indexing | |
| 1387 // d_idx is negative | |
| 1388 offset += (d_dim + d_idx) * CudaNdarray_HOST_STRIDES(self)[0]; | |
| 1389 } | |
| 1390 else | |
| 1391 { | |
| 1392 PyErr_SetString(PyExc_IndexError, "index out of bounds"); | |
| 1393 return NULL; | |
| 1394 } | |
| 1395 | |
| 1396 //allocate our subtensor view | |
| 1397 py_rval = CudaNdarray_new_nd(self->nd - 1); | |
| 1398 rval = (CudaNdarray*) py_rval; | |
| 1399 if (!rval) return NULL; | |
| 1400 assert (0 == rval->data_allocated); | |
| 1401 | |
| 1402 //initialize the view's data pointer to our own. | |
| 1403 if (CudaNdarray_set_device_data(rval, CudaNdarray_DEV_DATA(self) + offset, self)) | |
| 1404 { | |
| 1405 Py_DECREF(rval); | |
| 1406 return NULL; | |
| 1407 } | |
| 1408 for (int d = 1; d < self->nd; ++d) | |
| 1409 { | |
| 1410 CudaNdarray_set_stride(rval, d-1, CudaNdarray_HOST_STRIDES(self)[d]); | |
| 1411 CudaNdarray_set_dim(rval, d-1, CudaNdarray_HOST_DIMS(self)[d]); | |
| 1412 } | |
| 1413 } | |
| 1414 else | |
| 1415 { | |
| 1416 PyErr_Clear(); | |
| 1417 } | |
| 1418 if (PySlice_Check(key)) //INDEXING BY SLICE | |
| 1419 { | |
| 1420 if (self->nd == 0) | |
| 1421 { | |
| 1422 PyErr_SetString(PyExc_ValueError, "cannot slice a 0-d array"); | |
| 1423 return NULL; | |
| 1424 } | |
| 1425 | |
| 1426 int d_dim = CudaNdarray_HOST_DIMS(self)[0]; | |
| 1427 Py_ssize_t start, stop, step, slen; | |
| 1428 if (PySlice_GetIndicesEx((PySliceObject*)key, d_dim, &start, &stop, &step, &slen)) | |
| 1429 { | |
| 1430 return NULL; | |
| 1431 } | |
| 1432 if (verbose) | |
| 1433 { | |
| 1434 std::cerr << "start " << start << "\n"; | |
| 1435 std::cerr << "stop " << stop << "\n"; | |
| 1436 std::cerr << "step " << step << "\n"; | |
| 1437 std::cerr << "slen " << slen << "\n"; | |
| 1438 } | |
| 1439 | |
| 1440 //allocate our subtensor view | |
| 1441 py_rval = CudaNdarray_new_nd(self->nd); | |
| 1442 rval = (CudaNdarray*) py_rval; | |
| 1443 if (!rval) return NULL; | |
| 1444 assert (0 == rval->data_allocated); | |
| 1445 | |
| 1446 | |
| 1447 //initialize the view's data pointer to our own. | |
| 1448 if (CudaNdarray_set_device_data(rval, | |
| 1449 CudaNdarray_DEV_DATA(self) + start * CudaNdarray_HOST_STRIDES(self)[0], | |
| 1450 self)) | |
| 1451 { | |
| 1452 Py_DECREF(rval); | |
| 1453 return NULL; | |
| 1454 } | |
| 1455 //initialize dimension 0 of rval | |
| 1456 CudaNdarray_set_stride(rval, 0, step * CudaNdarray_HOST_STRIDES(self)[0]); | |
| 1457 CudaNdarray_set_dim(rval, 0, slen); | |
| 1458 if (verbose) std::cerr << "rval stride " << CudaNdarray_HOST_STRIDES(rval)[0] << "\n"; | |
| 1459 // initialize dimensions > 0 of rval | |
| 1460 for (int d = 1; d < self->nd; ++d) | |
| 1461 { | |
| 1462 CudaNdarray_set_stride(rval, d, CudaNdarray_HOST_STRIDES(self)[d]); | |
| 1463 CudaNdarray_set_dim(rval, d, CudaNdarray_HOST_DIMS(self)[d]); | |
| 1464 } | |
| 1465 } | |
| 1466 if (PyTuple_Check(key)) //INDEXING BY TUPLE | |
| 1467 { | |
| 1468 //elements of the tuple can be either integers or slices | |
| 1469 //the dimensionality of the view we will return is diminished for each slice in the tuple | |
| 1470 | |
| 1471 if (PyTuple_Size(key) > self->nd) | |
| 1472 { | |
| 1473 PyErr_SetString(PyExc_IndexError, "index error"); | |
| 1474 return NULL; | |
| 1475 } | |
| 1476 | |
| 1477 //calculate the number of dimensions in the return value | |
| 1478 int rval_nd = self->nd; | |
| 1479 for (int d = 0; d < PyTuple_Size(key); ++d) | |
| 1480 { | |
| 1481 //On some paltform PyInt_Check(<type 'numpy.int64'>) return true, other it return false. | |
| 1482 //So we use PyArray_IsAnyScalar that should covert everything. | |
| 1483 rval_nd -= PyArray_IsAnyScalar(PyTuple_GetItem(key, d)); | |
| 1484 } | |
| 1485 | |
| 1486 //allocate our subtensor view | |
| 1487 py_rval = CudaNdarray_new_nd(rval_nd); | |
| 1488 rval = (CudaNdarray*) py_rval; | |
| 1489 if (!rval) return NULL; | |
| 1490 assert (0 == rval->data_allocated); | |
| 1491 | |
| 1492 //initialize the view's data pointer to our own. | |
| 1493 if (CudaNdarray_set_device_data(rval, CudaNdarray_DEV_DATA(self), self)) | |
| 1494 { | |
| 1495 Py_DECREF(rval); | |
| 1496 return NULL; | |
| 1497 } | |
| 1498 | |
| 1499 // rval_d will refer to the current dimension in the rval. | |
| 1500 // It will not be incremented for integer keys, but will be incremented for slice | |
| 1501 // keys | |
| 1502 int rval_d = 0; | |
| 1503 | |
| 1504 for (int d = 0; d < self->nd; ++d) | |
| 1505 { | |
| 1506 // keys can be shorter than self->nd. | |
| 1507 // when that happens, it means that the remaining dimensions are "full slices" | |
| 1508 if (d >=PyTuple_Size(key)) | |
| 1509 { | |
| 1510 CudaNdarray_set_stride(rval, rval_d, CudaNdarray_HOST_STRIDES(self)[d]); | |
| 1511 CudaNdarray_set_dim(rval, rval_d, CudaNdarray_HOST_DIMS(self)[d]); | |
| 1512 ++rval_d; | |
| 1513 } | |
| 1514 else | |
| 1515 { | |
| 1516 PyObject * key_d = PyTuple_GetItem(key, d); | |
| 1517 | |
| 1518 if (PySlice_Check(key_d)) | |
| 1519 { | |
| 1520 Py_ssize_t start, stop, step, slen; | |
| 1521 if (PySlice_GetIndicesEx((PySliceObject*)key_d, CudaNdarray_HOST_DIMS(self)[d], &start, &stop, &step, &slen)) | |
| 1522 { | |
| 1523 Py_DECREF(rval); | |
| 1524 return NULL; | |
| 1525 } | |
| 1526 rval->devdata += start * CudaNdarray_HOST_STRIDES(self)[d]; | |
| 1527 CudaNdarray_set_stride(rval, rval_d, step * CudaNdarray_HOST_STRIDES(self)[d]); | |
| 1528 CudaNdarray_set_dim(rval, rval_d, slen); | |
| 1529 if (0) | |
| 1530 { | |
| 1531 std::cerr << "start " << start << "\n"; | |
| 1532 std::cerr << "stop " << stop << "\n"; | |
| 1533 std::cerr << "step " << step << "\n"; | |
| 1534 std::cerr << "slen " << slen << "\n"; | |
| 1535 } | |
| 1536 ++rval_d; | |
| 1537 } | |
| 1538 else if ((intobj=PyNumber_Int(key_d))) | |
| 1539 { | |
| 1540 assert(PyArray_IsAnyScalar(key_d)); | |
| 1541 int d_idx = PyInt_AsLong(intobj); | |
| 1542 Py_DECREF(intobj); | |
| 1543 intobj = NULL; | |
| 1544 int d_dim = CudaNdarray_HOST_DIMS(self)[d]; | |
| 1545 | |
| 1546 if ((d_idx >= 0) && (d_idx < d_dim)) | |
| 1547 { | |
| 1548 //normal indexing | |
| 1549 rval->devdata += d_idx * CudaNdarray_HOST_STRIDES(self)[d]; | |
| 1550 } | |
| 1551 else if ((d_idx < 0) && (d_idx >= -d_dim)) | |
| 1552 { | |
| 1553 //end-based indexing | |
| 1554 rval->devdata += (d_dim + d_idx) * CudaNdarray_HOST_STRIDES(self)[d]; | |
| 1555 } | |
| 1556 else | |
| 1557 { | |
| 1558 PyErr_SetString(PyExc_IndexError, "index out of bounds"); | |
| 1559 Py_DECREF(rval); | |
| 1560 return NULL; | |
| 1561 } | |
| 1562 } | |
| 1563 else | |
| 1564 { | |
| 1565 PyErr_Clear(); // clear the error set by PyNumber_Int | |
| 1566 PyErr_SetString(PyExc_IndexError, "index must be either int or slice"); | |
| 1567 Py_DECREF(rval); | |
| 1568 return NULL; | |
| 1569 } | |
| 1570 } | |
| 1571 } | |
| 1572 } | |
| 1573 if (py_rval) | |
| 1574 { | |
| 1575 if (verbose) fprint_CudaNdarray(stderr, self); | |
| 1576 if (verbose) fprint_CudaNdarray(stderr, rval); | |
| 1577 } | |
| 1578 else | |
| 1579 { | |
| 1580 PyErr_SetString(PyExc_NotImplementedError, "Unknown key type"); | |
| 1581 return NULL; | |
| 1582 } | |
| 1583 return py_rval; | |
| 1584 } | |
| 1585 | |
| 1586 // Will by called by __setitem__ in Python | |
| 1587 // See http://docs.python.org/dev/py3k/c-api/object.html#PyObject_SetItem | |
| 1588 // Doesn't handle broadcasting, e.g. a[:] = 5 | |
| 1589 // Can only be assigned from a CudaNdarray on the right side | |
| 1590 // Or a ndarray when the left side part is c contiguous. | |
| 1591 static int | |
| 1592 CudaNdarray_setitem(PyObject *o, PyObject *key, PyObject *value) | |
| 1593 { | |
| 1594 if(CudaNdarray_Check(o) && PyArray_Check(value)){ | |
| 1595 // We try to copy directly into this CudaNdarray from the ndarray | |
| 1596 CudaNdarray* rval = (CudaNdarray*)CudaNdarray_Subscript(o, key); | |
| 1597 int typenum = PyArray_TYPE(value); | |
| 1598 | |
| 1599 if(!rval){ | |
| 1600 // CudaNdarray_Subscript failed and set the error msg. | |
| 1601 Py_XDECREF(rval); | |
| 1602 return -1; | |
| 1603 } | |
| 1604 if (typenum != REAL_TYPENUM){ | |
| 1605 PyErr_SetString(PyExc_TypeError, "CudaNdarray.__setitem__: can only copy from float32 arrays"); | |
| 1606 Py_XDECREF(rval); | |
| 1607 return -1; | |
| 1608 } | |
| 1609 if(! CudaNdarray_is_c_contiguous(rval)){ | |
| 1610 PyErr_SetString(PyExc_NotImplementedError, "CudaNdarray.__setitem__: When the new value is an ndarray the part where we copy it to must be c contiguous."); | |
| 1611 Py_XDECREF(rval); | |
| 1612 return -1; | |
| 1613 } | |
| 1614 if(rval->nd != ((PyArrayObject*)value)->nd){ | |
| 1615 PyErr_Format(PyExc_NotImplementedError, "CudaNdarray.__setitem__: need same number of dims. destination nd=%d, source nd=%d. No broadcasting implemented.", | |
| 1616 rval->nd,((PyArrayObject*)value)->nd); | |
| 1617 Py_XDECREF(rval); | |
| 1618 return -1; | |
| 1619 } | |
| 1620 for(int i=0 ; i<rval->nd ; i++){ | |
| 1621 if(CudaNdarray_HOST_DIMS(rval)[i] != ((PyArrayObject*)value)->dimensions[i]){ | |
| 1622 PyErr_Format(PyExc_ValueError, "CudaNdarray.__setitem__: need same dimensions for dim %d, destination=%d, source=%ld", | |
| 1623 i, | |
| 1624 CudaNdarray_HOST_DIMS(rval)[i], | |
| 1625 (long int)(((PyArrayObject*)value)->dimensions[i])); | |
| 1626 Py_XDECREF(rval); | |
| 1627 return -1; | |
| 1628 } | |
| 1629 } | |
| 1630 PyArrayObject * py_v = (PyArrayObject*)PyArray_ContiguousFromAny((PyObject*)value, typenum, | |
| 1631 rval->nd, rval->nd); | |
| 1632 cublasSetVector(PyArray_SIZE(py_v), | |
| 1633 sizeof(real), | |
| 1634 PyArray_DATA(py_v), 1, | |
| 1635 rval->devdata, 1); | |
| 1636 CNDA_THREAD_SYNC; | |
| 1637 Py_XDECREF(py_v); | |
| 1638 Py_XDECREF(rval); | |
| 1639 if (CUBLAS_STATUS_SUCCESS != cublasGetError()){ | |
| 1640 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray.__setitem__: error copying ndarray data to device memory"); | |
| 1641 return -1; | |
| 1642 } | |
| 1643 return 0; | |
| 1644 } | |
| 1645 | |
| 1646 | |
| 1647 if(!CudaNdarray_Check(o) || !CudaNdarray_Check(value)) | |
| 1648 { | |
| 1649 PyErr_SetString(PyExc_TypeError, "CudaNdarray.__setitem__: left must be a CudaNdarrays and right must be a CudaNdarrays or ndarray"); | |
| 1650 return -1; | |
| 1651 } | |
| 1652 | |
| 1653 CudaNdarray* rval = (CudaNdarray*)CudaNdarray_Subscript(o, key); | |
| 1654 | |
| 1655 if(rval == NULL) | |
| 1656 { | |
| 1657 // Actually error string was probably set if we get a NULL, so we leave it as it is | |
| 1658 //PyErr_SetString(PyExc_RuntimeError, "__getitem__ returned an error"); | |
| 1659 return -1; | |
| 1660 } | |
| 1661 else if(rval != (CudaNdarray*)o && | |
| 1662 (rval->data_allocated || | |
| 1663 // The new array should have a base | |
| 1664 !(((CudaNdarray*)rval)->base) || | |
| 1665 // If the original array has no base, the base of the new | |
| 1666 // array should be the original one | |
| 1667 (!((CudaNdarray*)o)->base && ((CudaNdarray*)rval)->base != o) || | |
| 1668 // Else, the two arrays should have the same base | |
| 1669 (((CudaNdarray*)o)->base && ((CudaNdarray*)rval)->base != ((CudaNdarray*)o)->base))) | |
| 1670 { | |
| 1671 // This case shouldn't happen, based on what I see in Subscript | |
| 1672 // but just in case it happens sometime in the future | |
| 1673 | |
| 1674 PyErr_Format(PyExc_RuntimeError, "__getitem__ must return a CudaNdarray that refers to the original CudaNdarray, not a copy. rval.base=%p o.base=%p o=%p", | |
| 1675 (((CudaNdarray*)rval)->base), ((CudaNdarray*)o)->base, o); | |
| 1676 Py_DECREF(rval); | |
| 1677 return -1; | |
| 1678 } | |
| 1679 | |
| 1680 if (cnda_copy_structure_to_device(rval)) | |
| 1681 { | |
| 1682 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray.__setitem__: syncing structure to device failed"); | |
| 1683 Py_DECREF(rval); | |
| 1684 return -1; | |
| 1685 } | |
| 1686 | |
| 1687 PyObject *baseSavedForComparison = rval->base; | |
| 1688 | |
| 1689 if(CudaNdarray_CopyFromCudaNdarray(rval, (CudaNdarray*)value, true)) | |
| 1690 { | |
| 1691 Py_DECREF((PyObject*)rval); | |
| 1692 return -1; | |
| 1693 } | |
| 1694 | |
| 1695 assert (rval->base == baseSavedForComparison); | |
| 1696 assert (rval->dev_structure_fresh); | |
| 1697 | |
| 1698 // Clean up locally-created references | |
| 1699 Py_DECREF(rval); | |
| 1700 | |
| 1701 return 0; | |
| 1702 } | |
| 1703 | |
| 1704 | |
| 1705 PyMappingMethods CudaNdarrayMappingMethods = { | |
| 1706 CudaNdarray_len, //lenfunc mp_length; __len__ | |
| 1707 CudaNdarray_Subscript, //binaryfunc mp_subscript; __getitem__ | |
| 1708 CudaNdarray_setitem //objobjargproc mp_ass_subscript; __setitem__ | |
| 1709 }; | |
| 1710 | |
| 1711 //////////////////// | |
| 1712 // | |
| 1713 //////////////////// | |
| 1714 | |
| 1715 static PyObject * | |
| 1716 CudaNdarray_get_shape(CudaNdarray *self, void *closure) | |
| 1717 { | |
| 1718 if (self->nd < 0) | |
| 1719 { | |
| 1720 PyErr_SetString(PyExc_ValueError, "CudaNdarray not initialized"); | |
| 1721 return NULL; | |
| 1722 } | |
| 1723 PyObject * rval = PyTuple_New(self->nd); | |
| 1724 for (int i = 0; i < self->nd; ++i) | |
| 1725 { | |
| 1726 if (!rval || PyTuple_SetItem(rval, i, PyInt_FromLong(CudaNdarray_HOST_DIMS(self)[i]))) | |
| 1727 { | |
| 1728 Py_XDECREF(rval); | |
| 1729 return NULL; | |
| 1730 } | |
| 1731 | |
| 1732 } | |
| 1733 return rval; | |
| 1734 } | |
| 1735 | |
| 1736 static int | |
| 1737 CudaNdarray_set_shape(CudaNdarray *self, PyObject *value, void *closure) | |
| 1738 { | |
| 1739 PyErr_SetString(PyExc_NotImplementedError, "TODO: call reshape"); | |
| 1740 return -1; | |
| 1741 } | |
| 1742 | |
| 1743 static PyObject * | |
| 1744 CudaNdarray_get_strides(CudaNdarray *self, void *closure) | |
| 1745 { | |
| 1746 if (self->nd < 0) | |
| 1747 { | |
| 1748 PyErr_SetString(PyExc_ValueError, "CudaNdarray not initialized"); | |
| 1749 return NULL; | |
| 1750 } | |
| 1751 PyObject * rval = PyTuple_New(self->nd); | |
| 1752 for (int i = 0; i < self->nd; ++i) | |
| 1753 { | |
| 1754 if (!rval || PyTuple_SetItem(rval, i, PyInt_FromLong(CudaNdarray_HOST_STRIDES(self)[i]))) | |
| 1755 { | |
| 1756 Py_XDECREF(rval); | |
| 1757 return NULL; | |
| 1758 } | |
| 1759 | |
| 1760 } | |
| 1761 return rval; | |
| 1762 } | |
| 1763 | |
| 1764 static int | |
| 1765 CudaNdarray_set_strides(CudaNdarray *self, PyObject *value, void *closure) | |
| 1766 { | |
| 1767 PyErr_SetString(PyExc_NotImplementedError, ""); | |
| 1768 return -1; | |
| 1769 } | |
| 1770 | |
| 1771 static PyObject * | |
| 1772 CudaNdarray_get_dev_data(CudaNdarray *self, void *closure) | |
| 1773 { | |
| 1774 float * p = CudaNdarray_DEV_DATA(self); | |
| 1775 //printf("get_dev_data %p %li \n", p, (long int)p ); | |
| 1776 return PyInt_FromLong((long int) CudaNdarray_DEV_DATA(self)); | |
| 1777 } | |
| 1778 | |
| 1779 static int | |
| 1780 CudaNdarray_set_dev_data(CudaNdarray *self, PyObject *value, void *closure) | |
| 1781 { | |
| 1782 long int newdevdata = PyInt_AsLong(value); | |
| 1783 //printf("set_dev_data %p %li \n",(float*)newdevdata ,newdevdata); | |
| 1784 if (PyErr_Occurred()) | |
| 1785 { | |
| 1786 return -1; | |
| 1787 } | |
| 1788 return CudaNdarray_set_device_data(self, (float*)newdevdata, (CudaNdarray*)self->base); | |
| 1789 } | |
| 1790 | |
| 1791 static PyObject * | |
| 1792 CudaNdarray_get_dtype(CudaNdarray *self, void *closure) | |
| 1793 { | |
| 1794 return PyString_FromString("float32"); | |
| 1795 } | |
| 1796 | |
| 1797 static PyObject * | |
| 1798 CudaNdarray_get_ndim(CudaNdarray *self, void *closure) | |
| 1799 { | |
| 1800 return PyInt_FromLong(self->nd); | |
| 1801 } | |
| 1802 | |
| 1803 static PyObject * | |
| 1804 CudaNdarray_get_base(CudaNdarray *self, void *closure) | |
| 1805 { | |
| 1806 PyObject * base = self->base; | |
| 1807 if (!base) | |
| 1808 { | |
| 1809 // We cannot return a NULL pointer, use None instead | |
| 1810 base = Py_None; | |
| 1811 } | |
| 1812 Py_INCREF(base); | |
| 1813 return base; | |
| 1814 } | |
| 1815 | |
| 1816 static PyGetSetDef CudaNdarray_getset[] = { | |
| 1817 {"shape", | |
| 1818 (getter)CudaNdarray_get_shape, | |
| 1819 (setter)CudaNdarray_set_shape, | |
| 1820 "shape of this ndarray (tuple)", | |
| 1821 NULL}, | |
| 1822 {"_strides", | |
| 1823 (getter)CudaNdarray_get_strides, | |
| 1824 (setter)CudaNdarray_set_strides, | |
| 1825 "data pointer strides (in elements)", | |
| 1826 NULL}, | |
| 1827 //gpudata is needed to allow calling pycuda fct with CudaNdarray input. | |
| 1828 {"gpudata", | |
| 1829 (getter)CudaNdarray_get_dev_data, | |
| 1830 NULL, | |
| 1831 "device data pointer", | |
| 1832 NULL}, | |
| 1833 {"_dev_data", | |
| 1834 (getter)CudaNdarray_get_dev_data, | |
| 1835 (setter)CudaNdarray_set_dev_data, | |
| 1836 "device data pointer", | |
| 1837 NULL}, | |
| 1838 {"dtype", | |
| 1839 (getter)CudaNdarray_get_dtype, | |
| 1840 NULL, | |
| 1841 "The dtype of the element. Now always float32", | |
| 1842 NULL}, | |
| 1843 {"size", | |
| 1844 (getter)CudaNdarray_SIZE_Object, | |
| 1845 NULL, | |
| 1846 "The number of elements in this object.", | |
| 1847 NULL}, | |
| 1848 //mem_size is neede for pycuda.elementwise.ElementwiseKernel Why do they use size and mem_size of the same value? | |
| 1849 {"mem_size", | |
| 1850 (getter)CudaNdarray_SIZE_Object, | |
| 1851 NULL, | |
| 1852 "The number of elements in this object.", | |
| 1853 NULL}, | |
| 1854 {"ndim", | |
| 1855 (getter)CudaNdarray_get_ndim, | |
| 1856 NULL, | |
| 1857 "The number of dimensions in this object.", | |
| 1858 NULL}, | |
| 1859 {"base", | |
| 1860 (getter)CudaNdarray_get_base, | |
| 1861 NULL, | |
| 1862 "If this ndarray is a view, base is the original ndarray.", | |
| 1863 NULL}, | |
| 1864 | |
| 1865 {NULL, NULL, NULL, NULL} /* Sentinel */ | |
| 1866 }; | |
| 1867 | |
| 1868 | |
| 1869 | |
| 1870 static PyTypeObject CudaNdarrayType = | |
| 1871 { | |
| 1872 PyObject_HEAD_INIT(NULL) | |
| 1873 0, /*ob_size*/ | |
| 1874 "CudaNdarray", /*tp_name*/ | |
| 1875 sizeof(CudaNdarray), /*tp_basicsize*/ | |
| 1876 0, /*tp_itemsize*/ | |
| 1877 (destructor)CudaNdarray_dealloc, /*tp_dealloc*/ | |
| 1878 0, /*tp_print*/ | |
| 1879 0, /*tp_getattr*/ | |
| 1880 0, /*tp_setattr*/ | |
| 1881 0, /*tp_compare*/ | |
| 1882 0, /*tp_repr*/ | |
| 1883 &CudaNdarrayNumberMethods, /*tp_as_number*/ | |
| 1884 0, /*tp_as_sequence*/ | |
| 1885 &CudaNdarrayMappingMethods,/*tp_as_mapping*/ | |
| 1886 0, /*tp_hash */ | |
| 1887 0, /*tp_call*/ | |
| 1888 0, /*tp_str*/ | |
| 1889 0, /*tp_getattro*/ | |
| 1890 0, /*tp_setattro*/ | |
| 1891 0, /*tp_as_buffer*/ | |
| 1892 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/ | |
| 1893 "CudaNdarray objects", /* tp_doc */ | |
| 1894 0, /* tp_traverse */ | |
| 1895 0, /* tp_clear */ | |
| 1896 0, /* tp_richcompare */ | |
| 1897 0, /* tp_weaklistoffset */ | |
| 1898 0, /* tp_iter */ | |
| 1899 0, /* tp_iternext */ | |
| 1900 CudaNdarray_methods, /* tp_methods */ | |
| 1901 CudaNdarray_members, /* tp_members */ | |
| 1902 CudaNdarray_getset, /* tp_getset */ | |
| 1903 0, /* tp_base */ | |
| 1904 0, /* tp_dict */ | |
| 1905 0, /* tp_descr_get */ | |
| 1906 0, /* tp_descr_set */ | |
| 1907 0, /* tp_dictoffset */ | |
| 1908 (initproc)CudaNdarray_init,/* tp_init */ | |
| 1909 0, /* tp_alloc */ | |
| 1910 CudaNdarray_new, /* tp_new */ | |
| 1911 }; | |
| 1912 | |
| 1913 static __global__ void get_gpu_ptr_size(int* dst) | |
| 1914 { | |
| 1915 dst[0] = sizeof(float*); | |
| 1916 dst[1] = sizeof(int); | |
| 1917 } | |
| 1918 | |
| 1919 PyObject * | |
| 1920 CudaNdarray_ptr_int_size(PyObject* _unused, PyObject* args) | |
| 1921 { | |
| 1922 int *gpu_data = (int*)device_malloc(sizeof(int)*2); | |
| 1923 if(gpu_data == NULL){ | |
| 1924 return PyErr_Format(PyExc_MemoryError, | |
| 1925 "CudaNdarray_ptr_int_size: Can't allocate memory on the gpu."); | |
| 1926 } | |
| 1927 get_gpu_ptr_size<<<1,1>>>(gpu_data); | |
| 1928 if (cudaSuccess != cublasGetError()){ | |
| 1929 | |
| 1930 device_free(gpu_data); | |
| 1931 return PyErr_Format(PyExc_RuntimeError, | |
| 1932 "CudaNdarray_ptr_int_size: error when calling the gpu code."); | |
| 1933 } | |
| 1934 | |
| 1935 // Transfer the result to cpu | |
| 1936 int gpu_sizes[] = {-1,-1}; | |
| 1937 cublasGetVector(2, sizeof(int), gpu_data, 1, gpu_sizes, 1); | |
| 1938 device_free(gpu_data); | |
| 1939 | |
| 1940 if (CUBLAS_STATUS_SUCCESS != cublasGetError()){ | |
| 1941 PyErr_SetString(PyExc_RuntimeError, "error copying data to from memory"); | |
| 1942 return NULL; | |
| 1943 } | |
| 1944 return Py_BuildValue("iiii", gpu_sizes[0], sizeof(float*), sizeof(int), gpu_sizes[1]); | |
| 1945 } | |
| 1946 | |
| 1947 // Initialize the gpu. | |
| 1948 // Takes one optional parameter, the device number. | |
| 1949 // If provided, it sets that device to be the active device. | |
| 1950 // If not provided (usually just to test whether the gpu is available at all), | |
| 1951 // it does not set an active device. | |
| 1952 // Raises EnvironmentError or ValueError (as appropriate) if the initialization failed. | |
| 1953 PyObject * | |
| 1954 CudaNdarray_gpu_init(PyObject* _unused, PyObject* args) | |
| 1955 { | |
| 1956 int card_nb = 0; | |
| 1957 int card_number_provided = 1; | |
| 1958 | |
| 1959 PyArg_ParseTuple(args, "|i", &card_nb); // if we're given something wildly invalid, this will throw a TypeError | |
| 1960 | |
| 1961 if(PyTuple_Size(args) == 0) { | |
| 1962 card_number_provided = 0; | |
| 1963 card_nb = 0; | |
| 1964 } | |
| 1965 | |
| 1966 int deviceCount; | |
| 1967 cudaError err = cudaGetDeviceCount(&deviceCount); | |
| 1968 if(cudaSuccess != err) { | |
| 1969 return PyErr_Format(PyExc_EnvironmentError, | |
| 1970 "Unable to get the number of gpus available: %s", | |
| 1971 cudaGetErrorString(cudaGetLastError())); | |
| 1972 } | |
| 1973 | |
| 1974 // as soon as the first successful call to a cuda* function is made, a | |
| 1975 // gpu context has been created | |
| 1976 g_gpu_context_active = 1; | |
| 1977 | |
| 1978 if(deviceCount <= 0) { | |
| 1979 return PyErr_Format(PyExc_EnvironmentError, | |
| 1980 "Can't use the GPU, no devices support CUDA"); | |
| 1981 } | |
| 1982 if(card_number_provided && (card_nb < 0 || card_nb > (deviceCount - 1))) { | |
| 1983 return PyErr_Format(PyExc_ValueError, | |
| 1984 "Bad device number %d. Only %d devices available.", | |
| 1985 card_nb, | |
| 1986 deviceCount); | |
| 1987 } | |
| 1988 | |
| 1989 cudaDeviceProp deviceProp; | |
| 1990 err = cudaGetDeviceProperties(&deviceProp, card_nb); | |
| 1991 if(cudaSuccess != err) { | |
| 1992 return PyErr_Format(PyExc_EnvironmentError, | |
| 1993 "Unable to get properties of gpu %i: %s", | |
| 1994 card_nb, | |
| 1995 cudaGetErrorString(cudaGetLastError())); | |
| 1996 } | |
| 1997 | |
| 1998 if(deviceProp.major == 9999 && deviceProp.minor == 9999 ){ | |
| 1999 return PyErr_Format(PyExc_EnvironmentError, | |
| 2000 "There is no device that supports CUDA"); | |
| 2001 } | |
| 2002 | |
| 2003 if(card_number_provided) { | |
| 2004 err = cudaSetDevice(card_nb); | |
| 2005 if(cudaSuccess != err) { | |
| 2006 return PyErr_Format(PyExc_EnvironmentError, | |
| 2007 "Unable to set device %i: %s", | |
| 2008 card_nb, | |
| 2009 cudaGetErrorString(cudaGetLastError())); | |
| 2010 } | |
| 2011 } | |
| 2012 | |
| 2013 Py_INCREF(Py_None); | |
| 2014 return Py_None; | |
| 2015 } | |
| 2016 | |
| 2017 PyObject * | |
| 2018 CudaNdarray_active_device_number(PyObject* _unused, PyObject* _unused_args) { | |
| 2019 // NB: No cuda error checking here; keeps things simple, and it's not | |
| 2020 // really necessary. | |
| 2021 int currentDevice; | |
| 2022 cudaGetDevice(¤tDevice); | |
| 2023 return PyInt_FromLong(currentDevice); | |
| 2024 } | |
| 2025 | |
| 2026 PyObject * | |
| 2027 CudaNdarray_active_device_name(PyObject* _unused, PyObject* _unused_args) { | |
| 2028 // NB: No cuda error checking here; keeps things simple, and it's not | |
| 2029 // really necessary. | |
| 2030 int currentDevice; | |
| 2031 cudaGetDevice(¤tDevice); | |
| 2032 | |
| 2033 cudaDeviceProp deviceProp; | |
| 2034 cudaGetDeviceProperties(&deviceProp, currentDevice); | |
| 2035 return PyString_FromString(deviceProp.name); | |
| 2036 } | |
| 2037 | |
| 2038 PyObject * | |
| 2039 CudaNdarray_gpu_shutdown(PyObject* _unused, PyObject* _unused_args) { | |
| 2040 cudaThreadExit(); | |
| 2041 g_gpu_context_active = 0; // context has now been closed down | |
| 2042 Py_INCREF(Py_None); | |
| 2043 return Py_None; | |
| 2044 } | |
| 2045 | |
| 2046 /* | |
| 2047 * This function is tested in theano/misc/test_pycuda_theano_simple.py | |
| 2048 */ | |
| 2049 PyObject * | |
| 2050 CudaNdarray_from_gpu_pointer(PyObject* _unused, PyObject* args) | |
| 2051 { | |
| 2052 PyObject *gpu_ptr = NULL; | |
| 2053 PyObject *shapes = NULL; | |
| 2054 PyObject *strides = NULL; | |
| 2055 PyObject *base = NULL; | |
| 2056 PyObject *rval = NULL; | |
| 2057 | |
| 2058 //args should consist of 3 python objects | |
| 2059 //The first is the gpu ptr | |
| 2060 //The second if the shape | |
| 2061 //The third if the strides | |
| 2062 if (! PyArg_ParseTuple(args, "OOOO", &gpu_ptr, &shapes, &strides, &base)) | |
| 2063 return NULL; | |
| 2064 | |
| 2065 printf("In CudaNdarray_from_gpu_pointer\n"); | |
| 2066 if (!PyLong_Check(gpu_ptr)) | |
| 2067 { | |
| 2068 PyErr_Format(PyExc_Exception, "CudaNdarray_from_gpu_pointer: The gpu pointor is not an long"); | |
| 2069 return NULL; | |
| 2070 } | |
| 2071 | |
| 2072 Py_ssize_t nd = PyObject_Length(shapes); | |
| 2073 if (nd < 0) | |
| 2074 { | |
| 2075 PyErr_SetString(PyExc_TypeError, "CudaNdarray_from_gpu_pointer: Couldn't get length of second argument"); | |
| 2076 return NULL; | |
| 2077 } | |
| 2078 Py_ssize_t nd_stride = PyObject_Length(strides); | |
| 2079 if (nd_stride < 0) | |
| 2080 { | |
| 2081 PyErr_SetString(PyExc_TypeError, "CudaNdarray_from_gpu_pointer: Couldn't get length of third argument"); | |
| 2082 return NULL; | |
| 2083 } | |
| 2084 | |
| 2085 if (nd != nd_stride) | |
| 2086 { | |
| 2087 PyErr_SetString(PyExc_TypeError, "CudaNdarray_from_gpu_pointer: We need the same number of shapes and strides"); | |
| 2088 return NULL; | |
| 2089 } | |
| 2090 | |
| 2091 rval = CudaNdarray_New(); | |
| 2092 | |
| 2093 if (CudaNdarray_set_nd((CudaNdarray *)rval, nd)) | |
| 2094 { | |
| 2095 //CudaNdarray_set_nd set the error msg | |
| 2096 return NULL; | |
| 2097 } | |
| 2098 // set gpu pointeur | |
| 2099 assert(((CudaNdarray *)rval)->data_allocated == 0); | |
| 2100 if (CudaNdarray_set_device_data((CudaNdarray *)rval, (float *)PyInt_AsLong(gpu_ptr), base)) | |
| 2101 { | |
| 2102 PyErr_SetString(PyExc_TypeError, "CudaNdarray_from_gpu_pointer: Error while setting the gpu pointor"); | |
| 2103 return NULL; | |
| 2104 | |
| 2105 } | |
| 2106 | |
| 2107 // Set dims and strides | |
| 2108 for (int i = nd-1; i >= 0; --i) | |
| 2109 { | |
| 2110 PyObject * idx = PyLong_FromLong(i); | |
| 2111 if (idx == NULL) | |
| 2112 { | |
| 2113 PyErr_SetString(PyExc_Exception, "CudaNdarray_from_gpu_pointer: Couldn't make long object to loop over list/tuple"); | |
| 2114 return NULL; | |
| 2115 } | |
| 2116 PyObject* dim_ = PyObject_GetItem(shapes, idx); | |
| 2117 PyObject* strd_ = PyObject_GetItem(strides, idx); | |
| 2118 if (!PyInt_Check(dim_)) | |
| 2119 { | |
| 2120 PyErr_Format(PyExc_Exception, "CudaNdarray_from_gpu_pointer: shapes[%d] is not an int", i); | |
| 2121 return NULL; | |
| 2122 } | |
| 2123 if (!PyInt_Check(strd_)) | |
| 2124 { | |
| 2125 PyErr_Format(PyExc_Exception, "CudaNdarray_from_gpu_pointer: strides[%d] is not an int", i); | |
| 2126 return NULL; | |
| 2127 } | |
| 2128 int dim = PyInt_AsLong(dim_); | |
| 2129 int strd = PyInt_AsLong(strd_); | |
| 2130 CudaNdarray_set_stride((CudaNdarray *)rval, i, strd); | |
| 2131 CudaNdarray_set_dim((CudaNdarray *)rval, i, dim); | |
| 2132 Py_DECREF(idx); | |
| 2133 Py_DECREF(dim_); | |
| 2134 Py_DECREF(strd_); | |
| 2135 } | |
| 2136 printf("CudaNdarray_from_gpu_pointer normal return\n"); | |
| 2137 return rval; | |
| 2138 } | |
| 2139 | |
| 2140 PyObject * | |
| 2141 CudaNdarray_Dot(PyObject* _unused, PyObject* args) | |
| 2142 { | |
| 2143 PyObject *l=NULL; | |
| 2144 PyObject *r=NULL; | |
| 2145 PyObject * rval = NULL; | |
| 2146 | |
| 2147 //args should consist of two python objects ("OO") | |
| 2148 if (! PyArg_ParseTuple(args, "OO", &l, &r)) | |
| 2149 return NULL; | |
| 2150 | |
| 2151 if (!CudaNdarray_Check(l) || !CudaNdarray_Check(r)) | |
| 2152 { | |
| 2153 PyErr_SetString(PyExc_TypeError, "CudaNdarray arguments required "); | |
| 2154 goto CudaNdarray_dot_fail; | |
| 2155 } | |
| 2156 if (((CudaNdarray*)l)->nd != 2) | |
| 2157 { | |
| 2158 PyErr_SetString(PyExc_TypeError, "need 2d CudaNdarray arg for now"); | |
| 2159 goto CudaNdarray_dot_fail; | |
| 2160 } | |
| 2161 if (((CudaNdarray*)r)->nd != 2) | |
| 2162 { | |
| 2163 PyErr_SetString(PyExc_TypeError, "need 2d CudaNdarray arg for now"); | |
| 2164 goto CudaNdarray_dot_fail; | |
| 2165 } | |
| 2166 rval = CudaNdarray_New(); | |
| 2167 if (!rval) | |
| 2168 { | |
| 2169 goto CudaNdarray_dot_fail; | |
| 2170 } | |
| 2171 int dims[2]; | |
| 2172 dims[0] = CudaNdarray_HOST_DIMS((CudaNdarray*)l)[0]; | |
| 2173 dims[1] = CudaNdarray_HOST_DIMS((CudaNdarray*)r)[1]; | |
| 2174 if (CudaNdarray_alloc_contiguous((CudaNdarray*)rval, 2, dims)) | |
| 2175 { | |
| 2176 goto CudaNdarray_dot_fail; | |
| 2177 } | |
| 2178 if (CudaNdarray_gemm(1.0, (CudaNdarray*)l, (CudaNdarray*)r, 0.0, (CudaNdarray*)rval)) | |
| 2179 { | |
| 2180 goto CudaNdarray_dot_fail; | |
| 2181 } | |
| 2182 | |
| 2183 return rval; | |
| 2184 | |
| 2185 CudaNdarray_dot_fail: | |
| 2186 Py_XDECREF(rval); | |
| 2187 return NULL; | |
| 2188 } | |
| 2189 | |
| 2190 static PyObject * | |
| 2191 filter(PyObject* __unsed_self, PyObject *args) // args = (data, broadcastable, strict, storage) | |
| 2192 { | |
| 2193 /* | |
| 2194 * TODO: DOC what this function should do in the various cases of | |
| 2195 * What is 'strict' supposed to mean in the context of this function? | |
| 2196 * What do we do with input that could be interpreted as matching the broadcastable pattern in strict vs. non-strict cases? | |
| 2197 * | |
| 2198 */ | |
| 2199 PyObject *py_data=NULL; | |
| 2200 PyArrayObject * data = NULL; | |
| 2201 int strict = 0; | |
| 2202 PyObject * broadcastable=NULL; | |
| 2203 PyObject * storage=NULL; | |
| 2204 CudaNdarray * rval=NULL; | |
| 2205 | |
| 2206 //Python object references which are provided to the caller are borrowed references | |
| 2207 if (!PyArg_ParseTuple(args, "OOiO", &py_data, &broadcastable, &strict, &storage)) return NULL; | |
| 2208 | |
| 2209 if (!PyTuple_Check(broadcastable)){ | |
| 2210 PyErr_SetString(PyExc_TypeError, "broadcastable arg should be a tuple of int."); | |
| 2211 return NULL; | |
| 2212 } | |
| 2213 Py_INCREF(py_data); | |
| 2214 Py_INCREF(broadcastable); | |
| 2215 | |
| 2216 CudaNdarray * cnda = (CudaNdarray*)py_data; | |
| 2217 | |
| 2218 if (strict || CudaNdarray_Check(py_data)) | |
| 2219 { | |
| 2220 //TODO: support non-strict "casting" from a vt to the broadcastable/type/size that we need. | |
| 2221 if (!CudaNdarray_Check(py_data)) | |
| 2222 { | |
| 2223 Py_DECREF(py_data); | |
| 2224 Py_DECREF(broadcastable); | |
| 2225 PyErr_SetString(PyExc_TypeError, "strict mode requires CudaNdarray"); | |
| 2226 return NULL; | |
| 2227 } | |
| 2228 if (cnda->nd != PyTuple_Size(broadcastable)) | |
| 2229 { | |
| 2230 Py_DECREF(py_data); | |
| 2231 Py_DECREF(broadcastable); | |
| 2232 PyErr_Format(PyExc_TypeError, "Wrong rank: %i vs %li", cnda->nd, (long)PyTuple_Size(broadcastable)); | |
| 2233 return NULL; | |
| 2234 } | |
| 2235 for (int i = 0; i < cnda->nd; ++i) | |
| 2236 { | |
| 2237 if ((CudaNdarray_HOST_DIMS(cnda)[i] > 1) && PyInt_AsLong(PyTuple_GetItem(broadcastable, Py_ssize_t(i)))) | |
| 2238 { | |
| 2239 PyErr_Format(PyExc_TypeError, "Non-unit size in broadcastable vt dimension %i", i); | |
| 2240 Py_DECREF(py_data); | |
| 2241 Py_DECREF(broadcastable); | |
| 2242 return NULL; | |
| 2243 } | |
| 2244 } | |
| 2245 Py_DECREF(broadcastable); | |
| 2246 return py_data; | |
| 2247 } | |
| 2248 else | |
| 2249 { | |
| 2250 data = (PyArrayObject*)PyArray_FromObject(py_data, REAL_TYPENUM, PyTuple_Size(broadcastable), PyTuple_Size(broadcastable)); | |
| 2251 if (!data) | |
| 2252 { | |
| 2253 //err message already defined | |
| 2254 Py_DECREF(py_data); | |
| 2255 Py_DECREF(broadcastable); | |
| 2256 return NULL; | |
| 2257 } | |
| 2258 for (int i = 0; i < data->nd; ++i) | |
| 2259 { | |
| 2260 if ((data->dimensions[i] > 1) && PyInt_AsLong(PyTuple_GetItem(broadcastable, Py_ssize_t(i)))) | |
| 2261 { | |
| 2262 PyErr_Format(PyExc_TypeError, "Non-unit size in broadcastable dimension %i", i); | |
| 2263 Py_DECREF(data); | |
| 2264 Py_DECREF(py_data); | |
| 2265 Py_DECREF(broadcastable); | |
| 2266 return NULL; | |
| 2267 } | |
| 2268 } | |
| 2269 if (storage && CudaNdarray_Check(storage)) | |
| 2270 { | |
| 2271 rval = (CudaNdarray*) storage; | |
| 2272 Py_INCREF(rval); | |
| 2273 } | |
| 2274 else | |
| 2275 { | |
| 2276 rval = (CudaNdarray*) CudaNdarray_New(); | |
| 2277 } | |
| 2278 if (rval) | |
| 2279 { | |
| 2280 if (CudaNdarray_CopyFromArray(rval, data)) | |
| 2281 { | |
| 2282 Py_DECREF(rval); | |
| 2283 rval = NULL; | |
| 2284 } | |
| 2285 } | |
| 2286 Py_DECREF(data); | |
| 2287 Py_DECREF(py_data); | |
| 2288 Py_DECREF(broadcastable); | |
| 2289 return (PyObject*)rval; | |
| 2290 } | |
| 2291 } | |
| 2292 | |
| 2293 //TODO-- CudaNdarray_Dot and CudaNdarray_active_device_name are following different capitalization conventions. | |
| 2294 // Pick one and standardize it, this file is already annoying enough to grep through | |
| 2295 static PyMethodDef module_methods[] = { | |
| 2296 {"dimshuffle", CudaNdarray_Dimshuffle, METH_VARARGS, "Returns the dimshuffle of a CudaNdarray."}, | |
| 2297 {"dot", CudaNdarray_Dot, METH_VARARGS, "Returns the matrix product of two CudaNdarray arguments."}, | |
| 2298 {"gpu_init", CudaNdarray_gpu_init, METH_VARARGS, "Select the gpu card to use; also usable to test whether CUDA is available."}, | |
| 2299 {"active_device_name", CudaNdarray_active_device_name, METH_VARARGS, "Get the name of the active device."}, | |
| 2300 {"active_device_number", CudaNdarray_active_device_number, METH_VARARGS, "Get the number of the active device."}, | |
| 2301 {"gpu_shutdown", CudaNdarray_gpu_shutdown, METH_VARARGS, "Shut down the gpu."}, | |
| 2302 {"ptr_int_size", CudaNdarray_ptr_int_size, METH_VARARGS, "Return a tuple with the size of gpu pointer, cpu pointer and int in bytes."}, | |
| 2303 {"filter", filter, METH_VARARGS, "filter(obj, broadcastable, strict, storage) returns a CudaNdarray initialized to obj if it matches the constraints of broadcastable. strict=True prevents any numeric casting. If storage is a CudaNdarray it may be overwritten and used as the return value."}, | |
| 2304 {"outstanding_mallocs", outstanding_mallocs, METH_VARARGS, "how many more mallocs have been called than free's"}, | |
| 2305 {"from_gpu_pointer", CudaNdarray_from_gpu_pointer, METH_VARARGS, "Used to create a CudaNdarray from already allocated memory on the gpu.(example by pycuda)"}, | |
| 2306 {NULL, NULL, NULL, NULL} /* Sentinel */ | |
| 2307 }; | |
| 2308 | |
| 2309 #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ | |
| 2310 #define PyMODINIT_FUNC void | |
| 2311 #endif | |
| 2312 PyMODINIT_FUNC | |
| 2313 initcuda_ndarray(void) | |
| 2314 { | |
| 2315 import_array(); | |
| 2316 | |
| 2317 PyObject* m; | |
| 2318 | |
| 2319 if (PyType_Ready(&CudaNdarrayType) < 0) | |
| 2320 return; | |
| 2321 | |
| 2322 m = Py_InitModule3("cuda_ndarray", module_methods, | |
| 2323 "Example module that creates an extension type."); | |
| 2324 | |
| 2325 if (m == NULL) | |
| 2326 return; | |
| 2327 | |
| 2328 Py_INCREF(&CudaNdarrayType); | |
| 2329 PyModule_AddObject(m, "CudaNdarray", (PyObject *)&CudaNdarrayType); | |
| 2330 #if COMPUTE_GPU_MEM_USED | |
| 2331 for(int i=0;i<TABLE_SIZE;i++){ | |
| 2332 _alloc_size_table[i].ptr=NULL; | |
| 2333 _alloc_size_table[i].size=0; | |
| 2334 } | |
| 2335 #endif | |
| 2336 // cublasInit(); | |
| 2337 //if (0&&CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 2338 //{ | |
| 2339 //std::cerr << "WARNING: initcuda_ndarray: error initializing device\n"; | |
| 2340 //} | |
| 2341 if (0) //TODO: is this necessary? | |
| 2342 { | |
| 2343 int deviceId = 0; // TODO: what number goes here? | |
| 2344 cudaSetDevice(deviceId); | |
| 2345 cudaError_t err = cudaGetLastError(); | |
| 2346 if( cudaSuccess != err) | |
| 2347 { | |
| 2348 std::cerr << "Error in SetDevice:" << cudaGetErrorString(err) << "\n"; | |
| 2349 } | |
| 2350 } | |
| 2351 } | |
| 2352 | |
| 2353 | |
| 2354 ////////////////////////////////////// | |
| 2355 // | |
| 2356 // C API FOR CudaNdarray | |
| 2357 // | |
| 2358 ////////////////////////////////////// | |
| 2359 | |
| 2360 int | |
| 2361 CudaNdarray_Check(const PyObject * ob) | |
| 2362 { | |
| 2363 //TODO: doesn't work with inheritance | |
| 2364 return CudaNdarray_CheckExact(ob); | |
| 2365 } | |
| 2366 int | |
| 2367 CudaNdarray_CheckExact(const PyObject * ob) | |
| 2368 { | |
| 2369 return ((ob->ob_type == &CudaNdarrayType) ? 1 : 0); | |
| 2370 } | |
| 2371 | |
| 2372 PyObject * | |
| 2373 CudaNdarray_New(int nd) | |
| 2374 { | |
| 2375 CudaNdarray *self = (CudaNdarray *)CudaNdarrayType.tp_alloc(&CudaNdarrayType, 0); | |
| 2376 if (self == NULL) | |
| 2377 { | |
| 2378 PyErr_SetString(PyExc_RuntimeError, "CudaNdarray_New failed to allocate self"); | |
| 2379 return NULL; | |
| 2380 } | |
| 2381 CudaNdarray_null_init(self); | |
| 2382 | |
| 2383 if (nd == 0) | |
| 2384 { | |
| 2385 self->nd = 0; | |
| 2386 } | |
| 2387 else if (nd > 0) | |
| 2388 { | |
| 2389 if (CudaNdarray_set_nd(self, nd)) | |
| 2390 { | |
| 2391 Py_DECREF(self); | |
| 2392 return NULL; | |
| 2393 } | |
| 2394 } | |
| 2395 ++_outstanding_mallocs[1]; | |
| 2396 return (PyObject *)self; | |
| 2397 } | |
| 2398 | |
| 2399 | |
| 2400 | |
| 2401 ////////////////////////////// | |
| 2402 // | |
| 2403 // Published helper functions | |
| 2404 // | |
| 2405 ////////////////////////////// | |
| 2406 | |
| 2407 int | |
| 2408 cublas_init() | |
| 2409 { | |
| 2410 cublasInit(); | |
| 2411 if (CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 2412 { | |
| 2413 PyErr_SetString(PyExc_RuntimeError, "error initializing device"); | |
| 2414 return -1; | |
| 2415 } | |
| 2416 return 0; | |
| 2417 } | |
| 2418 int | |
| 2419 cublas_shutdown() | |
| 2420 { | |
| 2421 cublasShutdown(); | |
| 2422 if (CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 2423 { | |
| 2424 PyErr_SetString(PyExc_RuntimeError, "error shutting down device"); | |
| 2425 return -1; | |
| 2426 } | |
| 2427 return 0; | |
| 2428 } | |
| 2429 | |
| 2430 int | |
| 2431 CudaNdarray_CopyFromArray(CudaNdarray * self, PyArrayObject*obj) | |
| 2432 { | |
| 2433 int err = CudaNdarray_alloc_contiguous(self, obj->nd, obj->dimensions); | |
| 2434 if (err) { | |
| 2435 return err; | |
| 2436 } | |
| 2437 | |
| 2438 int typenum = PyArray_TYPE(obj); | |
| 2439 if (typenum != REAL_TYPENUM) | |
| 2440 { | |
| 2441 PyErr_SetString(PyExc_TypeError, "can only copy from float arrays"); | |
| 2442 return -1; | |
| 2443 } | |
| 2444 assert( 4 == PyArray_ITEMSIZE(obj)); | |
| 2445 PyObject * py_src = PyArray_ContiguousFromAny((PyObject*)obj, typenum, self->nd, self->nd); | |
| 2446 if (!py_src) { | |
| 2447 return -1; | |
| 2448 } | |
| 2449 cublasSetVector(PyArray_SIZE(py_src), | |
| 2450 sizeof(real), | |
| 2451 PyArray_DATA(py_src), 1, | |
| 2452 self->devdata, 1); | |
| 2453 CNDA_THREAD_SYNC; | |
| 2454 if (CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 2455 { | |
| 2456 PyErr_SetString(PyExc_RuntimeError, "error copying data to device memory"); | |
| 2457 Py_DECREF(py_src); | |
| 2458 return -1; | |
| 2459 } | |
| 2460 Py_DECREF(py_src); | |
| 2461 return 0; | |
| 2462 } | |
| 2463 bool | |
| 2464 CudaNdarray_is_c_contiguous(const CudaNdarray * self) | |
| 2465 { | |
| 2466 bool c_contiguous = true; | |
| 2467 int size = 1; | |
| 2468 for (int i = self->nd-1; (i >= 0) && c_contiguous; --i) | |
| 2469 { | |
| 2470 if (CudaNdarray_HOST_DIMS(self)[i] == 1) | |
| 2471 continue; | |
| 2472 if (CudaNdarray_HOST_STRIDES(self)[i] != size) | |
| 2473 { | |
| 2474 c_contiguous = false; | |
| 2475 } | |
| 2476 size = size * CudaNdarray_HOST_DIMS(self)[i]; | |
| 2477 } | |
| 2478 return c_contiguous; | |
| 2479 } | |
| 2480 | |
| 2481 PyObject * | |
| 2482 CudaNdarray_new_nd(int nd) | |
| 2483 { | |
| 2484 CudaNdarray * rval = (CudaNdarray*) CudaNdarray_New(); | |
| 2485 if (!rval || CudaNdarray_set_nd(rval, nd)) | |
| 2486 { | |
| 2487 Py_XDECREF(rval); | |
| 2488 rval = NULL; | |
| 2489 } | |
| 2490 return (PyObject *) rval; | |
| 2491 } | |
| 2492 | |
| 2493 | |
| 2494 /** | |
| 2495 * Initialize 'self' as a view of 'base', with memory storage 'data' | |
| 2496 */ | |
| 2497 | |
| 2498 int CudaNdarray_set_device_data(CudaNdarray * self, float * data, PyObject * base) | |
| 2499 { | |
| 2500 if (self->data_allocated) | |
| 2501 { | |
| 2502 assert(self->devdata); | |
| 2503 if (device_free(self->devdata)) | |
| 2504 { | |
| 2505 self->devdata = NULL; | |
| 2506 self->data_allocated = 0; | |
| 2507 return -1; | |
| 2508 } | |
| 2509 } | |
| 2510 // Get the original base object (base.base.base...) | |
| 2511 // TODO: check that base is indeed a CudaNdarray? | |
| 2512 PyObject * orig_base = base; | |
| 2513 while (((CudaNdarray*) orig_base)->base) | |
| 2514 { | |
| 2515 // base_base is itself a view | |
| 2516 orig_base = ((CudaNdarray*) orig_base)->base; | |
| 2517 } | |
| 2518 //N.B. XDECREF and XINCREF are no-ops for NULL pointers | |
| 2519 if (self->base != orig_base) | |
| 2520 { | |
| 2521 Py_XDECREF(self->base); | |
| 2522 self->base = orig_base; | |
| 2523 Py_XINCREF(self->base); | |
| 2524 } | |
| 2525 self->data_allocated = 0; | |
| 2526 self->devdata = data; | |
| 2527 return 0; | |
| 2528 } | |
| 2529 | |
| 2530 static __global__ void k_copy_1d(const int N, const float * x, const int sx, float * y, const int sy) | |
| 2531 { | |
| 2532 for (int i = threadIdx.x + blockIdx.x * blockDim.x; i < N; i += gridDim.x*blockDim.x) | |
| 2533 { | |
| 2534 y[i*sy] = x[i*sx]; | |
| 2535 } | |
| 2536 } | |
| 2537 | |
| 2538 //copy from other into self | |
| 2539 int CudaNdarray_CopyFromCudaNdarray(CudaNdarray * self, CudaNdarray * other, bool unbroadcast) | |
| 2540 { | |
| 2541 int verbose = 0; | |
| 2542 if (verbose>1) fprintf(stderr, "CudaNdarray_CopyFromCudaNdarray\n"); | |
| 2543 | |
| 2544 //standard elemwise size checks | |
| 2545 if (self->nd == -1) | |
| 2546 { | |
| 2547 PyErr_SetString(PyExc_TypeError, "can't copy into un-initialized CudaNdarray"); | |
| 2548 return -1; | |
| 2549 } | |
| 2550 if (self->nd != other->nd) | |
| 2551 { | |
| 2552 PyErr_Format(PyExc_NotImplementedError, "CudaNdarray_CopyFromCudaNdarray: need same number of dims. destination nd=%d, source nd=%d. No broadcasting implemented.", self->nd, other->nd); | |
| 2553 return -1; | |
| 2554 } | |
| 2555 //standard elemwise dim checks (also compute total size) | |
| 2556 unsigned int size = 1; | |
| 2557 unsigned int size_source = 1; | |
| 2558 for (int i = 0; i< self->nd; ++i) | |
| 2559 { | |
| 2560 if ((CudaNdarray_HOST_DIMS(self)[i] != CudaNdarray_HOST_DIMS(other)[i]) | |
| 2561 && (1!=CudaNdarray_HOST_DIMS(other)[i] || !unbroadcast) ) | |
| 2562 { | |
| 2563 PyErr_Format(PyExc_ValueError, "need same dimensions for dim %d, destination=%d, source=%d", | |
| 2564 i, CudaNdarray_HOST_DIMS(self)[i], CudaNdarray_HOST_DIMS(other)[i]); | |
| 2565 return -1; | |
| 2566 } | |
| 2567 size *= (unsigned int) CudaNdarray_HOST_DIMS(self)[i]; | |
| 2568 size_source *= (unsigned int) CudaNdarray_HOST_DIMS(other)[i]; | |
| 2569 } | |
| 2570 if (0 == size) | |
| 2571 { | |
| 2572 return 0; //nothing to copy, we're done. | |
| 2573 } | |
| 2574 if (CudaNdarray_is_c_contiguous(self) && CudaNdarray_is_c_contiguous(other) && size == size_source) | |
| 2575 { | |
| 2576 cublasScopy(size, CudaNdarray_DEV_DATA(other), 1, CudaNdarray_DEV_DATA(self), 1); | |
| 2577 CNDA_THREAD_SYNC; | |
| 2578 if (CUBLAS_STATUS_SUCCESS != cublasGetError()) | |
| 2579 { | |
| 2580 PyErr_SetString(PyExc_RuntimeError, "Error copying memory"); | |
| 2581 return -1; | |
| 2582 } | |
| 2583 return 0; | |
| 2584 } | |
| 2585 //TODO: rewrite these copy operations to be more efficient | |
| 2586 // See, for example the transpose example in the cuda_sdk. | |
| 2587 switch (self->nd) | |
| 2588 { | |
| 2589 case 0: // scalar | |
| 2590 { | |
| 2591 // THIS CASE SHOULD NEVER HAPPEN BECAUSE SCALARS ARE ALWAYS C CONTIGUOUS | |
| 2592 assert(0); | |
| 2593 }; break; | |
| 2594 case 1: // vector | |
| 2595 { | |
| 2596 if (verbose) fprintf(stderr, "Copying non-contiguous vector\n"); | |
| 2597 if (verbose) fprint_CudaNdarray(stderr, other); | |
| 2598 unsigned int n_blocks = std::min(size, (unsigned int)NUM_VECTOR_OP_BLOCKS); | |
| 2599 unsigned int n_threads = std::min(ceil_intdiv(size, n_blocks), (unsigned int)NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 2600 k_copy_1d<<<n_blocks, n_threads>>>(size, | |
| 2601 CudaNdarray_DEV_DATA(other), CudaNdarray_HOST_STRIDES(other)[0], | |
| 2602 CudaNdarray_DEV_DATA(self), CudaNdarray_HOST_STRIDES(self)[0]); | |
| 2603 CNDA_THREAD_SYNC; | |
| 2604 cudaError_t err = cudaGetLastError(); | |
| 2605 if( cudaSuccess != err) | |
| 2606 { | |
| 2607 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s. (n_blocks=%i, n_threads_per_block=%i)\n", "k_copy_1d", cudaGetErrorString(err), n_blocks, n_threads); | |
| 2608 return -1; | |
| 2609 } | |
| 2610 }; break; | |
| 2611 default: | |
| 2612 { | |
| 2613 assert (cudaSuccess == cudaGetLastError()); | |
| 2614 if (verbose) fprintf(stderr, "Copying with default version unbroadcast=%d\n", unbroadcast); | |
| 2615 // call worker routine | |
| 2616 unsigned int n_blocks = std::min(size, (unsigned int)NUM_VECTOR_OP_BLOCKS); | |
| 2617 unsigned int threads_per_block = std::min(ceil_intdiv(size, n_blocks), (unsigned int)NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 2618 CudaNdarray * cuda_dims = other; | |
| 2619 if(unbroadcast) | |
| 2620 cuda_dims = self; | |
| 2621 //copy from other into self | |
| 2622 k_elemwise_unary_rowmajor_copy<<<n_blocks, threads_per_block>>>( | |
| 2623 size, | |
| 2624 (unsigned int)other->nd, | |
| 2625 (const int *)CudaNdarray_DEV_DIMS(cuda_dims), | |
| 2626 (const float*)CudaNdarray_DEV_DATA(other), (const int *)CudaNdarray_DEV_STRIDES(other), | |
| 2627 CudaNdarray_DEV_DATA(self), (const int *)CudaNdarray_DEV_STRIDES(self)); | |
| 2628 CNDA_THREAD_SYNC; | |
| 2629 cudaError_t err = cudaGetLastError(); | |
| 2630 if( cudaSuccess != err) | |
| 2631 { | |
| 2632 //fprint_CudaNdarray(stderr, self); | |
| 2633 //fprint_CudaNdarray(stderr, other); | |
| 2634 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s. (n_blocks=%i, n_threads_per_block=%i)\n", "k_elemwise_unary_rowmajor_copy", cudaGetErrorString(err), n_blocks, threads_per_block); | |
| 2635 return -1; | |
| 2636 } | |
| 2637 } | |
| 2638 }; | |
| 2639 return 0; | |
| 2640 } | |
| 2641 | |
| 2642 int CudaNdarray_gemm(float alpha, const CudaNdarray * A, const CudaNdarray * B, float beta, CudaNdarray * C) | |
| 2643 { | |
| 2644 if (A->nd != 2) { PyErr_SetString(PyExc_ValueError, "non-matrix arg to gemm"); return -1; } | |
| 2645 if (B->nd != 2) { PyErr_SetString(PyExc_ValueError, "non-matrix arg to gemm"); return -1; } | |
| 2646 if (C->nd != 2) { PyErr_SetString(PyExc_ValueError, "non-matrix arg to gemm"); return -1; } | |
| 2647 | |
| 2648 if ((CudaNdarray_HOST_DIMS(A)[1] != CudaNdarray_HOST_DIMS(B)[0]) | |
| 2649 || (CudaNdarray_HOST_DIMS(A)[0] != CudaNdarray_HOST_DIMS(C)[0]) | |
| 2650 || (CudaNdarray_HOST_DIMS(B)[1] != CudaNdarray_HOST_DIMS(C)[1])) | |
| 2651 { | |
| 2652 PyErr_Format(PyExc_ValueError, "dimension mismatch in args to gemm (%i,%i)x(%i,%i)->(%i,%i)", | |
| 2653 CudaNdarray_HOST_DIMS(A)[0], | |
| 2654 CudaNdarray_HOST_DIMS(A)[1], | |
| 2655 CudaNdarray_HOST_DIMS(B)[0], | |
| 2656 CudaNdarray_HOST_DIMS(B)[1], | |
| 2657 CudaNdarray_HOST_DIMS(C)[0], | |
| 2658 CudaNdarray_HOST_DIMS(C)[1]); | |
| 2659 return -1; | |
| 2660 } | |
| 2661 | |
| 2662 // a matrix has non-unit size and non-unit stride in both directions, we can't operate in-place | |
| 2663 // TODO: make a copy instead of returning in error | |
| 2664 if (((CudaNdarray_HOST_DIMS(A)[0] > 1) && (CudaNdarray_HOST_STRIDES(A)[0] != 1)) && ((CudaNdarray_HOST_DIMS(A)[1] > 1) && (CudaNdarray_HOST_STRIDES(A)[1] != 1))) | |
| 2665 { PyErr_SetString(PyExc_NotImplementedError, "non-unit stride in gemm arg"); return -1; } | |
| 2666 if (((CudaNdarray_HOST_DIMS(B)[0] > 1) && (CudaNdarray_HOST_STRIDES(B)[0] != 1)) && ((CudaNdarray_HOST_DIMS(B)[1] > 1) && (CudaNdarray_HOST_STRIDES(B)[1] != 1))) | |
| 2667 { PyErr_SetString(PyExc_NotImplementedError, "non-unit stride in gemm arg"); return -1; } | |
| 2668 if (((CudaNdarray_HOST_DIMS(C)[0] > 1) && (CudaNdarray_HOST_STRIDES(C)[0] != 1)) && ((CudaNdarray_HOST_DIMS(C)[1] > 1) && (CudaNdarray_HOST_STRIDES(C)[1] != 1))) | |
| 2669 { PyErr_SetString(PyExc_NotImplementedError, "non-unit stride in gemm arg"); return -1; } | |
| 2670 | |
| 2671 // the unit integer is divided logically into three fields of 4 bits | |
| 2672 // the lowermost 4 bits encode the stride pattern of the output | |
| 2673 // the next higher 4 bits encode the B variable (or y) | |
| 2674 // the next higher 4 bits encode the C variable (or x) | |
| 2675 // | |
| 2676 // the stride pattern for each input is encoded as 0 for unit stride from col to col (Row major) | |
| 2677 // 1 for unit stride from row to row (Col major) | |
| 2678 | |
| 2679 // a stride of 0 implies a dimension of 1 - so we can actually define | |
| 2680 // a stride of 0 as a 'unit' stride because gemm will never use it. | |
| 2681 int unit = 0; | |
| 2682 if (CudaNdarray_HOST_STRIDES(A)[1] == 1 || CudaNdarray_HOST_STRIDES(A)[1] == 0) { | |
| 2683 unit |= (0x0 << 8); | |
| 2684 } else if (CudaNdarray_HOST_STRIDES(A)[0] == 1 || CudaNdarray_HOST_STRIDES(A)[0] == 0) { | |
| 2685 unit |= (0x1 << 8); | |
| 2686 } else { | |
| 2687 unit |= (0x2 << 8); | |
| 2688 } | |
| 2689 if (CudaNdarray_HOST_STRIDES(B)[1] == 1 || CudaNdarray_HOST_STRIDES(B)[1] == 0) { | |
| 2690 unit |= (0x0 << 4); | |
| 2691 } else if (CudaNdarray_HOST_STRIDES(B)[0] == 1 || CudaNdarray_HOST_STRIDES(B)[0] == 0) { | |
| 2692 unit |= (0x1 << 4); | |
| 2693 } else { | |
| 2694 unit |= (0x2 << 4); | |
| 2695 } | |
| 2696 if (CudaNdarray_HOST_STRIDES(C)[1] == 1 || CudaNdarray_HOST_STRIDES(C)[1] == 0) { | |
| 2697 unit |= (0x0 << 0); | |
| 2698 } else if (CudaNdarray_HOST_STRIDES(C)[0] == 1 || CudaNdarray_HOST_STRIDES(C)[0] == 0) { | |
| 2699 unit |= (0x1 << 0); | |
| 2700 } else { | |
| 2701 unit |= (0x2 << 0); | |
| 2702 } | |
| 2703 | |
| 2704 // I don't know if cudablas handles negative strides | |
| 2705 assert (CudaNdarray_HOST_STRIDES(A)[0] >= 0) ; // for now | |
| 2706 assert (CudaNdarray_HOST_STRIDES(A)[1] >= 0) ; // for now | |
| 2707 assert (CudaNdarray_HOST_STRIDES(B)[0] >= 0) ; // for now | |
| 2708 assert (CudaNdarray_HOST_STRIDES(B)[1] >= 0) ; // for now | |
| 2709 assert (CudaNdarray_HOST_STRIDES(C)[0] >= 0) ; // for now | |
| 2710 assert (CudaNdarray_HOST_STRIDES(C)[1] >= 0) ; // for now | |
| 2711 | |
| 2712 /* create appropriate strides for malformed matrices that are row or column | |
| 2713 * vectors | |
| 2714 */ | |
| 2715 int sa_0 = (CudaNdarray_HOST_DIMS(A)[0] > 1) ? CudaNdarray_HOST_STRIDES(A)[0] : CudaNdarray_HOST_DIMS(A)[1]; | |
| 2716 int sa_1 = (CudaNdarray_HOST_DIMS(A)[1] > 1) ? CudaNdarray_HOST_STRIDES(A)[1] : CudaNdarray_HOST_DIMS(A)[0]; | |
| 2717 int sb_0 = (CudaNdarray_HOST_DIMS(B)[0] > 1) ? CudaNdarray_HOST_STRIDES(B)[0] : CudaNdarray_HOST_DIMS(B)[1]; | |
| 2718 int sb_1 = (CudaNdarray_HOST_DIMS(B)[1] > 1) ? CudaNdarray_HOST_STRIDES(B)[1] : CudaNdarray_HOST_DIMS(B)[0]; | |
| 2719 int sc_0 = (CudaNdarray_HOST_DIMS(C)[0] > 1) ? CudaNdarray_HOST_STRIDES(C)[0] : CudaNdarray_HOST_DIMS(C)[1]; | |
| 2720 int sc_1 = (CudaNdarray_HOST_DIMS(C)[1] > 1) ? CudaNdarray_HOST_STRIDES(C)[1] : CudaNdarray_HOST_DIMS(C)[0]; | |
| 2721 | |
| 2722 float* a = CudaNdarray_DEV_DATA(A); | |
| 2723 float* b = CudaNdarray_DEV_DATA(B); | |
| 2724 float* c = CudaNdarray_DEV_DATA(C); | |
| 2725 char N = 'N'; | |
| 2726 char T = 'T'; | |
| 2727 //std::cerr << (unit/256) MOD 16 << (unit / 16) MOD 16 << unit MOD 16<< '\\n'; | |
| 2728 //TODO: recognize the negative stride and make a copy of the offending argument, | |
| 2729 //rather than aborting | |
| 2730 #define CHK_STRIDE_SGEMM(T0, T1, D0, D1, D2, a, x, sx, y, sy, b, z, sz) \ | |
| 2731 if ((sx > 0) && (sy > 0) && (sz > 0)) { \ | |
| 2732 cublasSgemm(T0, T1, D0, D1, D2, a, x, sx, y, sy, b, z, sz); \ | |
| 2733 } else { \ | |
| 2734 PyErr_SetString(PyExc_NotImplementedError, "negative stride to sGemm");\ | |
| 2735 return -1; \ | |
| 2736 } | |
| 2737 | |
| 2738 switch(unit) | |
| 2739 { | |
| 2740 case 0x000: CHK_STRIDE_SGEMM(N, N, CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(A)[1], alpha, b, sb_0, a, sa_0, beta, c, sc_0); break; | |
| 2741 case 0x100: CHK_STRIDE_SGEMM(N, T, CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(A)[1], alpha, b, sb_0, a, sa_1, beta, c, sc_0); break; | |
| 2742 case 0x010: CHK_STRIDE_SGEMM(T, N, CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(A)[1], alpha, b, sb_1, a, sa_0, beta, c, sc_0); break; | |
| 2743 case 0x110: CHK_STRIDE_SGEMM(T, T, CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(A)[1], alpha, b, sb_1, a, sa_1, beta, c, sc_0); break; | |
| 2744 case 0x001: CHK_STRIDE_SGEMM(T, T, CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(A)[1], alpha, a, sa_0, b, sb_0, beta, c, sc_1); break; | |
| 2745 case 0x101: CHK_STRIDE_SGEMM(N, T, CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(A)[1], alpha, a, sa_1, b, sb_0, beta, c, sc_1); break; | |
| 2746 case 0x011: CHK_STRIDE_SGEMM(T, N, CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(A)[1], alpha, a, sa_0, b, sb_1, beta, c, sc_1); break; | |
| 2747 case 0x111: CHK_STRIDE_SGEMM(N, N, CudaNdarray_HOST_DIMS(C)[0], CudaNdarray_HOST_DIMS(C)[1], CudaNdarray_HOST_DIMS(A)[1], alpha, a, sa_1, b, sb_1, beta, c, sc_1); break; | |
| 2748 default: PyErr_Format(PyExc_ValueError, "some matrix has no unit stride (unit=%i)", unit); | |
| 2749 return -1; | |
| 2750 }; | |
| 2751 CNDA_THREAD_SYNC; | |
| 2752 cudaError_t err = cudaGetLastError(); | |
| 2753 if (CUBLAS_STATUS_SUCCESS != err) | |
| 2754 { | |
| 2755 PyErr_Format(PyExc_RuntimeError, "cublassGemm failed (%s)",cudaGetErrorString(err)); | |
| 2756 return -1; | |
| 2757 } | |
| 2758 return 0; | |
| 2759 } | |
| 2760 | |
| 2761 int CudaNdarray_sger(float alpha, CudaNdarray * x, CudaNdarray * y, CudaNdarray * A) { | |
| 2762 if (x->nd != 1) { PyErr_SetString(PyExc_ValueError, "non-vector arg x to sger"); return -1; } | |
| 2763 if (y->nd != 1) { PyErr_SetString(PyExc_ValueError, "non-vector arg y to sger"); return -1; } | |
| 2764 if (A->nd != 2) { PyErr_SetString(PyExc_ValueError, "non-matrix arg A to sger"); return -1; } | |
| 2765 | |
| 2766 if ((CudaNdarray_HOST_DIMS(A)[0] != CudaNdarray_HOST_DIMS(x)[0]) | |
| 2767 || (CudaNdarray_HOST_DIMS(A)[1] != CudaNdarray_HOST_DIMS(y)[0])) { | |
| 2768 PyErr_Format(PyExc_ValueError, | |
| 2769 "dimension mismatch in args to sger (%i)x(%i)->(%i,%i)", | |
| 2770 CudaNdarray_HOST_DIMS(x)[0], | |
| 2771 CudaNdarray_HOST_DIMS(y)[0], | |
| 2772 CudaNdarray_HOST_DIMS(A)[0], | |
| 2773 CudaNdarray_HOST_DIMS(A)[1]); | |
| 2774 return -1; | |
| 2775 } | |
| 2776 | |
| 2777 // Maybe this could work, but be safe for now | |
| 2778 if (!CudaNdarray_is_c_contiguous(A)) { | |
| 2779 PyErr_SetString(PyExc_NotImplementedError, "non-c continugous A in sger"); | |
| 2780 return -1; | |
| 2781 } | |
| 2782 | |
| 2783 // Same for this, be safe | |
| 2784 assert (CudaNdarray_HOST_STRIDES(x)[0] >= 0); | |
| 2785 assert (CudaNdarray_HOST_STRIDES(y)[0] >= 0); | |
| 2786 | |
| 2787 // Since Sger expects A in col-major, we invert x and y to fake this. | |
| 2788 cublasSger(CudaNdarray_HOST_DIMS(y)[0], CudaNdarray_HOST_DIMS(x)[0], alpha, | |
| 2789 CudaNdarray_DEV_DATA(y), CudaNdarray_HOST_STRIDES(y)[0], | |
| 2790 CudaNdarray_DEV_DATA(x), CudaNdarray_HOST_STRIDES(x)[0], | |
| 2791 CudaNdarray_DEV_DATA(A), CudaNdarray_HOST_DIMS(A)[1]); | |
| 2792 CNDA_THREAD_SYNC; | |
| 2793 | |
| 2794 cudaError_t err = cudaGetLastError(); | |
| 2795 if (CUBLAS_STATUS_SUCCESS != err) | |
| 2796 { | |
| 2797 PyErr_Format(PyExc_RuntimeError, "cublasSger failed (%s)",cudaGetErrorString(err)); | |
| 2798 return -1; | |
| 2799 } | |
| 2800 return 0; | |
| 2801 } | |
| 2802 | |
| 2803 /** | |
| 2804 * | |
| 2805 * Precondition: | |
| 2806 * a->dim[d] == (dims_a[d]==0) ? (1 << log2_dims_a[d]) : dims_a[d] | |
| 2807 * z->dim[d] == (z_str[d]==0) ? 1 : dims_a[d]; | |
| 2808 * | |
| 2809 * TODO: templatize this function to support other reductions. | |
| 2810 * All that needs to change is the initial value for sum, and the reduction operator. | |
| 2811 */ | |
| 2812 | |
| 2813 static __global__ void kernel_reduce_sum(const unsigned int size_z, | |
| 2814 const unsigned int nd, | |
| 2815 const int * dims_a, | |
| 2816 const int * log2_dims_a, | |
| 2817 const int * a_str, | |
| 2818 const float * a_data, | |
| 2819 const int * z_str, | |
| 2820 float * z_data) | |
| 2821 { | |
| 2822 const unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x; | |
| 2823 const unsigned int numThreads = blockDim.x * gridDim.x; | |
| 2824 | |
| 2825 //structure data contains the strides and dimensions of both a and z | |
| 2826 // a_dim[0], a_dim[1], ... a_dim[nd-1], | |
| 2827 // a_log2dim[0], a_log2dim[1], ... a_log2dim[nd-1], | |
| 2828 // a_str[0], ... a_str[nd-1], | |
| 2829 // z_str[0], ... z_str[nd-1] | |
| 2830 extern __shared__ int structure_data[]; | |
| 2831 for (unsigned int i = threadIdx.x; i < nd; i += blockDim.x) | |
| 2832 { | |
| 2833 structure_data[i+0*nd] = dims_a[i]; | |
| 2834 structure_data[i+1*nd] = log2_dims_a[i]; | |
| 2835 structure_data[i+2*nd] = a_str[i]; | |
| 2836 structure_data[i+3*nd] = z_str[i]; | |
| 2837 } | |
| 2838 dims_a = structure_data; | |
| 2839 log2_dims_a = structure_data + nd; | |
| 2840 a_str = structure_data + 2*nd; | |
| 2841 z_str = structure_data + 3*nd; | |
| 2842 | |
| 2843 __syncthreads(); //wait for all the shared structure to be loaded | |
| 2844 | |
| 2845 for (unsigned int i = idx; i < size_z; i += numThreads) | |
| 2846 { | |
| 2847 unsigned int ii = i; | |
| 2848 const float * a_data_i = a_data; | |
| 2849 float * z_data_i = z_data; | |
| 2850 unsigned int n_reduce_elements = 1; | |
| 2851 unsigned int n_reduce_dims = 0; | |
| 2852 unsigned int reduce_dim0 = nd-1; | |
| 2853 | |
| 2854 | |
| 2855 //In this loop, we locate the initial element of the slice that we'd like to reduce with this thread | |
| 2856 // At the same time, we [re]calculate the size of that slice (n_reduce_elements) | |
| 2857 for (unsigned int d = 0; d < nd; ++d) | |
| 2858 { | |
| 2859 if (a_str[d] && (!z_str[d])) // this means 'd' is a dimension we are reducing over | |
| 2860 { | |
| 2861 n_reduce_elements *= dims_a[d]; | |
| 2862 n_reduce_dims += 1; | |
| 2863 reduce_dim0 = (d < reduce_dim0) ? d : reduce_dim0; | |
| 2864 } | |
| 2865 else //'d' is not a dimension that we are reducing over | |
| 2866 { | |
| 2867 unsigned int pos_d; | |
| 2868 if (log2_dims_a[d]==-1) //TODO: when things are working, use this switch | |
| 2869 { | |
| 2870 // this branch is not preferred, | |
| 2871 // because the manual said that integer mod and div operations are slow on gpu | |
| 2872 pos_d = (ii % dims_a[d]); | |
| 2873 ii = (ii / dims_a[d]); | |
| 2874 } | |
| 2875 else | |
| 2876 { | |
| 2877 pos_d = (ii & ((1 << log2_dims_a[d])-1)); //take the lower log2_dims bits | |
| 2878 ii = (ii >> log2_dims_a[d]); //shift those lower log2_dims bits off of ii | |
| 2879 } | |
| 2880 a_data_i += pos_d * a_str[d]; | |
| 2881 z_data_i += pos_d * z_str[d]; | |
| 2882 } | |
| 2883 } | |
| 2884 // now we've got pointers a_data_i and z_data_i into element 0 of the slice over which we are reducing | |
| 2885 // do a similar loop | |
| 2886 | |
| 2887 float sum = 0.0f; | |
| 2888 switch(n_reduce_dims) | |
| 2889 { | |
| 2890 case 0: | |
| 2891 { | |
| 2892 sum = a_data_i[0]; | |
| 2893 } | |
| 2894 break; | |
| 2895 case 1: | |
| 2896 { | |
| 2897 const int stride = a_str[reduce_dim0]; | |
| 2898 const float * a_data_i_max = a_data_i + dims_a[reduce_dim0] * stride; | |
| 2899 while (a_data_i != a_data_i_max) | |
| 2900 { | |
| 2901 sum += a_data_i[0]; | |
| 2902 a_data_i += stride; | |
| 2903 } | |
| 2904 } | |
| 2905 break; | |
| 2906 case 2: | |
| 2907 { | |
| 2908 int rd = reduce_dim0+1; | |
| 2909 for (; rd < nd; ++rd) | |
| 2910 { | |
| 2911 if (a_str[rd] && (!z_str[rd])) // this means 'rd' is a dimension we are reducing over | |
| 2912 break; | |
| 2913 } | |
| 2914 const int stride0 = a_str[reduce_dim0]; | |
| 2915 const int stride1 = a_str[rd]; | |
| 2916 for (int ii = 0; ii < dims_a[rd]; ++ii) | |
| 2917 { | |
| 2918 const float * a_data_ri = a_data_i + ii * stride1; | |
| 2919 const float * a_data_ri_max = a_data_ri + dims_a[reduce_dim0] * stride0; | |
| 2920 while (a_data_ri != a_data_ri_max) | |
| 2921 { | |
| 2922 sum += a_data_ri[0]; | |
| 2923 a_data_ri += stride0; | |
| 2924 } | |
| 2925 } | |
| 2926 }; | |
| 2927 break; | |
| 2928 default: | |
| 2929 { | |
| 2930 for (unsigned int reduce_i = 0; reduce_i < n_reduce_elements; ++reduce_i) | |
| 2931 { | |
| 2932 //TODO: optimize this loop to work more like theano's Elemwise. It's serial code. | |
| 2933 unsigned int reduce_ii = reduce_i; | |
| 2934 const float * a_data_ri = a_data_i; | |
| 2935 | |
| 2936 //This loop finds the element in the a slice to add. | |
| 2937 for (unsigned int rd = reduce_dim0; rd < nd; ++rd) | |
| 2938 { | |
| 2939 unsigned int pos_d; | |
| 2940 if (a_str[rd] && (!z_str[rd])) // this means 'd' is a dimension we are reducing over | |
| 2941 { | |
| 2942 if (log2_dims_a[rd]==-1) | |
| 2943 { | |
| 2944 // this branch is not preferred, | |
| 2945 // because the manual said that integer mod and div operations are slow on gpu | |
| 2946 pos_d = (reduce_ii % dims_a[rd]); | |
| 2947 reduce_ii = (reduce_ii / dims_a[rd]); | |
| 2948 } | |
| 2949 else | |
| 2950 { | |
| 2951 pos_d = (reduce_ii & ((1 << log2_dims_a[rd])-1)); //take the lower log2_dims bits | |
| 2952 reduce_ii = (reduce_ii >> log2_dims_a[rd]); //shift those lower log2_dims bits off of ii | |
| 2953 } | |
| 2954 a_data_ri += pos_d * a_str[rd]; | |
| 2955 } | |
| 2956 } | |
| 2957 sum += a_data_ri[0]; | |
| 2958 } | |
| 2959 } | |
| 2960 } | |
| 2961 z_data_i[0] = sum; | |
| 2962 } | |
| 2963 } | |
| 2964 | |
| 2965 static __global__ void kernel_reduce_sum_1011( | |
| 2966 const unsigned int d0, | |
| 2967 const unsigned int d1, | |
| 2968 const unsigned int d2, | |
| 2969 const unsigned int d3, | |
| 2970 const float *A, const int sA0, const int sA1, const int sA2, const int sA3, | |
| 2971 float * Z, const int sZ0) | |
| 2972 { | |
| 2973 const int threadCount = blockDim.x * blockDim.y * blockDim.z; | |
| 2974 const int threadNum = threadIdx.z * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x; | |
| 2975 extern __shared__ float buf[]; | |
| 2976 float mysum = 0.0f; | |
| 2977 | |
| 2978 if (warpSize != 32) | |
| 2979 { | |
| 2980 return; //TODO: set error code | |
| 2981 } | |
| 2982 | |
| 2983 for (int i0 = threadIdx.z; i0 < d0; i0 += blockDim.z) | |
| 2984 { | |
| 2985 float Ai = A[i0 * sA0 + blockIdx.x * sA1 + threadIdx.y * sA2 + threadIdx.x * sA3]; | |
| 2986 mysum += Ai; | |
| 2987 } | |
| 2988 buf[threadNum] = mysum; | |
| 2989 __syncthreads(); | |
| 2990 | |
| 2991 // rest of function is handled by one warp | |
| 2992 if (threadNum < warpSize) | |
| 2993 { | |
| 2994 for (int i = threadNum + warpSize; i < threadCount; i += warpSize) | |
| 2995 { | |
| 2996 mysum += buf[i]; | |
| 2997 } | |
| 2998 buf[threadNum] = mysum; | |
| 2999 if (threadNum < 16) | |
| 3000 { | |
| 3001 //reduce so that threadNum 0 has the sum of everything | |
| 3002 if(threadNum + 16 < threadCount) buf[threadNum] += buf[threadNum+16]; | |
| 3003 if(threadNum + 8 < threadCount) buf[threadNum] += buf[threadNum+8]; | |
| 3004 if(threadNum + 4 < threadCount) buf[threadNum] += buf[threadNum+4]; | |
| 3005 if(threadNum + 2 < threadCount) buf[threadNum] += buf[threadNum+2]; | |
| 3006 if(threadNum + 1 < threadCount) buf[threadNum] += buf[threadNum+1]; | |
| 3007 if (threadNum == 0) | |
| 3008 { | |
| 3009 Z[blockIdx.x*sZ0] = buf[0]; | |
| 3010 } | |
| 3011 } | |
| 3012 } | |
| 3013 } | |
| 3014 /** | |
| 3015 * Dimensions in which the self has size 1 and A has size > 1 are considered summing dimensions | |
| 3016 * Dimensions in which self has size > 1 and A has size > 1 are considered non-summing dimensions, and in this case their sizes must be equal. | |
| 3017 */ | |
| 3018 int | |
| 3019 CudaNdarray_reduce_sum(CudaNdarray * self, CudaNdarray * A) | |
| 3020 { | |
| 3021 int verbose = 0; | |
| 3022 //check input rank | |
| 3023 if (self->nd != A->nd) | |
| 3024 { | |
| 3025 PyErr_Format(PyExc_TypeError, "Rank mismatch in CudaNdarray_sum: %i vs %i", self->nd, A->nd); | |
| 3026 return -1; | |
| 3027 } | |
| 3028 for (int i = 0; i < self->nd; ++i) | |
| 3029 { | |
| 3030 if ((CudaNdarray_HOST_DIMS(self)[i] > 1) && (CudaNdarray_HOST_DIMS(self)[i] != CudaNdarray_HOST_DIMS(A)[i])) | |
| 3031 { | |
| 3032 PyErr_Format(PyExc_TypeError, "Dimension mismatch in CudaNdarray_sum: self->dim[%i] == %i , A->dim[%i] = %i", | |
| 3033 i, CudaNdarray_HOST_DIMS(self)[i], i, CudaNdarray_HOST_DIMS(A)[i]); | |
| 3034 return -1; | |
| 3035 } | |
| 3036 } | |
| 3037 | |
| 3038 int n_summations = (unsigned int)CudaNdarray_SIZE(self); | |
| 3039 if (verbose) | |
| 3040 { | |
| 3041 std::cerr << "reduce_sum n_summations " << n_summations << '\n'; | |
| 3042 std::cerr << "reduce_sum nd " << self->nd << '\n'; | |
| 3043 fprint_CudaNdarray(stderr, A); | |
| 3044 fprint_CudaNdarray(stderr, self); | |
| 3045 } | |
| 3046 if (0 && (A->nd == 4) //check to see if kernel_reduce_sum_1011 applies | |
| 3047 && (CudaNdarray_HOST_DIMS(self)[0] == 1) | |
| 3048 && (CudaNdarray_HOST_DIMS(self)[2] == 1) | |
| 3049 && (CudaNdarray_HOST_DIMS(self)[3] == 1) | |
| 3050 ) | |
| 3051 { | |
| 3052 dim3 n_threads(CudaNdarray_HOST_DIMS(A)[3], CudaNdarray_HOST_DIMS(A)[2]); | |
| 3053 dim3 n_blocks(CudaNdarray_HOST_DIMS(A)[1]); | |
| 3054 while (n_threads.x * n_threads.y * n_threads.z < NUM_VECTOR_OP_THREADS_PER_BLOCK) ++n_threads.z; | |
| 3055 n_threads.z -= 1; | |
| 3056 if (n_threads.z > 64) n_threads.z = 64; | |
| 3057 if (n_threads.z) | |
| 3058 { | |
| 3059 if (verbose) printf("trying kernel_reduce_sum_1011\n"); | |
| 3060 int n_shared = sizeof(float) * n_threads.x * n_threads.y * n_threads.z; | |
| 3061 kernel_reduce_sum_1011<<<n_blocks, n_threads, n_shared>>>( | |
| 3062 CudaNdarray_HOST_DIMS(A)[0], | |
| 3063 CudaNdarray_HOST_DIMS(A)[1], | |
| 3064 CudaNdarray_HOST_DIMS(A)[2], | |
| 3065 CudaNdarray_HOST_DIMS(A)[3], | |
| 3066 CudaNdarray_DEV_DATA(A), | |
| 3067 CudaNdarray_HOST_STRIDES(A)[0], | |
| 3068 CudaNdarray_HOST_STRIDES(A)[1], | |
| 3069 CudaNdarray_HOST_STRIDES(A)[2], | |
| 3070 CudaNdarray_HOST_STRIDES(A)[3], | |
| 3071 CudaNdarray_DEV_DATA(self), | |
| 3072 CudaNdarray_HOST_STRIDES(self)[1]); | |
| 3073 CNDA_THREAD_SYNC; | |
| 3074 if (cudaSuccess == cudaGetLastError()) return 0; | |
| 3075 if (verbose) printf("failed, falling back to kernel_reduce_sum\n"); | |
| 3076 } | |
| 3077 } | |
| 3078 | |
| 3079 int n_threads_per_block = std::min(n_summations, | |
| 3080 NUM_VECTOR_OP_THREADS_PER_BLOCK); | |
| 3081 int n_blocks = std::min(ceil_intdiv(n_summations,n_threads_per_block), | |
| 3082 NUM_VECTOR_OP_BLOCKS); | |
| 3083 int n_structure_cache = self->nd * 4 * sizeof(int); | |
| 3084 | |
| 3085 if (verbose) | |
| 3086 { | |
| 3087 std::cerr << "n_blocks, n_threads_per_block " << n_blocks << ' ' << n_threads_per_block << '\n'; | |
| 3088 } | |
| 3089 assert (self->nd > 0); | |
| 3090 assert (self->nd == A->nd); | |
| 3091 kernel_reduce_sum<<<n_blocks, n_threads_per_block, n_structure_cache>>>( | |
| 3092 n_summations, | |
| 3093 self->nd, | |
| 3094 CudaNdarray_DEV_DIMS(A), | |
| 3095 CudaNdarray_DEV_LOG2DIMS(A), | |
| 3096 CudaNdarray_DEV_STRIDES(A), | |
| 3097 CudaNdarray_DEV_DATA(A), | |
| 3098 CudaNdarray_DEV_STRIDES(self), | |
| 3099 CudaNdarray_DEV_DATA(self)); | |
| 3100 CNDA_THREAD_SYNC; | |
| 3101 cudaError_t err = cudaGetLastError(); | |
| 3102 if (cudaSuccess != err) | |
| 3103 { | |
| 3104 PyErr_Format(PyExc_RuntimeError, "Cuda error: %s: %s.\n", "kernel_reduce_sum", cudaGetErrorString(err)); | |
| 3105 return -1; | |
| 3106 } | |
| 3107 return 0; | |
| 3108 } | |
| 3109 int | |
| 3110 CudaNdarray_reduce_prod(CudaNdarray * self, const CudaNdarray * A) | |
| 3111 { | |
| 3112 PyErr_SetString(PyExc_NotImplementedError, ""); | |
| 3113 return -1; | |
| 3114 } | |
| 3115 int | |
| 3116 CudaNdarray_reduce_min(CudaNdarray * self, const CudaNdarray * A) | |
| 3117 { | |
| 3118 PyErr_SetString(PyExc_NotImplementedError, ""); | |
| 3119 return -1; | |
| 3120 } | |
| 3121 int | |
| 3122 CudaNdarray_reduce_max(CudaNdarray * self, const CudaNdarray * A) | |
| 3123 { | |
| 3124 PyErr_SetString(PyExc_NotImplementedError, ""); | |
| 3125 return -1; | |
| 3126 } | |
| 3127 | |
| 3128 | |
| 3129 /** | |
| 3130 * | |
| 3131 * pattern is a permutation of [0, 1, ... self->nd-1] with the following twists: | |
| 3132 * - an element 'd' of the permutation can be dropped if CudaNdarray_HOST_DIMS(self)[d] == 1 | |
| 3133 * - any number of '-1' elements can be in the pattern, and they will cause new ranks (with dim==1) to be inserted. | |
| 3134 * | |
| 3135 * For example, if CudaNdarray_HOST_DIMS(self) == [4, 5, 1, 6], and pattern = [0,3,-1,-1, 1], then CudaNdarray_HOST_DIMS(self) would be modified to become: | |
| 3136 * [4, 6, 1, 1, 5] (we dropped the original dim[2]==1, and inserted two singleton dimensions with the -1s. | |
| 3137 */ | |
| 3138 int | |
| 3139 CudaNdarray_dimshuffle(CudaNdarray * self, unsigned int len, const int * pattern) | |
| 3140 { | |
| 3141 //TODO: pass a workspace pointer to avoid the internal malloc | |
| 3142 int * newdims = (int *)malloc(sizeof(int) * (len + len + self->nd)); //we tack on the taken buffer here for speed of not having to malloc twice. | |
| 3143 int * newstrides = newdims + len; | |
| 3144 int * dims_taken = newstrides + len; | |
| 3145 if (!newdims) | |
| 3146 { | |
| 3147 PyErr_SetString(PyExc_MemoryError, "CudaNdarray_dimshuffle: Failed to allocate temporary space"); | |
| 3148 return -1; | |
| 3149 } | |
| 3150 for (int i = 0; i < self->nd; ++i) | |
| 3151 { | |
| 3152 dims_taken[i] = 0; | |
| 3153 } | |
| 3154 for (int i = 0; i < len; ++i) | |
| 3155 { | |
| 3156 if (pattern[i] < 0) | |
| 3157 { | |
| 3158 newdims[i] = 1; | |
| 3159 newstrides[i] = 0; | |
| 3160 } | |
| 3161 else if(dims_taken[pattern[i]]) | |
| 3162 { | |
| 3163 PyErr_Format(PyExc_ValueError, "Cudandarray_dimshuffle: invalid pattern for Cudandarray_dimshuffle. You used the dimensions %d multiple time", | |
| 3164 pattern[i]); | |
| 3165 free(newdims); | |
| 3166 return -1; | |
| 3167 } | |
| 3168 else if (pattern[i]>= self->nd) | |
| 3169 { | |
| 3170 PyErr_Format(PyExc_ValueError, "Cudandarray_dimshuffle: invalid pattern for Cudandarray_dimshuffle. You asked for a dimensions that don't exist %d for a %d dims CudaNdarray", | |
| 3171 pattern[i], self->nd); | |
| 3172 free(newdims); | |
| 3173 return -1; | |
| 3174 } | |
| 3175 else | |
| 3176 { | |
| 3177 newdims[i] = CudaNdarray_HOST_DIMS(self)[pattern[i]]; | |
| 3178 newstrides[i] = CudaNdarray_HOST_STRIDES(self)[pattern[i]]; | |
| 3179 dims_taken[pattern[i]] = 1; | |
| 3180 } | |
| 3181 } | |
| 3182 //Check if we dropped not broadcastable dims | |
| 3183 for (int i = 0; i < self->nd; ++i) | |
| 3184 { | |
| 3185 if (dims_taken[i]==0 && CudaNdarray_HOST_DIMS(self)[i]!=1) | |
| 3186 { | |
| 3187 PyErr_SetString(PyExc_ValueError, "Cudandarray_dimshuffle: You cannot drop a non-broadcastable dimension."); | |
| 3188 free(newdims); | |
| 3189 return -1; | |
| 3190 } | |
| 3191 } | |
| 3192 //swap this structure in for the one in self, and sync to the card | |
| 3193 if (CudaNdarray_set_nd(self, len)) | |
| 3194 { | |
| 3195 free(newdims); | |
| 3196 return -1; | |
| 3197 } | |
| 3198 for (int i = 0; i < len; ++i) | |
| 3199 { | |
| 3200 CudaNdarray_set_dim(self, i, newdims[i]); | |
| 3201 CudaNdarray_set_stride(self, i, newstrides[i]); | |
| 3202 } | |
| 3203 if (cnda_copy_structure_to_device(self)) | |
| 3204 { | |
| 3205 free(newdims); | |
| 3206 return -1; | |
| 3207 } | |
| 3208 free(newdims); | |
| 3209 return 0; | |
| 3210 } | |
| 3211 | |
| 3212 | |
| 3213 | |
| 3214 /** | |
| 3215 * | |
| 3216 * This is the function that bind to python. | |
| 3217 * See CudaNdarray_dimshuffle to call from C. | |
| 3218 * We use -1 to mean 'x' as in Tensor Dimshuffle. | |
| 3219 */ | |
| 3220 PyObject * | |
| 3221 CudaNdarray_Dimshuffle(PyObject* _unused, PyObject* args) | |
| 3222 { | |
| 3223 PyObject * self = NULL; | |
| 3224 PyObject * pattern_object = NULL; | |
| 3225 int * pattern = NULL; | |
| 3226 PyObject * rval = NULL; | |
| 3227 int success = -1; | |
| 3228 //const int * dims = NULL; | |
| 3229 | |
| 3230 //args should consist of two python objects ("OO") | |
| 3231 if (! PyArg_ParseTuple(args, "OO", &self, &pattern_object)) | |
| 3232 return NULL; | |
| 3233 | |
| 3234 if (!CudaNdarray_Check(self) ) | |
| 3235 { | |
| 3236 PyErr_SetString(PyExc_TypeError, "First argument to cuda_ndarray.dimshuffle must be a CudaNdarray"); | |
| 3237 return NULL; | |
| 3238 } | |
| 3239 | |
| 3240 //parse pattern_object into int * pattern | |
| 3241 | |
| 3242 Py_ssize_t pattern_dim = PyObject_Length(pattern_object); | |
| 3243 | |
| 3244 if (pattern_dim < 0) | |
| 3245 { | |
| 3246 PyErr_SetString(PyExc_TypeError, "Couldn't get length of third argument to cuda_ndarray.dimshuffle"); | |
| 3247 return NULL; | |
| 3248 } | |
| 3249 | |
| 3250 pattern = (int *) malloc( pattern_dim * sizeof(int)); | |
| 3251 | |
| 3252 for (Py_ssize_t i = 0; i < pattern_dim; i++) | |
| 3253 { | |
| 3254 PyObject * idx = PyLong_FromLong(i); | |
| 3255 | |
| 3256 if (idx == NULL) | |
| 3257 { | |
| 3258 PyErr_SetString(PyExc_Exception, "Couldn't make long object to loop over list/tuple"); | |
| 3259 goto CudaNdarray_dimshuffle_fail; | |
| 3260 } | |
| 3261 | |
| 3262 long elem_value = 0; | |
| 3263 | |
| 3264 PyObject * elem = PyObject_GetItem(pattern_object, idx); | |
| 3265 | |
| 3266 if (elem == NULL) | |
| 3267 { | |
| 3268 Py_XDECREF( elem); | |
| 3269 PyErr_SetString(PyExc_ValueError, "Third argument to dimshuffle must be list or tuple of integers"); | |
| 3270 goto CudaNdarray_dimshuffle_fail; | |
| 3271 } | |
| 3272 | |
| 3273 elem_value = PyInt_AsLong(elem); | |
| 3274 | |
| 3275 if (elem_value == -1 && PyErr_Occurred() ) | |
| 3276 { | |
| 3277 Py_XDECREF(elem); | |
| 3278 PyErr_SetString(PyExc_ValueError, "Third argument to dimshuffle must be list or tuple of integers"); | |
| 3279 goto CudaNdarray_dimshuffle_fail; | |
| 3280 } | |
| 3281 | |
| 3282 pattern[i] = elem_value; | |
| 3283 | |
| 3284 Py_XDECREF( elem ); | |
| 3285 Py_XDECREF( idx ); | |
| 3286 } | |
| 3287 | |
| 3288 //allocate rval | |
| 3289 rval = (PyObject *) CudaNdarray_View((CudaNdarray *) self); | |
| 3290 | |
| 3291 if (rval == NULL) | |
| 3292 { | |
| 3293 //CudaNdarray_New should have set the exception string | |
| 3294 goto CudaNdarray_dimshuffle_fail; | |
| 3295 } | |
| 3296 | |
| 3297 | |
| 3298 //printf("pattern_dim: %d\n",pattern_dim); | |
| 3299 //printf("pattern: %d %d\n",pattern[0],pattern[1]); | |
| 3300 //dims = CudaNdarray_HOST_DIMS( (CudaNdarray *) self); | |
| 3301 //printf("dims before: %d %d\n",dims[0],dims[1]); | |
| 3302 | |
| 3303 success = CudaNdarray_dimshuffle((CudaNdarray *) rval, pattern_dim, pattern); | |
| 3304 | |
| 3305 if (success != 0) | |
| 3306 { | |
| 3307 //Exception string should already be set by CudaNdarray_dimshuffle | |
| 3308 goto CudaNdarray_dimshuffle_fail; | |
| 3309 } | |
| 3310 | |
| 3311 free(pattern); | |
| 3312 | |
| 3313 return rval; | |
| 3314 | |
| 3315 CudaNdarray_dimshuffle_fail: | |
| 3316 | |
| 3317 if (pattern != NULL) | |
| 3318 free(pattern); | |
| 3319 | |
| 3320 Py_XDECREF(rval); | |
| 3321 return NULL; | |
| 3322 } | |
| 3323 | |
| 3324 /* | |
| 3325 Local Variables: | |
| 3326 mode:c++ | |
| 3327 c-basic-offset:4 | |
| 3328 c-file-style:"stroustrup" | |
| 3329 c-file-offsets:((innamespace . 0)(inline-open . 0)) | |
| 3330 indent-tabs-mode:nil | |
| 3331 fill-column:79 | |
| 3332 End: | |
| 3333 */ | |
| 3334 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 : | |
| 3335 | |
| ERROR (theano.sandbox.cuda): Failed to compile cuda_ndarray.cu: ('nvcc return status', 1, 'for cmd', 'nvcc -shared -g -m64 -Xcompiler -fPIC,-m64 -Xlinker -rpath,/usr/local/cuda/lib -I/Users/bayerj/devel/third-party/Theano/theano/sandbox/cuda -I/Library/Python/2.6/site-packages/numpy-2.0.0.dev_ac2c160_20110325-py2.6-macosx-10.6-universal.egg/numpy/core/include -I/System/Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 -o /Users/bayerj/.theano/compiledir_Darwin-10.6.0-i386-64bit-i386-2.6.1/cuda_ndarray/cuda_ndarray.so mod.cu -L/usr/local/cuda/lib -lcublas -lcudart python version 2.6.1 can\'t run /usr/bin/python-config. Try the alternative(s): /usr/bin/python2.5-config (uses python 2.5) /usr/bin/python2.6-config (uses python 2.6) Run "man python" for more information about multiple version support in Mac OS X.') | |
| WARNING (theano.sandbox.cuda): Cuda is disabled, cuda-based code will thus not be working properly |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment