OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-CompactHash / gpu.c

467 lines · c ref:
/* Copyright 2013-19.  Triad National Security, LLC. This material was produced
 * under U.S. Government contract 89233218CNA000001 for Los Alamos National 
 * Laboratory (LANL), which is operated by Triad National Security, LLC
 * for the U.S. Department of Energy. The U.S. Government has rights to use,
 * reproduce, and distribute this software.  NEITHER THE GOVERNMENT NOR
 * TRIAD NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
 * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified
 * to produce derivative works, such modified software should be clearly marked,
 * so as not to confuse it with the version available from LANL.   
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at 
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Under this license, it is required to include a reference to this work. We
 * request that each derivative work contain a reference to LANL Copyright 
 * Disclosure C14043/LA-CC-14-003 so that this work's impact can be roughly
 * measured.
 *
 * This is LANL Copyright Disclosure C14043/LA-CC-14-003
 */

/*
 *  Authors: Bob Robey         XCP-2   brobey@lanl.gov
 *           Peter Ahrens      XCP-2   peter.ahrens@lanl.gov, ptrahrens@gmail.com
 *           Sara Hartse       XCP-2   sara@lanl.gov, 
 *           Rebecka Tumblin           rtumblin@lanl.gov, rebeckatumblin@gmail.com
 */

/* Modified from LANL Copyright Disclosure C13002/LA-CC-12-022 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "gpu.h"

#ifdef HAVE_CL_DOUBLE
typedef double real;
#ifdef HAVE_OPENCL
typedef cl_double cl_real;
typedef cl_double4 cl_real4;
#endif
#else
typedef float real;
#ifdef HAVE_OPENCL
typedef cl_float cl_real;
typedef cl_float4 cl_real4;
#endif
#endif

#ifndef DEVICE_DETECT_DEBUG
#define DEVICE_DETECT_DEBUG 0
#endif

#ifdef HAVE_OPENCL
void GPUInit(cl_context *context, cl_command_queue *queue, int *device_type, cl_program *program, char *filename, char *addlibsource) {
  if (DEVICE_DETECT_DEBUG){
    printf("============== Hardware detection report ===============\n");
  }

  cl_platform_id* platforms;
  cl_platform_id platform = NULL;
  cl_uint num_platforms;
  cl_uint num_devices;
  cl_device_id* devices;
  int *device_appropriate;
  int device_selected = -99;
  cl_int platform_selected = -1;
  //cl_program program;
  cl_int error = 0;
  
  error = clGetPlatformIDs(0, NULL, &num_platforms);
  if (DEVICE_DETECT_DEBUG){
    printf("\nGPUInit: %d opencl platform(s) detected\n",num_platforms);
  }
  if(error != CL_SUCCESS) {
      printf("GPU_INIT: Error with clGetPlatformIDs call in file %s at line %d\n", __FILE__, __LINE__);
      if (error == CL_INVALID_VALUE){
         printf("GPU_INIT: Invalid value in clGetPlatformID call\n");
      }    
      exit(error);
  }
  if (num_platforms == 0) { 
     printf("GPU_INIT: Error -- No opencl platforms detected in file %s at line %d\n", __FILE__, __LINE__);
     exit(-1);
  }
  if (DEVICE_DETECT_DEBUG){
     printf("\n\nGPU_INIT: %d opencl platform(s) detected\n",num_platforms);
  }

  platforms = (cl_platform_id*)malloc(num_platforms*sizeof(cl_platform_id));
  error = clGetPlatformIDs(num_platforms, platforms, NULL);
  if (error != CL_SUCCESS){
     printf("GPU_INIT: Error with clGetPlatformIDs call in file %s at line %d\n", __FILE__, __LINE__);
     if (error == CL_INVALID_VALUE){
        printf("Invalid value in clGetPlatformID call\n");
     }    
  }

  if (DEVICE_DETECT_DEBUG){
     char info[1024];
     for (uint iplatform=0; iplatform<num_platforms; iplatform++){
        printf("  Platform %d:\n",iplatform+1);

        //clGetPlatformInfo(platforms[iplatform],CL_PLATFORM_PROFILE,   1024L,info,0);
        //printf("    CL_PLATFORM_PROFILE    : %s\n",info);

        clGetPlatformInfo(platforms[iplatform],CL_PLATFORM_VERSION,   1024L,info,0);
        printf("    CL_PLATFORM_VERSION    : %s\n",info);

        clGetPlatformInfo(platforms[iplatform],CL_PLATFORM_NAME,      1024L,info,0);
        printf("    CL_PLATFORM_NAME       : %s\n",info);

        clGetPlatformInfo(platforms[iplatform],CL_PLATFORM_VENDOR,    1024L,info,0);
        printf("    CL_PLATFORM_VENDOR     : %s\n",info);

        //clGetPlatformInfo(platforms[iplatform],CL_PLATFORM_EXTENSIONS,1024L,info,0);
        //printf("    CL_PLATFORM_EXTENSIONS : %s\n",info);
     }
     printf("\n");
  }

  char info[1024];
  clGetPlatformInfo(platforms[0],CL_PLATFORM_VENDOR, 1024, info, 0);

  // Get the number of devices, allocate, and get the devices
  for (uint iplatform=0; iplatform<num_platforms; iplatform++){
     error = clGetDeviceIDs(platforms[iplatform],CL_DEVICE_TYPE_GPU,0,NULL,&num_devices);
     if (error == CL_DEVICE_NOT_FOUND) {
        if (DEVICE_DETECT_DEBUG) {
          printf("Warning: Device of requested type not found for platform %d in clGetDeviceID call\n",iplatform);
        }
        continue;
     }
     if (error != CL_SUCCESS) {
       /* Possible Errors
        *  CL_INVALID_PLATFORM:
        *  CL_INVALID_DEVICE_TYPE:
        *  CL_INVALID_VALUE:
        *  CL_DEVICE_NOT_FOUND:
        */
       printf("GPU_INIT clGetDeviceIDs error %d file %s line %d\n", error, __FILE__, __LINE__);
     }
     if (DEVICE_DETECT_DEBUG){
        printf("GPU_INIT: %d opencl devices(s) detected\n",num_devices);
     }
     platform_selected = iplatform;
     platform = platforms[iplatform];
     //nDevices_selected = nDevices;
  }

  if (platform_selected == -1){
     printf("Warning: Device of requested type not found in clGetDeviceID call\n");
     exit(-1);
  }

  devices = (cl_device_id*)malloc(num_devices*sizeof(cl_device_id));
  device_appropriate = malloc(num_devices*sizeof(int));
  
  error = clGetDeviceIDs(platforms[platform_selected], CL_DEVICE_TYPE_GPU, num_devices, devices, NULL);
  if(error != CL_SUCCESS) {
    printf("Error getting device ids\n");
    exit(error);
  }
 
  int idevice_appropriate = 0;
  for (uint idevice=0; idevice<num_devices; idevice++){
     device_appropriate[idevice] = device_double_support(devices[idevice]);;
     if (device_appropriate[idevice] == 1){
        if (device_selected == -99) device_selected = idevice;
        devices[idevice_appropriate] = devices[idevice];
        idevice_appropriate++;
     }
     if (DEVICE_DETECT_DEBUG){
        printf(  "  Device %d:\n", idevice+1);
        device_info(devices[idevice]);
     }
  }
  num_devices = idevice_appropriate;
  
  *device_type = VENDOR_UNKNOWN;
  char *device_string[] = {"Unknown", "Nvidia", "Tesla K20Xm", "Tesla M2090",
    "Tesla 2050/2070", "GeForce GT 650M", "ATI", "MIC"};

  if (DEVICE_DETECT_DEBUG){
    printf("\n");
    printf("  Device:\n");
  }
  clGetDeviceInfo(devices[0], CL_DEVICE_NAME, sizeof(info), &info, NULL);
  if (DEVICE_DETECT_DEBUG){
    printf("    CL_DEVICE_NAME         : %s\n",info);
  }

  if (! strncmp(info,"Intel(R) Many Integrated Core",29) )  *device_type = MIC;
  if (! strncmp(info,"Tesla K20Xm",11) )       *device_type = TESLA_K20Xm;
  if (! strncmp(info,"Tesla M2090",11) )       *device_type = TESLA_M2090;
  if (! strncmp(info,"Tesla 2050 / 2070",17) ) *device_type = TESLA_2050_2070;
  if (! strncmp(info,"GeForce GT 650M",15) )   *device_type = GeFORCE_GT_650M;
  
  clGetDeviceInfo(devices[0], CL_DEVICE_VENDOR, sizeof(info), &info, NULL);
  if (DEVICE_DETECT_DEBUG){
     printf("    CL_DEVICE_VENDOR       : %s\n",info);
  }

  if (*device_type == VENDOR_UNKNOWN){
     if (! strncmp(info,"ATI",5) )    *device_type = ATI;
     if (! strncmp(info,"NVIDIA",6) ) *device_type = NVIDIA;
  }
  
  size_t isize;
  clGetDeviceInfo(devices[0], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(isize), &isize, NULL);
  if (DEVICE_DETECT_DEBUG){
    printf("    CL_DEVICE_MAX_WORK_GROUP_SIZE : %lu\n",isize);
    printf("\n");

    printf("    device vendor type set : %s\n",device_string[*device_type]);
  }

  cl_context_properties context_properties[3]=
  {
    CL_CONTEXT_PLATFORM,
    (cl_context_properties)platform,
    0 // 0 terminates list
  };   

  *context = clCreateContext(context_properties, num_devices, devices, NULL, NULL, &error);
  if(error != CL_SUCCESS) {
    printf("Error creating context\n");
    exit(error);
  }
  *queue = clCreateCommandQueue(*context, devices[0], CL_QUEUE_PROFILING_ENABLE, &error);
  if(error != CL_SUCCESS) {
    printf("Error creating command queue\n");
    exit(error);
  }
  
  if (DEVICE_DETECT_DEBUG){
    printf("============== Hardware detection report ===============\n");
  }

  // Load the kernel source code into the array source
  struct stat statbuf;
  FILE *fh;
  char *source;
  
  fh = fopen(filename, "r");
  if (!fh) {
    fprintf(stderr, "Failed to load kernel.\n");
    exit(-1);
  }
  stat(filename, &statbuf);
  
  int string_pos = 0;
  //int line_count = 0;
  int addlibsource_length = strlen(addlibsource);
  //printf("DEBUG -- length is addlibsource_length %d\n",addlibsource_length);
  if (addlibsource != NULL && addlibsource_length > 0){
    source = (char*)malloc(statbuf.st_size + 1 + addlibsource_length+1);
    strncpy(source, addlibsource, addlibsource_length);
    string_pos = addlibsource_length;
    source[string_pos] = '\n';
    string_pos++;
    //const char *src_string = source;
    //for (uint sindex = 0; sindex < addlibsource_length; src_string++, sindex++){
    //   if (! strncmp(src_string, '\n', 1)) line_count++;
    //}
    //printf("Offset line error in compile by %d lines\n",line_count);
  } else {
    source = (char*)malloc(statbuf.st_size + 1);
  }
  if( fread(&source[string_pos], statbuf.st_size, 1, fh) != 1) {
    printf("Problem reading program source file\n");
  }
  source[string_pos+statbuf.st_size] = '\0';
  //printf("DEBUG string is %s\n",source);
  fclose( fh );
  
  *program = clCreateProgramWithSource(*context, 1, (const char**) &source, NULL, &error);
  if (error != CL_SUCCESS){
    printf("clCreateProgramWithSource returned an error %d at line %d in file %s\n", error,__LINE__,__FILE__);
  }
  //printf("%d %s\n", (int)statbuf.st_size, source);
  
  size_t nReportSize;
  char* BuildReport;
  
  char CompileString[80];
  CompileString[0]='\0';

  #ifdef HAVE_CL_DOUBLE
    strcat(CompileString,"-DHAVE_CL_DOUBLE");
  #else
    strcat(CompileString,"-DNO_CL_DOUBLE -cl-single-precision-constant");
  #endif
  if (*device_type == NVIDIA) strcat(CompileString, " -DIS_NVIDIA");
  if (*device_type == TESLA_K20Xm)     strcat(CompileString, " -DIS_NVIDIA");
  if (*device_type == TESLA_M2090)     strcat(CompileString, " -DIS_NVIDIA");
  if (*device_type == TESLA_2050_2070) strcat(CompileString, " -DIS_NVIDIA");
  if (*device_type == GeFORCE_GT_650M) strcat(CompileString, " -DIS_NVIDIA");

  error = clBuildProgram(*program, 1, devices, CompileString, NULL, NULL);

  if (error != CL_SUCCESS){
    printf("clBuildProgram returned an error %d at line %d in file %s\n", error,__LINE__,__FILE__);
    error = clGetProgramBuildInfo(*program, devices[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &nReportSize);
    if (error != CL_SUCCESS) {
      switch (error){
        case CL_INVALID_DEVICE:
          printf("Invalid device in clProgramBuildInfo\n");
          break;
        case CL_INVALID_VALUE:
          printf("Invalid value in clProgramBuildInfo\n");
          break;
        case CL_INVALID_PROGRAM:
          printf("Invalid program in clProgramBuildInfo\n");
          break;
      }
    }
    
    BuildReport = (char *)malloc(nReportSize);
    
    error = clGetProgramBuildInfo(*program, devices[0], CL_PROGRAM_BUILD_LOG, nReportSize, BuildReport, NULL);
    if (error != CL_SUCCESS) {
      switch (error){
        case CL_INVALID_DEVICE:
          printf("Invalid device in clProgramBuildInfo\n");
          break;
        case CL_INVALID_VALUE:
          printf("Invalid value in clProgramBuildInfo\n");
          break;
        case CL_INVALID_PROGRAM:
          printf("Invalid program in clProgramBuildInfo\n");
          break;
      }
    }
    printf("%s\n", BuildReport);
  }
}

int device_double_support(cl_device_id device){
   int have_double = 0;
   char info[1024];

   clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, sizeof(info), &info, NULL);

   if (!(strstr(info,"cl_khr_fp64") == NULL)){
     if (DEVICE_DETECT_DEBUG){
        printf(  "    Device has double : %s\n\n", strstr(info,"cl_khr_fp64"));
     }
     have_double = 1;
   }

   return(have_double);
}

void device_info(cl_device_id device){
   if (device == NULL) {
      printf(" Error with device in device_info\n");
   }
   char info[1024];
   cl_bool iflag;
   cl_uint inum;
   size_t isize;
   cl_ulong ilong;
   cl_device_type device_type;
   cl_command_queue_properties iprop;

   clGetDeviceInfo(device,CL_DEVICE_TYPE,sizeof(device_type),&device_type,0);
   if( device_type & CL_DEVICE_TYPE_CPU )
      printf("    CL_DEVICE_TYPE                       : %s\n", "CL_DEVICE_TYPE_CPU");
   if( device_type & CL_DEVICE_TYPE_GPU )
      printf("    CL_DEVICE_TYPE                       : %s\n", "CL_DEVICE_TYPE_GPU");
   if( device_type & CL_DEVICE_TYPE_ACCELERATOR )
      printf("    CL_DEVICE_TYPE                       : %s\n", "CL_DEVICE_TYPE_ACCELERATOR");
   if( device_type & CL_DEVICE_TYPE_DEFAULT )
      printf("    CL_DEVICE_TYPE                       : %s\n", "CL_DEVICE_TYPE_DEFAULT");

   clGetDeviceInfo(device,CL_DEVICE_AVAILABLE,sizeof(iflag),&iflag,0);
   if (iflag == CL_TRUE) {
      printf(  "    CL_DEVICE_AVAILABLE                  : TRUE\n");
   } else {
      printf(  "    CL_DEVICE_AVAILABLE                  : FALSE\n");
   }

   clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(info), &info, NULL);
   printf(  "    CL_DEVICE_VENDOR                     : %s\n", info);

   clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(info), &info, NULL);
   printf(  "    CL_DEVICE_NAME                       : %s\n", info);

   clGetDeviceInfo(device, CL_DRIVER_VERSION, sizeof(info), &info, NULL);
   printf(  "    CL_DRIVER_VERSION                    : %s\n", info);

   clGetDeviceInfo(device, CL_DEVICE_VERSION, sizeof(info), &info, NULL);
   printf(  "    CL_DEVICE_VERSION                    : %s\n", info);

   clGetDeviceInfo(device,CL_DEVICE_MAX_COMPUTE_UNITS,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_MAX_COMPUTE_UNITS          : %d\n", inum);

   clGetDeviceInfo(device,CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS   : %d\n", inum);

   size_t *item_sizes = (size_t *)malloc(inum*sizeof(size_t));
   clGetDeviceInfo(device,CL_DEVICE_MAX_WORK_ITEM_SIZES,sizeof(item_sizes),item_sizes,0);
   printf(  "    CL_DEVICE_MAX_WORK_ITEM_SIZES        : %ld %ld %ld\n",
         item_sizes[0], item_sizes[1], item_sizes[2]);
   free(item_sizes);

   clGetDeviceInfo(device,CL_DEVICE_MAX_WORK_GROUP_SIZE,sizeof(isize),&isize,0);
   printf(  "    CL_DEVICE_MAX_WORK_GROUP_SIZE        : %ld\n", isize);

   clGetDeviceInfo(device,CL_DEVICE_MAX_CLOCK_FREQUENCY,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_MAX_CLOCK_FREQUENCY        : %d\n", inum);

   clGetDeviceInfo(device,CL_DEVICE_MAX_MEM_ALLOC_SIZE,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_MAX_MEM_ALLOC_SIZE         : %d\n", inum);

#ifdef __APPLE_CC__
   clGetDeviceInfo(device,CL_DEVICE_GLOBAL_MEM_SIZE,sizeof(ilong),&ilong,0);
   printf(  "    CL_DEVICE_GLOBAL_MEM_SIZE            : %llu\n", ilong);

   clGetDeviceInfo(device,CL_DEVICE_GLOBAL_MEM_CACHE_SIZE,sizeof(ilong),&ilong,0);
   printf(  "    CL_DEVICE_GLOBAL_MEM_CACHE_SIZE      : %llu\n", ilong);
#else
   clGetDeviceInfo(device,CL_DEVICE_GLOBAL_MEM_SIZE,sizeof(ilong),&ilong,0);
   printf(  "    CL_DEVICE_GLOBAL_MEM_SIZE            : %lu\n", ilong);

   clGetDeviceInfo(device,CL_DEVICE_GLOBAL_MEM_CACHE_SIZE,sizeof(ilong),&ilong,0);
   printf(  "    CL_DEVICE_GLOBAL_MEM_CACHE_SIZE      : %lu\n", ilong);
#endif
   clGetDeviceInfo(device,CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE  : %d\n", inum);

   clGetDeviceInfo(device,CL_DEVICE_MAX_CONSTANT_ARGS,sizeof(inum),&inum,0);
   printf(  "    CL_DEVICE_GLOBAL_MAX_CONSTANT_ARGS   : %d\n", inum);

   clGetDeviceInfo(device,CL_DEVICE_ERROR_CORRECTION_SUPPORT,sizeof(iflag),&iflag,0);
   if (iflag == CL_TRUE) {
      printf(  "    CL_DEVICE_ERROR_CORRECTION_SUPPORT   : TRUE\n");
   } else {
      printf(  "    CL_DEVICE_ERROR_CORRECTION_SUPPORT   : FALSE\n");
   }

   clGetDeviceInfo(device,CL_DEVICE_PROFILING_TIMER_RESOLUTION,sizeof(isize),&isize,0);
   printf(  "    CL_DEVICE_PROFILING_TIMER_RESOLUTION : %ld nanosecs\n", isize);

   clGetDeviceInfo(device,CL_DEVICE_QUEUE_PROPERTIES,sizeof(iprop),&iprop,0);
   if (iprop & CL_QUEUE_PROFILING_ENABLE) {
      printf(  "    CL_DEVICE_QUEUE PROFILING            : AVAILABLE\n");
   } else {
      printf(  "    CL_DEVICE_QUEUE PROFILING            : NOT AVAILABLE\n");
   }

   clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, sizeof(info), &info, NULL);
   printf(  "    CL_DEVICE_EXTENSIONS                 : %s\n\n", info);

}
#endif