random123

Counter-based Random Number Generators
git clone git://git.meso-star.com/random123.git
Log | Files | Refs | README | LICENSE

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__ */