util_opencl.h (16563B)
1 /* 2 Copyright 2010-2011, D. E. Shaw Research. 3 All rights reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are 7 met: 8 9 * Redistributions of source code must retain the above copyright 10 notice, this list of conditions, and the following disclaimer. 11 12 * Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions, and the following disclaimer in the 14 documentation and/or other materials provided with the distribution. 15 16 * Neither the name of D. E. Shaw Research nor the names of its 17 contributors may be used to endorse or promote products derived from 18 this software without specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 #ifndef UTIL_OPENCL_H__ 33 #define UTIL_OPENCL_H__ 34 /* 35 * has a couple of utility functions to setup and teardown OpenCL. 36 * Avoid much boilerplate in every OpenCL program 37 */ 38 39 #include "util.h" 40 41 /* We use the old clCreateCommandQueue for max portability since we do not 42 * use any cmdq properties anyway, avoids a warning. 43 */ 44 #define CL_USE_DEPRECATED_OPENCL_1_2_APIS 45 46 #if defined(__APPLE__) || defined(__MACOSX) 47 #include <OpenCL/cl.h> 48 #include <OpenCL/cl_ext.h> 49 #else 50 #include <CL/cl.h> 51 #include <CL/cl_ext.h> 52 #endif 53 54 #define UCL_STRSIZE 128 55 56 typedef struct ucl_info { 57 cl_context ctx; 58 cl_program prog; 59 cl_device_id devid; 60 cl_command_queue cmdq; 61 cl_uint clkfreq, compunits; 62 size_t wgsize; 63 int cores; 64 double cycles; 65 char vendor[UCL_STRSIZE], devname[UCL_STRSIZE], 66 version[UCL_STRSIZE], driver[UCL_STRSIZE]; 67 int computeflags; 68 cl_device_fp_config fpdbl; 69 cl_device_type devtype; 70 } UCLInfo; 71 72 /* Miscellaneous checking macros for convenience */ 73 static char *print_cl_errstring(cl_int err) { 74 switch (err) { 75 case CL_SUCCESS: return strdup("Success!"); 76 case CL_DEVICE_NOT_FOUND: return strdup("Device not found."); 77 case CL_DEVICE_NOT_AVAILABLE: return strdup("Device not available"); 78 case CL_COMPILER_NOT_AVAILABLE: return strdup("Compiler not available"); 79 case CL_MEM_OBJECT_ALLOCATION_FAILURE: return strdup("Memory object allocation failure"); 80 case CL_OUT_OF_RESOURCES: return strdup("Out of resources"); 81 case CL_OUT_OF_HOST_MEMORY: return strdup("Out of host memory"); 82 case CL_PROFILING_INFO_NOT_AVAILABLE: return strdup("Profiling information not available"); 83 case CL_MEM_COPY_OVERLAP: return strdup("Memory copy overlap"); 84 case CL_IMAGE_FORMAT_MISMATCH: return strdup("Image format mismatch"); 85 case CL_IMAGE_FORMAT_NOT_SUPPORTED: return strdup("Image format not supported"); 86 case CL_BUILD_PROGRAM_FAILURE: return strdup("Program build failure"); 87 case CL_MAP_FAILURE: return strdup("Map failure"); 88 case CL_INVALID_VALUE: return strdup("Invalid value"); 89 case CL_INVALID_DEVICE_TYPE: return strdup("Invalid device type"); 90 case CL_INVALID_PLATFORM: return strdup("Invalid platform"); 91 case CL_INVALID_DEVICE: return strdup("Invalid device"); 92 case CL_INVALID_CONTEXT: return strdup("Invalid context"); 93 case CL_INVALID_QUEUE_PROPERTIES: return strdup("Invalid queue properties"); 94 case CL_INVALID_COMMAND_QUEUE: return strdup("Invalid command queue"); 95 case CL_INVALID_HOST_PTR: return strdup("Invalid host pointer"); 96 case CL_INVALID_MEM_OBJECT: return strdup("Invalid memory object"); 97 case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return strdup("Invalid image format descriptor"); 98 case CL_INVALID_IMAGE_SIZE: return strdup("Invalid image size"); 99 case CL_INVALID_SAMPLER: return strdup("Invalid sampler"); 100 case CL_INVALID_BINARY: return strdup("Invalid binary"); 101 case CL_INVALID_BUILD_OPTIONS: return strdup("Invalid build options"); 102 case CL_INVALID_PROGRAM: return strdup("Invalid program"); 103 case CL_INVALID_PROGRAM_EXECUTABLE: return strdup("Invalid program executable"); 104 case CL_INVALID_KERNEL_NAME: return strdup("Invalid kernel name"); 105 case CL_INVALID_KERNEL_DEFINITION: return strdup("Invalid kernel definition"); 106 case CL_INVALID_KERNEL: return strdup("Invalid kernel"); 107 case CL_INVALID_ARG_INDEX: return strdup("Invalid argument index"); 108 case CL_INVALID_ARG_VALUE: return strdup("Invalid argument value"); 109 case CL_INVALID_ARG_SIZE: return strdup("Invalid argument size"); 110 case CL_INVALID_KERNEL_ARGS: return strdup("Invalid kernel arguments"); 111 case CL_INVALID_WORK_DIMENSION: return strdup("Invalid work dimension"); 112 case CL_INVALID_WORK_GROUP_SIZE: return strdup("Invalid work group size"); 113 case CL_INVALID_WORK_ITEM_SIZE: return strdup("Invalid work item size"); 114 case CL_INVALID_GLOBAL_OFFSET: return strdup("Invalid global offset"); 115 case CL_INVALID_EVENT_WAIT_LIST: return strdup("Invalid event wait list"); 116 case CL_INVALID_EVENT: return strdup("Invalid event"); 117 case CL_INVALID_OPERATION: return strdup("Invalid operation"); 118 case CL_INVALID_GL_OBJECT: return strdup("Invalid OpenGL object"); 119 case CL_INVALID_BUFFER_SIZE: return strdup("Invalid buffer size"); 120 case CL_INVALID_MIP_LEVEL: return strdup("Invalid mip-map level"); 121 default: return strdup("Unknown"); 122 } 123 } 124 125 static const char *cldevtypestr(cl_device_type c) { 126 switch (c) { 127 case CL_DEVICE_TYPE_CPU: return "CPU"; 128 case CL_DEVICE_TYPE_GPU: return "GPU"; 129 case CL_DEVICE_TYPE_ACCELERATOR: return "ACCELERATOR"; 130 case CL_DEVICE_TYPE_DEFAULT: return "DEFAULT"; 131 default: return "UNKNOWN"; 132 } 133 } 134 135 #define CHECKERR(x) do { \ 136 (x); \ 137 if (err != CL_SUCCESS) { \ 138 fprintf(stderr, "%s: error %d: %s from %s\n", progname, err, print_cl_errstring(err), #x); \ 139 exit(1); \ 140 } \ 141 } while(0) 142 143 #define CHECK(x) CHECKERR(err = (x)) 144 145 static UCLInfo *opencl_init(const char *devstr, const char *src, 146 const char *options) 147 { 148 #define UCL_MAX_PROPERTIES 32 149 #define UCL_MAX_PLATFORMS 8 150 #define UCL_MAX_DEVICES 16 151 UCLInfo *tp; 152 cl_context_properties ctxprop[UCL_MAX_PROPERTIES]; 153 cl_int err; 154 cl_platform_id platforms[UCL_MAX_PLATFORMS]; 155 cl_uint nplatforms, ndevices; 156 cl_device_id devices[UCL_MAX_DEVICES]; 157 const char *srcstr[2], *clbinfile, *coremultstr; 158 unsigned i, j; 159 int cores, devcores, coremultguess = 0; 160 161 if (devstr == NULL) 162 devstr = getenv("R123EXAMPLE_ENVCONF_OPENCL_DEVICE"); 163 164 coremultstr = getenv("R123EXAMPLE_ENVCONF_OPENCL_CORES_PER_UNIT"); 165 if (coremultstr) { 166 coremultguess = atoi(coremultstr); 167 dprintf(("setting coremultguess to %d\n", coremultguess)); 168 } 169 170 /* get list of platforms */ 171 CHECK(clGetPlatformIDs(0, NULL, &nplatforms)); 172 dprintf(("nplatforms = %d\n", nplatforms)); 173 CHECK(clGetPlatformIDs(UCL_MAX_PLATFORMS, platforms, &nplatforms)); 174 if (nplatforms == 0) { 175 fprintf(stderr, "No OpenCL platforms available\n"); 176 return NULL; 177 } 178 dprintf(("found %d platform%s:\n", nplatforms, nplatforms == 1 ? "" : "s")); 179 CHECKNOTZERO(tp = (UCLInfo *) malloc(sizeof(UCLInfo))); 180 ctxprop[0] = CL_CONTEXT_PLATFORM; 181 ctxprop[1] = 0; /* will fill in platform in loop */ 182 ctxprop[2] = 0; 183 cores = devcores = 0; 184 for (i = 0; i < nplatforms; i++) { 185 dprintf(("platform %d: 0x%lx\n", i, (unsigned long)platforms[i])); 186 clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, UCL_MAX_DEVICES, 187 devices, &ndevices); 188 // Sometimes, a platform with no devices will just return a failure instead 189 // of 0 devices, do not be deterred by that ... 190 if (err != CL_SUCCESS) { 191 fprintf(stderr, "%s: error %d: %s from clGetDeviceIDs for platform %d ( 0x%lx )\n", progname, err, print_cl_errstring(err), i, 192 (unsigned long)platforms[i]); 193 continue; 194 } 195 dprintf(("platform 0x%lx has %d devices:\n", (unsigned long)platforms[i], ndevices)); 196 for (j = 0; j < ndevices; j++) { 197 UCLInfo uc; 198 uc.devid = devices[j]; 199 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_NAME, 200 sizeof uc.devname, uc.devname, 0)); 201 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_VENDOR, 202 sizeof uc.vendor, uc.vendor, 0)); 203 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_VERSION, 204 sizeof uc.version, uc.version, 0)); 205 CHECK(clGetDeviceInfo(devices[j], CL_DRIVER_VERSION, 206 sizeof uc.driver, uc.driver, 0)); 207 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_MAX_CLOCK_FREQUENCY, 208 sizeof uc.clkfreq, &uc.clkfreq, 0)); 209 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_MAX_COMPUTE_UNITS, 210 sizeof uc.compunits, &uc.compunits, 0)); 211 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_MAX_WORK_GROUP_SIZE, 212 sizeof uc.wgsize, &uc.wgsize, 0)); 213 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_DOUBLE_FP_CONFIG, 214 sizeof uc.fpdbl, &uc.fpdbl, 0)); 215 CHECK(clGetDeviceInfo(devices[j], CL_DEVICE_TYPE, 216 sizeof uc.devtype, &uc.devtype, 0)); 217 uc.computeflags = 0; 218 #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 219 { 220 cl_uint nvmaj, nvmin; 221 if(clGetDeviceInfo(devices[j], CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, 222 sizeof(nvmaj), &nvmaj, 0) == CL_SUCCESS && 223 clGetDeviceInfo(devices[j], CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, 224 sizeof(nvmin), &nvmin, 0) == CL_SUCCESS) { 225 uc.computeflags = nvmaj*10 + nvmin; 226 } 227 } 228 #endif 229 cores = uc.compunits; 230 /* XXX Hardwired knowledge about devices */ 231 if (coremultguess) { 232 cores *= coremultguess; 233 printf("Using override cores per unit %d so %d cores\n", coremultguess, cores); 234 } else if (strstr(uc.devname, "Cayman") || strstr(uc.devname, "Tahiti")) { 235 /* 236 * Most modern AMD compute units (shader cluster?) 237 * are a 16-lane SIMD Engine with 4 (Cayman) or 5 238 * (Cypress) VLIW slots per lane. AMD appears to 239 * think of each slot as a "stream processor" 240 * (shader processor) in their marketing i.e. a 241 * Cayman-based Radeon 6950 with 24 compute units 242 * has 1536 stream processors. 243 * With Tahiti/Southern Islands/GCN, each compute 244 * unit has four vector execution SIMD units, each 245 * with 16 lanes. So the Tahiti-based Radeon 7970 with 246 * 32 compute units has 2048 cores/stream processors. 247 */ 248 cores *= 16*4; 249 } else if (strstr(uc.devname, "Cypress")) { 250 cores *= 16*5; 251 } else if (strstr(uc.devname, "GTX TITAN X") || 252 strstr(uc.devname, "GTX 9")) { 253 cores *= 128; 254 } else if (strstr(uc.devname, "GTX 6") || 255 strstr(uc.devname, "GTX 7") || 256 strstr(uc.devname, "Tesla K2") || 257 strstr(uc.devname, "Tesla K4") || 258 strstr(uc.devname, "GTX TITAN")) { 259 /* Kepler has 192 cores per SMX */ 260 cores *= 192; 261 } else if (strstr(uc.devname, "GTX 580") || 262 strstr(uc.devname, "GTX 480") || 263 strstr(uc.devname, "C20") || 264 strstr(uc.devname, "M20")) { 265 /* 266 * Fermi has 32 cores per SM. Maybe use 267 * computeflags to figure this out? 268 */ 269 cores *= 32; 270 } else if (uc.devtype == CL_DEVICE_TYPE_GPU) { 271 fprintf(stderr, "Unknown # of cores per unit for this device \"%s\", assuming 1, so cpb may be wrong and choice of threads may be suboptimal, fix by setting R123EXAMPLE_ENVCONF_OPENCL_CORES_PER_UNIT\n", 272 uc.devname); 273 } 274 /* clkfreq is in Megahertz! */ 275 uc.cycles = 1e6 * uc.clkfreq * cores; 276 dprintf((" %d: device 0x%lx vendor %s %s version %s driver %s : %u compute units @ %u MHz %d cores cycles/s %.2f flags %d fpdbl 0x%lx\n", 277 j, (unsigned long) devices[j], uc.vendor, uc.devname, uc.version, 278 uc.driver, uc.compunits, uc.clkfreq, cores, uc.cycles, uc.computeflags, 279 (unsigned long) uc.fpdbl)); 280 if (devstr && strstr(uc.devname, devstr) == NULL) { 281 if (verbose || debug) 282 printf("skipping device %s\n", uc.devname); 283 continue; 284 } 285 if (cores > devcores) { 286 ctxprop[1] = (cl_context_properties) platforms[i]; 287 devcores = cores; 288 *tp = uc; 289 } 290 } 291 } 292 if (devcores == 0) { 293 fprintf(stderr, "%s: No matching devices found\n", progname); 294 exit(1); 295 } 296 tp->cores = devcores; 297 298 // using DEVICE_MAX_WORKGROUP_SIZE as the workgroup size seems to break 299 // weirdly on NVIDIA SDK 4.0.17 (the returned ctr arrays are all zeros) 300 // Halving it seems to produce as good or fractionally better performance 301 // on AMD, so seems a good choice. -- mm, 20110831 302 if (tp->wgsize > 2) { 303 tp->wgsize /= 2; 304 } 305 printf("device 0x%lx %s : %d units %d cores %.2f Gcycles/s %lu maxwg %s device\n", 306 (unsigned long)tp->devid, tp->devname, tp->compunits, devcores, tp->cycles*1e-9, tp->wgsize, cldevtypestr(tp->devtype)); 307 CHECKERR(tp->ctx = clCreateContext(ctxprop, 1, &tp->devid, 0, 0, &err)); 308 dprintf(("create OpenCL context for device 0x%lx %s\n", (unsigned long)tp->devid, tp->devname)); 309 CHECKERR(tp->cmdq = clCreateCommandQueue(tp->ctx, tp->devid, 0, &err)); 310 /* 311 * create & compile OpenCL program from source string. Could 312 * normalize this out of the context but that creates a more 313 * complex API. 314 */ 315 dprintf(("create OpenCL program from source\n")); 316 317 /* If the device has support for double, enable it, might need it for u01.h */ 318 i = 0; 319 #define UCLDBL "\n\ 320 #ifdef cl_khr_fp64\n\ 321 #pragma OPENCL EXTENSION cl_khr_fp64 : enable\n\ 322 #elif defined(cl_amd_fp64)\n\ 323 #pragma OPENCL EXTENSION cl_amd_fp64 : enable\n\ 324 #endif\n\ 325 " 326 if (tp->fpdbl) { 327 srcstr[i++] = UCLDBL; 328 } 329 srcstr[i++] = src; 330 CHECKERR(tp->prog = clCreateProgramWithSource(tp->ctx, i, srcstr, 0, &err)); 331 if ((err = clBuildProgram(tp->prog, 1, &tp->devid, options, 0, 0)) != CL_SUCCESS || debug) { 332 char errbuf[512*1024]; 333 strcpy(errbuf, "<error report not filled in>"); 334 cl_int builderr = err; 335 CHECK(clGetProgramBuildInfo(tp->prog, tp->devid, CL_PROGRAM_BUILD_LOG, 336 sizeof errbuf, &errbuf[0], 0)); 337 fprintf(stderr, "%s: OpenCL build for device id 0x%lx %s returned error %d (%s): %s\n", 338 progname, (unsigned long) tp->devid, tp->devname, builderr, print_cl_errstring(builderr), errbuf); 339 if (builderr != CL_SUCCESS) 340 exit(1); 341 } 342 if ((clbinfile = getenv("R123_SAVE_OPENCL_BINARY")) != NULL) { 343 size_t sz, szret; 344 unsigned char *binp; 345 FILE *fp; 346 CHECKERR(clGetProgramInfo(tp->prog, CL_PROGRAM_BINARY_SIZES, sizeof(sz), &sz, &szret)); 347 CHECKNOTZERO(szret); 348 CHECKNOTZERO(sz); 349 printf("szret %lu, sz %lu\n", szret, (unsigned long) sz); 350 if (szret > 0 && sz > 0) { 351 CHECKNOTZERO((binp = (unsigned char *) malloc(sz))); 352 CHECKERR(clGetProgramInfo(tp->prog, CL_PROGRAM_BINARIES, sizeof(binp), &binp, &szret)); 353 CHECKNOTZERO(szret); 354 CHECKNOTZERO(fp = fopen(clbinfile, "wc")); 355 CHECKEQUAL(sz, fwrite(binp, 1, sz, fp)); 356 CHECKZERO(fclose(fp)); 357 free(binp); 358 printf("wrote OpenCL binary to %s\n", clbinfile); 359 } 360 } 361 dprintf(("opencl_init done\n")); 362 /* XXX Save build programs as .deviceid so we can read them back and run? */ 363 return tp; 364 } 365 366 367 static void opencl_done(UCLInfo *tp) { 368 cl_int err; 369 370 dprintf(("opencl_done\n")); 371 CHECK(clReleaseCommandQueue(tp->cmdq)); 372 tp->cmdq = 0; 373 CHECK(clReleaseProgram(tp->prog)); 374 tp->prog = 0; 375 CHECK(clReleaseContext(tp->ctx)); 376 tp->ctx = 0; 377 free(tp); 378 } 379 380 381 #endif /* UTIL_OPENCL_H__ */