Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages

CUDABench.cu

Go to the documentation of this file.
00001 /***************************************************************************
00002 *cr
00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the
00004 *cr University of Illinois
00005 *cr All Rights Reserved
00006 *cr
00007 ***************************************************************************/
00008 /***************************************************************************
00009 * RCS INFORMATION:
00010 *
00011 * $RCSfile: CUDABench.cu,v $
00012 * $Author: johns $ $Locker: $ $State: Exp $
00013 * $Revision: 1.42 $ $Date: 2022年02月09日 04:03:19 $
00014 *
00015 ***************************************************************************/
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <cuda.h>
00025 
00026 #include "Inform.h"
00027 #include "WKFThreads.h"
00028 #include "WKFUtils.h"
00029 #include "CUDAKernels.h" 
00030 #include "Measure.h"
00031 
00032 
00033 //
00034 // Restrict macro to make it easy to do perf tuning tests
00035 //
00036 #if 1
00037 #define RESTRICT __restrict__
00038 #else
00039 #define RESTRICT
00040 #endif
00041 
00042 
00043 //#define VMDUSECUDAGDS 1
00044 #if defined(VMDUSECUDAGDS)
00045 #include </usr/local/gds-beta-0.7.1/lib/cufile.h> // GPU-Direct Storage
00046 
00047 // direct calls to JS plugin for devel/testing until the plugin manager
00048 // and headers incorporate the new out-of-core GPU-direct I/O hooks.
00049 #define VMDJSPLUGININCLUDESRC 1
00050 #include "/home/johns/plugins/molfile_plugin/src/jsplugin.c"
00051 #endif
00052 
00053 
00054 #define CUERR { cudaError_t err; \
00055 if ((err = cudaGetLastError()) != cudaSuccess) { \
00056 printf("CUDA error: %s, %s line %d\n", cudaGetErrorString(err), __FILE__, __LINE__); \
00057 return -1; }}
00058 
00059 
00060 //
00061 // Benchmark peak Multiply-Add instruction performance, in GFLOPS
00062 //
00063 
00064 // FMADD16 macro contains a sequence of operations that the compiler
00065 // won't optimize out, and will translate into a densely packed block
00066 // of multiply-add instructions with no intervening register copies/moves
00067 // or other instructions. 
00068 #define FMADD16 \
00069 tmp0 = tmp0*tmp4+tmp7; \
00070 tmp1 = tmp1*tmp5+tmp0; \
00071 tmp2 = tmp2*tmp6+tmp1; \
00072 tmp3 = tmp3*tmp7+tmp2; \
00073 tmp4 = tmp4*tmp0+tmp3; \
00074 tmp5 = tmp5*tmp1+tmp4; \
00075 tmp6 = tmp6*tmp2+tmp5; \
00076 tmp7 = tmp7*tmp3+tmp6; \
00077 tmp8 = tmp8*tmp12+tmp15; \
00078 tmp9 = tmp9*tmp13+tmp8; \
00079 tmp10 = tmp10*tmp14+tmp9; \
00080 tmp11 = tmp11*tmp15+tmp10; \
00081 tmp12 = tmp12*tmp8+tmp11; \
00082 tmp13 = tmp13*tmp9+tmp12; \
00083 tmp14 = tmp14*tmp10+tmp13; \
00084 tmp15 = tmp15*tmp11+tmp14;
00085 
00086 // CUDA grid, thread block, loop, and MADD operation counts
00087 #define GRIDSIZEX 6144 // number of 1-D thread blocks
00088 #define BLOCKSIZEX 64 // number of threads per 1-D block
00089 #define GLOOPS 2000 // iteration count (all threads)
00090 #define FMADD16COUNT 32 // 32 reps
00091 #define FLOPSPERFMADD16 32 // 16 MULs and 16 ADDs
00092 
00093 // FLOP counting
00094 #define FLOPSPERLOOP (FMADD16COUNT * FLOPSPERFMADD16)
00095 
00096 __global__ static void madd_kernel(float *doutput) {
00097 int tid = blockIdx.x * blockDim.x + threadIdx.x;
00098 float tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7;
00099 float tmp8,tmp9,tmp10,tmp11,tmp12,tmp13,tmp14,tmp15;
00100 tmp0=tmp1=tmp2=tmp3=tmp4=tmp5=tmp6=tmp7=0.0f;
00101 tmp8=tmp9=tmp10=tmp11=tmp12=tmp13=tmp14=tmp15 = 0.0f;
00102 
00103 tmp15=tmp7 = blockIdx.x * 0.001f; // prevent compiler from optimizing out
00104 tmp1 = blockIdx.y * 0.001f; // the body of the loop...
00105 
00106 int loop;
00107 for(loop=0; loop<GLOOPS; loop++){
00108 FMADD16
00109 FMADD16
00110 FMADD16
00111 FMADD16
00112 FMADD16
00113 FMADD16
00114 FMADD16
00115 FMADD16
00116 FMADD16
00117 FMADD16
00118 FMADD16
00119 FMADD16
00120 FMADD16
00121 FMADD16
00122 FMADD16
00123 FMADD16
00124 FMADD16
00125 FMADD16
00126 FMADD16
00127 FMADD16
00128 FMADD16
00129 FMADD16
00130 FMADD16
00131 FMADD16
00132 FMADD16
00133 FMADD16
00134 FMADD16
00135 FMADD16
00136 FMADD16
00137 FMADD16
00138 FMADD16
00139 FMADD16
00140 }
00141 
00142 doutput[tid] = tmp0+tmp1+tmp2+tmp3+tmp4+tmp5+tmp6+tmp7
00143 +tmp8+tmp9+tmp10+tmp11+tmp12+tmp13+tmp14+tmp15;
00144 }
00145 
00146 
00147 static int cudamaddgflops(int cudadev, double *gflops, int testloops) {
00148 float *doutput = NULL;
00149 dim3 Bsz, Gsz;
00150 wkf_timerhandle timer;
00151 int i;
00152 
00153 cudaError_t rc;
00154 rc = cudaSetDevice(cudadev);
00155 if (rc != cudaSuccess) {
00156 #if CUDART_VERSION >= 2010
00157 rc = cudaGetLastError(); // query last error and reset error state
00158 if (rc != cudaErrorSetOnActiveProcess)
00159 return -1; // abort and return an error
00160 #else
00161 cudaGetLastError(); // just ignore and reset error state, since older CUDA
00162 // revs don't have a cudaErrorSetOnActiveProcess enum
00163 #endif
00164 }
00165 
00166 
00167 // setup CUDA grid and block sizes
00168 Bsz.x = BLOCKSIZEX;
00169 Bsz.y = 1;
00170 Bsz.z = 1;
00171 Gsz.x = GRIDSIZEX;
00172 Gsz.y = 1;
00173 Gsz.z = 1;
00174 
00175 // allocate output array
00176 cudaMalloc((void**)&doutput, BLOCKSIZEX * GRIDSIZEX * sizeof(float));
00177 CUERR // check and clear any existing errors
00178 
00179 // warmup run
00180 madd_kernel<<<Gsz, Bsz>>>(doutput);
00181 cudaDeviceSynchronize(); // wait for kernel to finish
00182 
00183 // benchmark run
00184 timer=wkf_timer_create();
00185 wkf_timer_start(timer);
00186 for (i=0; i<testloops; i++) { 
00187 madd_kernel<<<Gsz, Bsz>>>(doutput);
00188 }
00189 cudaDeviceSynchronize(); // wait for kernel to finish
00190 CUERR // check and clear any existing errors
00191 wkf_timer_stop(timer);
00192 
00193 double runtime = wkf_timer_time(timer);
00194 double gflop = ((double) GLOOPS) * ((double) FLOPSPERLOOP) *
00195 ((double) BLOCKSIZEX) * ((double) GRIDSIZEX) * (1.0e-9) * testloops;
00196 
00197 *gflops = gflop / runtime;
00198 
00199 cudaFree(doutput);
00200 CUERR // check and clear any existing errors
00201 
00202 wkf_timer_destroy(timer);
00203 
00204 return 0;
00205 }
00206 
00207 typedef struct {
00208 int deviceid;
00209 int testloops;
00210 double gflops;
00211 } maddthrparms;
00212 
00213 static void * cudamaddthread(void *voidparms) {
00214 maddthrparms *parms = (maddthrparms *) voidparms;
00215 cudamaddgflops(parms->deviceid, &parms->gflops, parms->testloops);
00216 return NULL;
00217 }
00218 
00219 int vmd_cuda_madd_gflops(int numdevs, int *devlist, double *gflops,
00220 int testloops) {
00221 maddthrparms *parms;
00222 wkf_thread_t * threads;
00223 int i;
00224 
00225 /* allocate array of threads */
00226 threads = (wkf_thread_t *) calloc(numdevs * sizeof(wkf_thread_t), 1);
00227 
00228 /* allocate and initialize array of thread parameters */
00229 parms = (maddthrparms *) malloc(numdevs * sizeof(maddthrparms));
00230 for (i=0; i<numdevs; i++) {
00231 if (devlist != NULL)
00232 parms[i].deviceid = devlist[i];
00233 else
00234 parms[i].deviceid = i;
00235 
00236 parms[i].testloops = testloops;
00237 parms[i].gflops = 0.0;
00238 }
00239 
00240 #if defined(VMDTHREADS)
00241 /* spawn child threads to do the work */
00242 /* thread 0 must also be processed this way otherwise */
00243 /* we'll permanently bind the main thread to some device */
00244 for (i=0; i<numdevs; i++) {
00245 wkf_thread_create(&threads[i], cudamaddthread, &parms[i]);
00246 }
00247 
00248 /* join the threads after work is done */
00249 for (i=0; i<numdevs; i++) {
00250 wkf_thread_join(threads[i], NULL);
00251 }
00252 #else
00253 /* single thread does all of the work */
00254 cudamaddthread((void *) &parms[0]);
00255 #endif
00256 
00257 for (i=0; i<numdevs; i++) {
00258 gflops[i] = parms[i].gflops; 
00259 }
00260 
00261 /* free thread parms */
00262 free(parms);
00263 free(threads);
00264 
00265 return 0;
00266 }
00267 
00268 
00269 
00270 
00271 
00272 
00273 //
00274 // Host-GPU memcpy I/O bandwidth benchmark
00275 //
00276 
00277 #define BWITER 500
00278 #define LATENCYITER 50000
00279 
00280 static int cudabusbw(int cudadev, 
00281 double *hdmbsec, double *hdlatusec, 
00282 double *phdmbsec, double *phdlatusec, 
00283 double *dhmbsec, double *dhlatusec,
00284 double *pdhmbsec, double *pdhlatusec) {
00285 float *hdata = NULL; // non-pinned DMA buffer
00286 float *phdata = NULL; // pinned DMA buffer
00287 float *ddata = NULL;
00288 int i;
00289 double runtime;
00290 wkf_timerhandle timer;
00291 int memsz = 1024 * 1024 * sizeof(float);
00292 
00293 *hdmbsec = 0.0;
00294 *hdlatusec = 0.0;
00295 *dhmbsec = 0.0;
00296 *dhlatusec = 0.0;
00297 *phdmbsec = 0.0;
00298 *phdlatusec = 0.0;
00299 *pdhmbsec = 0.0;
00300 *pdhlatusec = 0.0;
00301 
00302 // attach to the selected device
00303 cudaError_t rc;
00304 rc = cudaSetDevice(cudadev);
00305 if (rc != cudaSuccess) {
00306 #if CUDART_VERSION >= 2010
00307 rc = cudaGetLastError(); // query last error and reset error state
00308 if (rc != cudaErrorSetOnActiveProcess)
00309 return -1; // abort and return an error
00310 #else
00311 cudaGetLastError(); // just ignore and reset error state, since older CUDA
00312 // revs don't have a cudaErrorSetOnActiveProcess enum
00313 #endif
00314 }
00315 
00316 // allocate non-pinned output array
00317 hdata = (float *) malloc(memsz); 
00318 
00319 // allocate pinned output array
00320 cudaMallocHost((void**) &phdata, memsz);
00321 CUERR // check and clear any existing errors
00322 
00323 // allocate device memory
00324 cudaMalloc((void**) &ddata, memsz);
00325 CUERR // check and clear any existing errors
00326 
00327 // create timer
00328 timer=wkf_timer_create();
00329 
00330 //
00331 // Host to device timings
00332 //
00333 
00334 // non-pinned bandwidth
00335 wkf_timer_start(timer);
00336 for (i=0; i<BWITER; i++) {
00337 cudaMemcpy(ddata, hdata, memsz, cudaMemcpyHostToDevice);
00338 }
00339 wkf_timer_stop(timer);
00340 CUERR // check and clear any existing errors
00341 runtime = wkf_timer_time(timer);
00342 *hdmbsec = ((double) BWITER) * ((double) memsz) / runtime / (1024.0 * 1024.0);
00343 
00344 // non-pinned latency
00345 wkf_timer_start(timer);
00346 for (i=0; i<LATENCYITER; i++) {
00347 cudaMemcpy(ddata, hdata, 1, cudaMemcpyHostToDevice);
00348 }
00349 wkf_timer_stop(timer);
00350 CUERR // check and clear any existing errors
00351 runtime = wkf_timer_time(timer);
00352 *hdlatusec = runtime * 1.0e6 / ((double) LATENCYITER);
00353 
00354 
00355 // pinned bandwidth
00356 wkf_timer_start(timer);
00357 for (i=0; i<BWITER; i++) {
00358 cudaMemcpy(ddata, phdata, memsz, cudaMemcpyHostToDevice);
00359 }
00360 wkf_timer_stop(timer);
00361 CUERR // check and clear any existing errors
00362 runtime = wkf_timer_time(timer);
00363 *phdmbsec = ((double) BWITER) * ((double) memsz) / runtime / (1024.0 * 1024.0);
00364 
00365 // pinned latency
00366 wkf_timer_start(timer);
00367 for (i=0; i<LATENCYITER; i++) {
00368 cudaMemcpy(ddata, phdata, 1, cudaMemcpyHostToDevice);
00369 }
00370 wkf_timer_stop(timer);
00371 CUERR // check and clear any existing errors
00372 runtime = wkf_timer_time(timer);
00373 *phdlatusec = runtime * 1.0e6 / ((double) LATENCYITER);
00374 
00375 
00376 //
00377 // Device to host timings
00378 //
00379 
00380 // non-pinned bandwidth
00381 wkf_timer_start(timer);
00382 for (i=0; i<BWITER; i++) {
00383 cudaMemcpy(hdata, ddata, memsz, cudaMemcpyDeviceToHost);
00384 }
00385 wkf_timer_stop(timer);
00386 CUERR // check and clear any existing errors
00387 runtime = wkf_timer_time(timer);
00388 *dhmbsec = ((double) BWITER) * ((double) memsz) / runtime / (1024.0 * 1024.0);
00389 
00390 // non-pinned latency
00391 wkf_timer_start(timer);
00392 for (i=0; i<LATENCYITER; i++) {
00393 cudaMemcpy(hdata, ddata, 1, cudaMemcpyDeviceToHost);
00394 }
00395 wkf_timer_stop(timer);
00396 CUERR // check and clear any existing errors
00397 runtime = wkf_timer_time(timer);
00398 *dhlatusec = runtime * 1.0e6 / ((double) LATENCYITER);
00399 
00400 
00401 // pinned bandwidth
00402 wkf_timer_start(timer);
00403 for (i=0; i<BWITER; i++) {
00404 cudaMemcpy(phdata, ddata, memsz, cudaMemcpyDeviceToHost);
00405 }
00406 wkf_timer_stop(timer);
00407 CUERR // check and clear any existing errors
00408 runtime = wkf_timer_time(timer);
00409 *pdhmbsec = ((double) BWITER) * ((double) memsz) / runtime / (1024.0 * 1024.0);
00410 
00411 // pinned latency
00412 wkf_timer_start(timer);
00413 for (i=0; i<LATENCYITER; i++) {
00414 cudaMemcpy(phdata, ddata, 1, cudaMemcpyDeviceToHost);
00415 }
00416 wkf_timer_stop(timer);
00417 CUERR // check and clear any existing errors
00418 runtime = wkf_timer_time(timer);
00419 *pdhlatusec = runtime * 1.0e6 / ((double) LATENCYITER);
00420 
00421 
00422 cudaFree(ddata);
00423 CUERR // check and clear any existing errors
00424 cudaFreeHost(phdata);
00425 CUERR // check and clear any existing errors
00426 free(hdata);
00427 
00428 wkf_timer_destroy(timer);
00429 
00430 return 0;
00431 }
00432 
00433 typedef struct {
00434 int deviceid;
00435 double hdmbsec;
00436 double hdlatusec;
00437 double phdmbsec;
00438 double phdlatusec;
00439 double dhmbsec;
00440 double dhlatusec;
00441 double pdhmbsec;
00442 double pdhlatusec;
00443 } busbwthrparms;
00444 
00445 static void * cudabusbwthread(void *voidparms) {
00446 busbwthrparms *parms = (busbwthrparms *) voidparms;
00447 cudabusbw(parms->deviceid, 
00448 &parms->hdmbsec, &parms->hdlatusec,
00449 &parms->phdmbsec, &parms->phdlatusec,
00450 &parms->dhmbsec, &parms->dhlatusec,
00451 &parms->pdhmbsec, &parms->pdhlatusec);
00452 return NULL;
00453 }
00454 
00455 int vmd_cuda_bus_bw(int numdevs, int *devlist, 
00456 double *hdmbsec, double *hdlatusec,
00457 double *phdmbsec,double *phdlatusec,
00458 double *dhmbsec, double *dhlatusec,
00459 double *pdhmbsec, double *pdhlatusec) {
00460 busbwthrparms *parms;
00461 wkf_thread_t * threads;
00462 int i;
00463 
00464 /* allocate array of threads */
00465 threads = (wkf_thread_t *) calloc(numdevs * sizeof(wkf_thread_t), 1);
00466 
00467 /* allocate and initialize array of thread parameters */
00468 parms = (busbwthrparms *) malloc(numdevs * sizeof(busbwthrparms));
00469 for (i=0; i<numdevs; i++) {
00470 if (devlist != NULL)
00471 parms[i].deviceid = devlist[i];
00472 else
00473 parms[i].deviceid = i;
00474 parms[i].hdmbsec = 0.0;
00475 parms[i].hdlatusec = 0.0;
00476 parms[i].phdmbsec = 0.0;
00477 parms[i].phdlatusec = 0.0;
00478 parms[i].dhmbsec = 0.0;
00479 parms[i].dhlatusec = 0.0;
00480 parms[i].pdhmbsec = 0.0;
00481 parms[i].pdhlatusec = 0.0;
00482 }
00483 
00484 #if defined(VMDTHREADS)
00485 /* spawn child threads to do the work */
00486 /* thread 0 must also be processed this way otherwise */
00487 /* we'll permanently bind the main thread to some device */
00488 for (i=0; i<numdevs; i++) {
00489 wkf_thread_create(&threads[i], cudabusbwthread, &parms[i]);
00490 }
00491 
00492 /* join the threads after work is done */
00493 for (i=0; i<numdevs; i++) {
00494 wkf_thread_join(threads[i], NULL);
00495 }
00496 #else
00497 /* single thread does all of the work */
00498 cudabusbwthread((void *) &parms[0]);
00499 #endif
00500 
00501 for (i=0; i<numdevs; i++) {
00502 hdmbsec[i] = parms[i].hdmbsec; 
00503 hdlatusec[i] = parms[i].hdlatusec; 
00504 phdmbsec[i] = parms[i].phdmbsec; 
00505 phdlatusec[i] = parms[i].phdlatusec; 
00506 dhmbsec[i] = parms[i].dhmbsec; 
00507 dhlatusec[i] = parms[i].dhlatusec; 
00508 pdhmbsec[i] = parms[i].pdhmbsec; 
00509 pdhlatusec[i] = parms[i].pdhlatusec; 
00510 }
00511 
00512 /* free thread parms */
00513 free(parms);
00514 free(threads);
00515 
00516 return 0;
00517 }
00518 
00519 
00520 
00521 //
00522 // GPU device global memory bandwidth benchmark
00523 //
00524 template <class T>
00525 __global__ void gpuglobmemcpybw(T *dest, const T *src) {
00526 const unsigned int idx = threadIdx.x + blockIdx.x * blockDim.x;
00527 dest[idx] = src[idx];
00528 }
00529 
00530 template <class T>
00531 __global__ void gpuglobmemsetbw(T *dest, const T val) {
00532 int idx = threadIdx.x + blockIdx.x * blockDim.x;
00533 dest[idx] = val;
00534 }
00535 
00536 typedef float4 datatype;
00537 
00538 static int cudaglobmembw(int cudadev, double *gpumemsetgbsec, double *gpumemcpygbsec) {
00539 int i;
00540 int len = 1 << 22; // one thread per data element
00541 int loops = 500;
00542 datatype *src, *dest;
00543 datatype val=make_float4(1.0f, 1.0f, 1.0f, 1.0f);
00544 
00545 // initialize to zero for starters
00546 float memsettime = 0.0f;
00547 float memcpytime = 0.0f;
00548 *gpumemsetgbsec = 0.0;
00549 *gpumemcpygbsec = 0.0;
00550 
00551 // attach to the selected device
00552 cudaError_t rc;
00553 rc = cudaSetDevice(cudadev);
00554 if (rc != cudaSuccess) {
00555 #if CUDART_VERSION >= 2010
00556 rc = cudaGetLastError(); // query last error and reset error state
00557 if (rc != cudaErrorSetOnActiveProcess)
00558 return -1; // abort and return an error
00559 #else
00560 cudaGetLastError(); // just ignore and reset error state, since older CUDA
00561 // revs don't have a cudaErrorSetOnActiveProcess enum
00562 #endif
00563 }
00564 
00565 cudaMalloc((void **) &src, sizeof(datatype)*len);
00566 CUERR
00567 cudaMalloc((void **) &dest, sizeof(datatype)*len);
00568 CUERR
00569 
00570 dim3 BSz(256, 1, 1);
00571 dim3 GSz(len / (BSz.x * BSz.y * BSz.z), 1, 1); 
00572 
00573 // do a warm-up pass
00574 gpuglobmemsetbw<datatype><<< GSz, BSz >>>(src, val);
00575 CUERR
00576 gpuglobmemsetbw<datatype><<< GSz, BSz >>>(dest, val);
00577 CUERR
00578 gpuglobmemcpybw<datatype><<< GSz, BSz >>>(dest, src);
00579 CUERR
00580 
00581 cudaEvent_t start, end;
00582 cudaEventCreate(&start);
00583 cudaEventCreate(&end);
00584 
00585 // execute the memset kernel
00586 cudaEventRecord(start, 0);
00587 for (i=0; i<loops; i++) {
00588 gpuglobmemsetbw<datatype><<< GSz, BSz >>>(dest, val);
00589 }
00590 CUERR
00591 cudaEventRecord(end, 0);
00592 CUERR
00593 cudaEventSynchronize(start);
00594 CUERR
00595 cudaEventSynchronize(end);
00596 CUERR
00597 cudaEventElapsedTime(&memsettime, start, end);
00598 CUERR
00599 
00600 // execute the memcpy kernel
00601 cudaEventRecord(start, 0);
00602 for (i=0; i<loops; i++) {
00603 gpuglobmemcpybw<datatype><<< GSz, BSz >>>(dest, src);
00604 }
00605 cudaEventRecord(end, 0);
00606 CUERR
00607 cudaEventSynchronize(start);
00608 CUERR
00609 cudaEventSynchronize(end);
00610 CUERR
00611 cudaEventElapsedTime(&memcpytime, start, end);
00612 CUERR
00613 
00614 cudaEventDestroy(start);
00615 CUERR
00616 cudaEventDestroy(end);
00617 CUERR
00618 
00619 *gpumemsetgbsec = (len * sizeof(datatype) / (1024.0 * 1024.0)) / (memsettime / loops);
00620 *gpumemcpygbsec = (2 * len * sizeof(datatype) / (1024.0 * 1024.0)) / (memcpytime / loops);
00621 cudaFree(dest);
00622 cudaFree(src);
00623 CUERR
00624 
00625 return 0;
00626 }
00627 
00628 typedef struct {
00629 int deviceid;
00630 double memsetgbsec;
00631 double memcpygbsec;
00632 } globmembwthrparms;
00633 
00634 static void * cudaglobmembwthread(void *voidparms) {
00635 globmembwthrparms *parms = (globmembwthrparms *) voidparms;
00636 cudaglobmembw(parms->deviceid, &parms->memsetgbsec, &parms->memcpygbsec);
00637 return NULL;
00638 }
00639 
00640 int vmd_cuda_globmem_bw(int numdevs, int *devlist, 
00641 double *memsetgbsec, double *memcpygbsec) {
00642 globmembwthrparms *parms;
00643 wkf_thread_t * threads;
00644 int i;
00645 
00646 /* allocate array of threads */
00647 threads = (wkf_thread_t *) calloc(numdevs * sizeof(wkf_thread_t), 1);
00648 
00649 /* allocate and initialize array of thread parameters */
00650 parms = (globmembwthrparms *) malloc(numdevs * sizeof(globmembwthrparms));
00651 for (i=0; i<numdevs; i++) {
00652 if (devlist != NULL)
00653 parms[i].deviceid = devlist[i];
00654 else
00655 parms[i].deviceid = i;
00656 parms[i].memsetgbsec = 0.0;
00657 parms[i].memcpygbsec = 0.0;
00658 }
00659 
00660 #if defined(VMDTHREADS)
00661 /* spawn child threads to do the work */
00662 /* thread 0 must also be processed this way otherwise */
00663 /* we'll permanently bind the main thread to some device */
00664 for (i=0; i<numdevs; i++) {
00665 wkf_thread_create(&threads[i], cudaglobmembwthread, &parms[i]);
00666 }
00667 
00668 /* join the threads after work is done */
00669 for (i=0; i<numdevs; i++) {
00670 wkf_thread_join(threads[i], NULL);
00671 }
00672 #else
00673 /* single thread does all of the work */
00674 cudaglobmembwthread((void *) &parms[0]);
00675 #endif
00676 
00677 for (i=0; i<numdevs; i++) {
00678 memsetgbsec[i] = parms[i].memsetgbsec;
00679 memcpygbsec[i] = parms[i].memcpygbsec;
00680 }
00681 
00682 /* free thread parms */
00683 free(parms);
00684 free(threads);
00685 
00686 return 0;
00687 }
00688 
00689 
00690 //
00691 // Benchmark latency for complete threadpool barrier wakeup/run/sleep cycle
00692 //
00693 static void * vmddevpoollatencythread(void *voidparms) {
00694 return NULL;
00695 }
00696 
00697 static void * vmddevpooltilelatencythread(void *voidparms) {
00698 int threadid=-1;
00699 int tilesize=1;
00700 void *parms=NULL;
00701 wkf_threadpool_worker_getid(voidparms, &threadid, NULL);
00702 wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
00703 
00704 // grind through task tiles until none are left
00705 wkf_tasktile_t tile;
00706 while (wkf_threadpool_next_tile(voidparms, tilesize, &tile) != WKF_SCHED_DONE) {
00707 // do nothing but eat work units...
00708 }
00709 
00710 return NULL;
00711 }
00712 
00713 
00714 // no-op kernel for timing kernel launches
00715 __global__ static void nopkernel(float * ddata) {
00716 unsigned int xindex = blockIdx.x * blockDim.x + threadIdx.x;
00717 unsigned int yindex = blockIdx.y * blockDim.y + threadIdx.y;
00718 unsigned int outaddr = gridDim.x * blockDim.x * yindex + xindex;
00719 
00720 if (ddata != NULL)
00721 ddata[outaddr] = outaddr;
00722 }
00723 
00724 // empty kernel for timing kernel launches
00725 __global__ static void voidkernel(void) {
00726 return;
00727 }
00728 
00729 static void * vmddevpoolcudatilelatencythread(void *voidparms) {
00730 int threadid=-1;
00731 int tilesize=1;
00732 float *parms=NULL;
00733 wkf_threadpool_worker_getid(voidparms, &threadid, NULL);
00734 
00735 // XXX Note that we expect parms to be set to NULL or a valid CUDA
00736 // global memory pointer for correct operation of the NOP kernel below
00737 wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
00738 
00739 #if 0
00740 // scale tile size by device performance
00741 tilesize=4; // GTX 280, Tesla C1060 starting point tile size
00742 wkf_threadpool_worker_devscaletile(voidparms, &tilesize);
00743 #endif
00744 
00745 // grind through task tiles until none are left
00746 wkf_tasktile_t tile;
00747 dim3 Gsz(1,1,0);
00748 dim3 Bsz(8,8,1);
00749 while (wkf_threadpool_next_tile(voidparms, tilesize, &tile) != WKF_SCHED_DONE) {
00750 // launch a no-op CUDA kernel
00751 nopkernel<<<Gsz, Bsz, 0>>>(parms);
00752 }
00753 
00754 // wait for all GPU kernels to complete
00755 cudaDeviceSynchronize();
00756 
00757 return NULL;
00758 }
00759 
00760 
00761 int vmd_cuda_devpool_latency(wkf_threadpool_t *devpool, int tilesize,
00762 double *kernlaunchlatency,
00763 double *barlatency,
00764 double *cyclelatency, 
00765 double *tilelatency,
00766 double *kernellatency) {
00767 int i;
00768 wkf_tasktile_t tile;
00769 wkf_timerhandle timer;
00770 int loopcount;
00771 
00772 timer=wkf_timer_create();
00773 
00774 // execute just a CUDA kernel launch and measure latency on whatever
00775 // GPU we get.
00776 loopcount = 15000;
00777 dim3 VGsz(1,1,0);
00778 dim3 VBsz(8,8,1);
00779 wkf_timer_start(timer);
00780 for (i=0; i<loopcount; i++) {
00781 voidkernel<<<VGsz, VBsz, 0>>>();
00782 }
00783 // wait for GPU kernels to complete
00784 cudaDeviceSynchronize();
00785 wkf_timer_stop(timer);
00786 *kernlaunchlatency = wkf_timer_time(timer) / ((double) loopcount);
00787 
00788 // execute just a raw barrier sync and measure latency
00789 loopcount = 15000;
00790 wkf_timer_start(timer);
00791 for (i=0; i<loopcount; i++) {
00792 wkf_threadpool_wait(devpool);
00793 }
00794 wkf_timer_stop(timer);
00795 *barlatency = wkf_timer_time(timer) / ((double) loopcount);
00796 
00797 // time wake-up, launch, and sleep/join of device pool doing a no-op
00798 loopcount = 5000;
00799 wkf_timer_start(timer);
00800 for (i=0; i<loopcount; i++) {
00801 tile.start=0;
00802 tile.end=0;
00803 wkf_threadpool_sched_dynamic(devpool, &tile);
00804 wkf_threadpool_launch(devpool, vmddevpoollatencythread, NULL, 1);
00805 }
00806 wkf_timer_stop(timer);
00807 *cyclelatency = wkf_timer_time(timer) / ((double) loopcount);
00808 
00809 // time wake-up, launch, and sleep/join of device pool eating tiles
00810 loopcount = 5000;
00811 wkf_timer_start(timer);
00812 for (i=0; i<loopcount; i++) {
00813 tile.start=0;
00814 tile.end=tilesize;
00815 wkf_threadpool_sched_dynamic(devpool, &tile);
00816 wkf_threadpool_launch(devpool, vmddevpooltilelatencythread, NULL, 1);
00817 }
00818 wkf_timer_stop(timer);
00819 *tilelatency = wkf_timer_time(timer) / ((double) loopcount);
00820 
00821 // time wake-up, launch, and sleep/join of device pool eating tiles
00822 loopcount = 2000;
00823 wkf_timer_start(timer);
00824 for (i=0; i<loopcount; i++) {
00825 tile.start=0;
00826 tile.end=tilesize;
00827 wkf_threadpool_sched_dynamic(devpool, &tile);
00828 wkf_threadpool_launch(devpool, vmddevpoolcudatilelatencythread, NULL, 1);
00829 }
00830 wkf_timer_stop(timer);
00831 *kernellatency = wkf_timer_time(timer) / ((double) loopcount);
00832 
00833 wkf_timer_destroy(timer);
00834 
00835 #if 1
00836 vmd_cuda_measure_latencies(devpool);
00837 #endif
00838 
00839 return 0;
00840 }
00841 
00842 
00843 //
00844 // Benchmark CUDA kernel launch and memory copy latencies in isolation
00845 //
00846 typedef struct {
00847 int deviceid;
00848 int testloops;
00849 double kernlatency;
00850 double bcopylatency;
00851 double kbseqlatency;
00852 } latthrparms;
00853 
00854 static void * vmddevpoolcudalatencythread(void *voidparms) {
00855 int threadid=-1;
00856 latthrparms *parms=NULL;
00857 
00858 wkf_threadpool_worker_getid(voidparms, &threadid, NULL);
00859 wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
00860 if (parms->deviceid == threadid) { 
00861 wkf_timerhandle timer;
00862 timer=wkf_timer_create();
00863 printf("Thread/device %d running...\n", threadid);
00864 cudaStream_t devstream;
00865 cudaStreamCreate(&devstream);
00866 
00867 char *hostbuf = (char *) calloc(1, 65536 * sizeof(char));
00868 char *gpubuf = NULL;
00869 cudaMalloc((void**)&gpubuf, 65536 * sizeof(char));
00870 
00871 dim3 Gsz(1,1,0);
00872 dim3 Bsz(8,8,1);
00873 
00874 // measure back-to-back NULL kernel launches
00875 wkf_timer_start(timer);
00876 int i;
00877 for (i=0; i<parms->testloops; i++) {
00878 // launch a no-op CUDA kernel
00879 nopkernel<<<Gsz, Bsz, 0, devstream>>>(NULL);
00880 }
00881 // wait for all GPU kernels to complete
00882 cudaStreamSynchronize(devstream);
00883 wkf_timer_stop(timer);
00884 parms->kernlatency = 1000000 * wkf_timer_time(timer) / ((double) parms->testloops);
00885 
00886 // measure back-to-back round-trip 1-byte memcpy latencies
00887 wkf_timer_start(timer);
00888 for (i=0; i<parms->testloops; i++) {
00889 cudaMemcpyAsync(gpubuf, hostbuf, 1, cudaMemcpyHostToDevice, devstream);
00890 cudaMemcpyAsync(hostbuf, gpubuf, 1, cudaMemcpyDeviceToHost, devstream);
00891 }
00892 // wait for all GPU kernels to complete
00893 cudaStreamSynchronize(devstream);
00894 wkf_timer_stop(timer);
00895 parms->kernlatency = 1000000 * wkf_timer_time(timer) / ((double) parms->testloops);
00896 
00897 printf("NULL kernel launch latency (usec): %.2f\n", parms->kernlatency);
00898 
00899 cudaStreamDestroy(devstream);
00900 cudaFree(gpubuf);
00901 free(hostbuf);
00902 wkf_timer_destroy(timer);
00903 }
00904 
00905 return NULL;
00906 }
00907 
00908 
00909 int vmd_cuda_measure_latencies(wkf_threadpool_t *devpool) {
00910 latthrparms thrparms;
00911 int workers = wkf_threadpool_get_workercount(devpool);
00912 int i;
00913 printf("vmd_cuda_measure_latencies()...\n");
00914 for (i=0; i<workers; i++) {
00915 memset(&thrparms, 0, sizeof(thrparms));
00916 thrparms.deviceid = i;
00917 thrparms.testloops = 2500;
00918 wkf_threadpool_launch(devpool, vmddevpoolcudalatencythread, &thrparms, 1);
00919 }
00920 
00921 return 0;
00922 }
00923 
00924 
00925 #if defined(VMDUSECUDAGDS)
00926 typedef struct {
00927 int nfiles;
00928 const char **trjfileset;
00929 jshandle **jshandles;
00930 CUfileHandle_t *cfh;
00931 int devcount;
00932 int natoms;
00933 const AtomSel *sel;
00934 int first;
00935 int last;
00936 int step;
00937 } gpuoocbenchthreadparms;
00938 
00939 #define VMDGDSMAXFRAMEBUF 8
00940 static void * gpu_ooc_bench_thread(void *voidparms) {
00941 int threadid, numthreads;
00942 gpuoocbenchthreadparms *parms = NULL;
00943 wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
00944 wkf_threadpool_worker_getid(voidparms, &threadid, &numthreads);
00945 
00946 //
00947 // copy in per-thread parameters
00948 //
00949 int nfiles = parms->nfiles;
00950 int natoms = parms->natoms;
00951 const AtomSel *sel = parms->sel;
00952 int first = parms->first;
00953 int last = parms->last;
00954 int step = parms->step;
00955 
00956 int usecufile = 1;
00957 fio_fd *hostfds = NULL;
00958 int pinhostiobuffer = 1;
00959 if (getenv("VMDGDSHOSTNOPIN")) {
00960 pinhostiobuffer=0;
00961 }
00962 
00963 if (getenv("VMDGDSUSEHOST")) {
00964 usecufile=0;
00965 hostfds = (fio_fd *) calloc(1, nfiles * sizeof(fio_fd));
00966 
00967 int hostusedirectio = 1;
00968 if (getenv("VMDGDSHOSTBUFFEREDIO") != NULL)
00969 hostusedirectio = 0;
00970 
00971 int openmode = FIO_READ;
00972 if (hostusedirectio)
00973 openmode |= FIO_DIRECT;
00974 
00975 int i;
00976 for (i=0; i<nfiles; i++) {
00977 if (fio_open(parms->trjfileset[i], openmode, &hostfds[i]) < 0) {
00978 if (hostusedirectio) {
00979 printf("Thr[%d] direct I/O unavailable or can't open file '%s'\n",
00980 threadid, parms->trjfileset[i]);
00981 } else {
00982 printf("Thr[%d] can't open file '%s'\n",
00983 threadid, parms->trjfileset[i]);
00984 }
00985 return NULL;
00986 }
00987 }
00988 }
00989 
00990 if (hostfds && usecufile) {
00991 printf("Inconsistent cufile/hostfds state, aborting!\n");
00992 return NULL;
00993 }
00994 
00995 /* ensure we have a large enough allocation so we can align */
00996 /* the starting pointer to a blocksz page boundary */
00997 long blocksz = MOLFILE_DIRECTIO_MIN_BLOCK_SIZE;
00998 long sz = 3L*sizeof(float)*natoms + blocksz;
00999 
01000 /* pad the allocation to an even multiple of the block size */
01001 size_t blockpadsz = (sz + (blocksz - 1)) & (~(blocksz - 1));
01002 
01003 int framecount = (last - first + 1) / step;
01004 int framesperfile = framecount / nfiles;
01005 
01006 if (threadid == 0) {
01007 printf("Thr[%2d] %d frames total, natoms: %d selected: %d\n",
01008 threadid, framecount, natoms, sel->selected);
01009 printf("Thr[%2d] %d frames/file\n", threadid, framesperfile);
01010 }
01011 
01012 cudaError_t crc;
01013 cudaStream_t oocstream;
01014 float *devptr=NULL;
01015 float *hostptr=NULL;
01016 float *hostptr_unaligned=NULL;
01017 
01018 float *crdx1=NULL, *crdy1=NULL, *crdz1=NULL;
01019 float *crdx2=NULL, *crdy2=NULL, *crdz2=NULL;
01020 int multiframeio = 0;
01021 if (getenv("VMDGDSMULTIFRAME"))
01022 multiframeio = atoi(getenv("VMDGDSMULTIFRAME"));
01023 if (multiframeio > VMDGDSMAXFRAMEBUF)
01024 multiframeio = VMDGDSMAXFRAMEBUF;
01025 
01026 // set block sizes and counts for IO bench calcs
01027 dim3 IOBsz = dim3(256, 1, 1);
01028 dim3 IOGsz = dim3((natoms + IOBsz.x - 1) / IOBsz.x, 1, 1);
01029 
01030 if (parms->devcount > 0) {
01031 long gpuallocsz = (VMDGDSMAXFRAMEBUF+1) * blockpadsz;
01032 
01033 if (threadid == 0) {
01034 printf("Thr[%2d] Allocating GPU timestep I/O buf: %ld \n",
01035 threadid, gpuallocsz);
01036 }
01037 crc = cudaMalloc((void**) &devptr, gpuallocsz);
01038 
01039 if (hostfds != NULL) {
01040 if (pinhostiobuffer) {
01041 crc = cudaMallocHost((void**) &hostptr, gpuallocsz);
01042 } else {
01043 hostptr = (float *) alloc_aligned_ptr(gpuallocsz, 4096,
01044 (void**) &hostptr_unaligned);
01045 if (!hostptr) {
01046 printf("Thr[%d]: Failed allocation!\n", threadid);
01047 return NULL;
01048 }
01049 }
01050 }
01051 
01052 long crdsz = sel->selected * sizeof(float);
01053 
01054 // atomic coord buffers
01055 crc = cudaMalloc((void**) &crdx1, crdsz);
01056 crc = cudaMalloc((void**) &crdy1, crdsz);
01057 crc = cudaMalloc((void**) &crdz1, crdsz);
01058 crc = cudaMalloc((void**) &crdx2, crdsz);
01059 crc = cudaMalloc((void**) &crdy2, crdsz);
01060 crc = cudaMalloc((void**) &crdz2, crdsz);
01061 if (crc != cudaSuccess) {
01062 printf("Thr[%2d], Failed to allocate GPU buffer!\n", threadid);
01063 return NULL; // XXX error handling needs to be done here
01064 }
01065 
01066 cudaStreamCreate(&oocstream);
01067 
01068 #if defined(VMDUSECUDAGDS)
01069 cuFileBufRegister(devptr, gpuallocsz, 0);
01070 #endif
01071 }
01072 
01073 int verbose = (getenv("VMDGDSVERBOSE") != NULL) ? 1 : 0;
01074 
01075 int filestrategy = 0;
01076 if (getenv("VMDGDSFILESTRATEGY")) {
01077 filestrategy = atoi(getenv("VMDGDSFILESTRATEGY"));
01078 }
01079 if (threadid == 0) {
01080 printf("Thr[%2d] file strategy set to: %d\n", threadid, filestrategy);
01081 }
01082 
01083 wkf_tasktile_t tile;
01084 while (wkf_threadlaunch_next_tile(voidparms, VMDGDSMAXFRAMEBUF * 1, &tile) != WKF_SCHED_DONE) {
01085 //
01086 // simple I/O + compute benchmarking...
01087 //
01088 int idx;
01089 int threadspergpu;
01090 if (parms->devcount > 0)
01091 threadspergpu = numthreads / parms->devcount;
01092 else 
01093 threadspergpu = 1;
01094 
01095 for (idx=tile.start; idx<tile.end; idx++) {
01096 int myfileidx, fileframeidx;
01097 
01098 switch (filestrategy) {
01099 case 1:
01100 myfileidx = (idx / multiframeio) % nfiles;
01101 fileframeidx = idx % framesperfile;
01102 break;
01103 
01104 case 2:
01105 myfileidx = (idx / (multiframeio * threadspergpu)) % nfiles;
01106 fileframeidx = idx % framesperfile;
01107 break;
01108 
01109 case 3:
01110 myfileidx = (threadid / 4) % nfiles;
01111 fileframeidx = idx % framesperfile;
01112 break;
01113 
01114 case 0:
01115 default:
01116 myfileidx = (threadid / threadspergpu) % nfiles;
01117 fileframeidx = idx % framesperfile;
01118 break;
01119 }
01120 
01121 //
01122 // compute multi-frame or single-frame I/O offsets and sizes
01123 //
01124 long startoffset, foffset, readlen;
01125 read_js_timestep_index_offsets(parms->jshandles[myfileidx],
01126 natoms, fileframeidx, 0, natoms, NULL,
01127 &startoffset, &foffset, &readlen);
01128 if (multiframeio) {
01129 // multi-frame reads use the same starting offset, but the 
01130 // read length is computed from the first and last frames 
01131 // in the group.
01132 long multistartoffset, multifoffset, multireadlen;
01133 read_js_timestep_index_offsets(parms->jshandles[myfileidx], natoms,
01134 fileframeidx+multiframeio-1,
01135 0, natoms, NULL,
01136 &multistartoffset, &multifoffset,
01137 &multireadlen);
01138 
01139 multireadlen = (multifoffset + multireadlen) - foffset;
01140 
01141 //printf("** readlen: %ld multireadlen: %ld\n", readlen, multireadlen);
01142 readlen = multireadlen;
01143 idx+=multiframeio-1; // add in the required increment...
01144 }
01145 
01146 //
01147 // perform the required I/O via GDS or by host kernel I/O
01148 //
01149 long ret=0;
01150 if (usecufile) {
01151 ret = cuFileRead(parms->cfh[myfileidx], (char *) devptr, readlen, foffset, 0);
01152 } else if (hostfds) {
01153 foffset=0;
01154 ret=fio_fseek(hostfds[myfileidx], foffset, FIO_SEEK_SET);
01155 if (ret<0) { printf("fio_fseek() error!\n"); return NULL; }
01156 ret=fio_fread(hostptr, readlen, 1, hostfds[myfileidx]);
01157 if (ret<0) { printf("fio_fseek() error!\n"); return NULL; }
01158 cudaMemcpy(devptr, hostptr, readlen, cudaMemcpyHostToDevice);
01159 } else {
01160 printf("Inconsistent cufile/hostfds state, aborting!\n");
01161 return NULL;
01162 }
01163 
01164 // handle errors if they have occured
01165 if (ret < 0) {
01166 printf("Thr[%2d] Error: cuFileRead(): %ld\n", threadid, ret);
01167 return NULL; // XXX error handling needs to be done here
01168 }
01169 
01170 if (verbose) {
01171 printf("Thr[%2d]F[%d][tile: %d to %d] frame: %d cuFile len: %ld off: %ld\n",
01172 threadid, myfileidx, tile.start, tile.end, idx, 
01173 readlen, foffset);
01174 }
01175 }
01176 }
01177 
01178 cudaFree(crdx1);
01179 cudaFree(crdy1);
01180 cudaFree(crdz1);
01181 cudaFree(crdx2);
01182 cudaFree(crdy2);
01183 cudaFree(crdz2);
01184 
01185 #if defined(VMDUSECUDAGDS)
01186 if (usecufile) {
01187 cuFileBufDeregister(devptr);
01188 }
01189 #endif
01190 
01191 if (hostfds != NULL) {
01192 int i;
01193 for (i=0; i<nfiles; i++) {
01194 fio_fclose(hostfds[i]);
01195 }
01196 free(hostfds);
01197 }
01198 
01199 if (hostptr != NULL) {
01200 if (pinhostiobuffer) {
01201 cudaFreeHost(hostptr);
01202 } else {
01203 free(hostptr_unaligned);
01204 }
01205 }
01206 
01207 return NULL;
01208 }
01209 
01210 #endif
01211 
01212 
01213 int gpu_ooc_bench(wkf_threadpool_t *devpool, // VMD GPU worker thread pool
01214 int nfiles, const char **trjfileset, const AtomSel *sel,
01215 int first, int last, int step) {
01216 printf("gpu_ooc_bench()\n");
01217 wkf_threadpool_t *bigpool = NULL;
01218 
01219 #if defined(VMDUSECUDAGDS)
01220 int devcount;
01221 cudaError_t crc = cudaGetDeviceCount(&devcount);
01222 printf("gpu_ooc_bench) GPU device count: %d\n", devcount);
01223 if (devcount==0)
01224 printf("gpu_ooc_bench) No GPU devices, continuing with host only...\n");
01225 
01226 CUfileHandle_t * cfh = (CUfileHandle_t *) calloc(1, nfiles * sizeof(CUfileHandle_t));
01227 CUfileDescr_t * cfhdesc = (CUfileDescr_t *) calloc(1, nfiles * sizeof(CUfileDescr_t));
01228 memset(&cfh[0], 0, sizeof(cfh));
01229 memset(&cfhdesc[0], 0, sizeof(cfhdesc));
01230 
01231 int natomschk = 0;
01232 jshandle **jshandles = (jshandle **) calloc(1, nfiles * sizeof(jshandle *));
01233 fio_fd *directio_fds = (fio_fd *) calloc(1, nfiles * sizeof(fio_fd));
01234 
01235 int i;
01236 for (i=0; i<nfiles; i++) {
01237 const char *filename = trjfileset[i];
01238 printf("gpu_ooc_bench) File[%d] GDS setup, opening '%s'\n", i, filename);
01239 jshandles[i] = (jshandle *) open_js_read(filename, "js", &natomschk);
01240 if (!jshandles[i]) {
01241 printf("gpu_ooc_bench) File[%d] open_js_read failed for file %s\n", i, filename);
01242 return -1; // deal with error handling later
01243 }
01244 
01245 #if vmdplugin_ABIVERSION > 17
01246 long blocksz = MOLFILE_DIRECTIO_MIN_BLOCK_SIZE;
01247 int filepgalignsz = 1;
01248 read_js_timestep_pagealign_size(jshandles[i], &filepgalignsz);
01249 if (filepgalignsz != blocksz) {
01250 printf("gpu_ooc_bench) File[%d] Plugin-returned page alignment size mismatch!\n", i);
01251 } else {
01252 printf("gpu_ooc_bench) File[%d] Page alignment size: %d\n", i, filepgalignsz);
01253 }
01254 #endif
01255 
01256 read_js_timestep_index_offsets(jshandles[i], natomschk, 0, 0, 0,
01257 &directio_fds[i], NULL, NULL, NULL);
01258 
01259 cfhdesc[i].handle.fd = directio_fds[i]; // typedef of Unix FD
01260 cfhdesc[i].type = CU_FILE_HANDLE_TYPE_OPAQUE_FD;
01261 CUfileError_t cferr = cuFileHandleRegister(&cfh[i], &cfhdesc[i]);
01262 
01263 if (cferr.err != CU_FILE_SUCCESS) {
01264 printf("gpu_ooc_bench) File[%d] cuFileImportExternalFile on fd %d failed!\n",
01265 i, cfhdesc[i].handle.fd);
01266 return -1; // XXX error handling needs to be done here
01267 }
01268 }
01269 
01270 
01271 //
01272 // copy in per-thread parameters
01273 //
01274 gpuoocbenchthreadparms parms;
01275 memset(&parms, 0, sizeof(parms));
01276 parms.devcount = devcount;
01277 parms.nfiles = nfiles;
01278 parms.trjfileset = trjfileset;
01279 parms.jshandles = jshandles;
01280 parms.cfh = cfh;
01281 parms.natoms = sel->num_atoms;
01282 parms.sel = sel;
01283 parms.first = first;
01284 parms.last = last;
01285 parms.step = step;
01286 
01287 int framecount = nfiles * (last / step);
01288 
01289 // create timers
01290 wkf_timerhandle timer;
01291 timer=wkf_timer_create();
01292 
01293 // spawn child threads to do the work
01294 wkf_tasktile_t tile;
01295 tile.start=0;
01296 tile.end=framecount - 1; // first row only
01297 
01298 printf("gpu_ooc_bench) tile start: %d end: %d\n", tile.start, tile.end);
01299 
01300 int gdsthreadspergpu = 1;
01301 if (getenv("VMDGDSTHREADSPERGPU") != NULL)
01302 gdsthreadspergpu = atoi(getenv("VMDGDSTHREADSPERGPU"));
01303 
01304 printf("gpu_ooc_bench) gdsthreadspergpu: %d\n", gdsthreadspergpu);
01305 
01306 if (gdsthreadspergpu > 1) {
01307 // XXX extra-large GPU device thread pool
01308 int workercount = devcount * gdsthreadspergpu;
01309 
01310 int *devlist = new int[workercount];
01311 int k;
01312 for (k=0; k<workercount; k++) {
01313 devlist[k] = k / gdsthreadspergpu; // XXX ignores VMD CUDA device masks
01314 }
01315 
01316 msgInfo << "Creating Multi-worker ("
01317 << gdsthreadspergpu << " per-GPU) CUDA device pool..." << sendmsg;
01318 bigpool=wkf_threadpool_create(workercount, devlist);
01319 delete [] devlist;
01320 
01321 // associate each worker thread with a specific GPU
01322 if (getenv("VMDCUDAVERBOSE") != NULL)
01323 wkf_threadpool_launch(bigpool, vmd_cuda_devpool_setdeviceonly, (void*)"VMD CUDA Dev Init", 1);
01324 else
01325 wkf_threadpool_launch(bigpool, vmd_cuda_devpool_setdeviceonly, NULL, 1);
01326 
01327 // clear all available device memory on each of the GPUs
01328 wkf_threadpool_launch(bigpool, vmd_cuda_devpool_clear_device_mem, NULL, 1);
01329 
01330 // XXX override which GPU device pool we're going to use
01331 devpool = bigpool;
01332 }
01333 
01334 // XXX affinitize GPU worker threads for best perf
01335 wkf_threadpool_launch(devpool, vmd_cuda_affinitize_threads, NULL, 1);
01336 
01337 wkf_threadpool_sched_dynamic(devpool, &tile);
01338 wkf_timer_start(timer);
01339 wkf_threadpool_launch(devpool, gpu_ooc_bench_thread, &parms, 1);
01340 wkf_timer_stop(timer);
01341 
01342 double runtime = wkf_timer_time(timer);
01343 double gbytes = sel->num_atoms * 12L * (tile.end+1) / (1024.0 * 1024.0 * 1024.0);
01344 
01345 printf("gpu_ooc_bench) natoms: %d, fsz: %ld, tsz: %ld\n",
01346 sel->num_atoms, sel->num_atoms * 12L,
01347 sel->num_atoms * 12L * (tile.end+1));
01348 
01349 int pinhostiobuffer = 1;
01350 if (getenv("VMDGDSHOSTNOPIN"))
01351 pinhostiobuffer=0;
01352 
01353 int hostusedirectio = 1;
01354 if (getenv("VMDGDSHOSTBUFFEREDIO") != NULL)
01355 hostusedirectio = 0;
01356 
01357 int usecufile=1;
01358 if (getenv("VMDGDSUSEHOST"))
01359 usecufile=0;
01360 
01361 if (usecufile) {
01362 printf("OOC I/O via GDS + cuFile\n");
01363 } else {
01364 printf("OOC I/O via host, %s APIs, %s memory buffers\n",
01365 (hostusedirectio) ? "Direct I/O" : "Buffered I/O",
01366 (pinhostiobuffer) ? "pinned" : "unpinned");
01367 }
01368 
01369 int multiframeio = 0;
01370 if (getenv("VMDGDSMULTIFRAME"))
01371 multiframeio = atoi(getenv("VMDGDSMULTIFRAME"));
01372 if (multiframeio > VMDGDSMAXFRAMEBUF)
01373 multiframeio = VMDGDSMAXFRAMEBUF;
01374 if (multiframeio) {
01375 printf("GDS multi-frame read opt: %d frames per call, %ld bytes\n",
01376 multiframeio,
01377 multiframeio * sel->num_atoms * 12L);
01378 }
01379 
01380 printf("OOC runtime: %.1f, %.2fGB/sec\n", runtime, gbytes/runtime);
01381 
01382 for (i=0; i<nfiles; i++) {
01383 #if defined(VMDUSECUDAGDS)
01384 cuFileHandleDeregister(cfh[i]);
01385 #endif
01386 close_js_read(jshandles[i]);
01387 }
01388 #endif
01389 
01390 #if defined(VMDUSECUDAGDS)
01391 if (cfh != NULL)
01392 free(cfh);
01393 
01394 if (cfhdesc != NULL)
01395 free(cfhdesc);
01396 
01397 if (jshandles != NULL)
01398 free(jshandles);
01399 
01400 if (directio_fds != NULL)
01401 free(directio_fds);
01402 #endif
01403 
01404 // if we created an extra large-thread-count-per-GPU thread pool, we
01405 // need to destroy it here...
01406 if (bigpool != NULL)
01407 wkf_threadpool_destroy(bigpool);
01408 
01409 return 0;
01410 } 
01411 
01412 
01413 

Generated on Wed Nov 19 02:46:04 2025 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002

AltStyle によって変換されたページ (->オリジナル) /