2015年5月24日日曜日

NVAPIを使ってGPUの各種情報を取得する

前のエントリNVAPIを使ってGPUの使用メモリ量を求めるでメモリ量を取得したが、それ以外の情報も取得してみる。

GPU温度の取得
  // 物理GPUハンドルは取得済みであるものとする
  NvPhysicalGpuHandle hPhysicalGpu = ...
  NvS32 temperature = 0;
  NvAPI_Status nv_status = NVAPI_OK;
  NV_GPU_THERMAL_SETTINGS thermal = {0,};
  thermal.version = NV_GPU_THERMAL_SETTINGS_VER;
  nv_status = NvAPI_GPU_GetThermalSettings( hPhysicalGpu, 0, &thermal );
  if ( nv_status == NVAPI_OK ) {
    if ( temp != NULL ) {
      for ( size_t i=0; i<NVAPI_MAX_THERMAL_SENSORS_PER_GPU; i++ ) {
        // targetがNVAPI_THERMAL_TARGET_GPUかどうかチェック
        if ( thermal.sensor[i].target == NVAPI_THERMAL_TARGET_GPU ) {
          temperature = thermal.sensor[i].currentTemp;
          break;
        }
      }
    }
  }

GPU使用率の取得
  // 物理GPUハンドルは取得済みであるものとする
  NvPhysicalGpuHandle hPhysicalGpu = ...
  NvAPI_Status nv_status = NVAPI_OK;
  // 現在のGPU使用率の取得
  NvU32 act_pct = 0;
  NV_GPU_DYNAMIC_PSTATES_INFO_EX info = {0,};
  info.version = NV_GPU_DYNAMIC_PSTATES_INFO_EX_VER;
  nv_status = NvAPI_GPU_GetDynamicPstatesInfoEx( m_handle, &info );
  if ( nv_status == NVAPI_OK ) {
    // ドキュメントにはutilization[NVAPI_GPU_UTILIZATION_DOMAIN_GPU]で
    // 参照できると書いてあるが、ヘッダにはNVAPI_GPU_UTILIZATION_DOMAIN_GPUの
    // 定義が見当たらない。
    // おそらく GPU FB VID BUS と並んでいるので0番の要素を参照する。
    if ( info.utilization[0].bIsPresent > 0 ) {
      // 単位は[%]
      act_pct = info.utilization[0].percentage;
    }
  }

コアクロックの取得
  // 物理GPUハンドルは取得済みであるものとする
  NvPhysicalGpuHandle hPhysicalGpu = ...
  NvAPI_Status nv_status = NVAPI_OK;
  // 最大クロックの取得
  NvU32 max_clock = 0;
  NV_GPU_CLOCK_FREQUENCIES freqs = {0,};
  freqs.version = NV_GPU_CLOCK_FREQUENCIES_VER;
  freqs.ClockType = NV_GPU_CLOCK_FREQUENCIES_BOOST_CLOCK;
  nv_status = NvAPI_GPU_GetAllClockFrequencies( hPhysicalGpu, &freqs );
  if ( nv_status == NVAPI_OK ) {
    if ( freqs.domain[NVAPI_GPU_PUBLIC_CLOCK_GRAPHICS].bIsPresent > 0 ) {
      // 単位はkHz
      max_clock = freqs.domain[NVAPI_GPU_PUBLIC_CLOCK_GRAPHICS].frequency;
    }
  }
  // 現在のクロックの取得
  NvS32 cur_clock = 0;
  // ここではfreqs構造体を使いまわしている
  // 現在のクロックを取得する場合はClockTypeをNV_GPU_CLOCK_FREQUENCIES_CURRENT_FREQにする
  freqs.ClockType = NV_GPU_CLOCK_FREQUENCIES_CURRENT_FREQ;
  nv_status = NvAPI_GPU_GetAllClockFrequencies( hPhysicalGpu, &freqs );
  if ( nv_status == NVAPI_OK ) {
    if ( freqs.domain[NVAPI_GPU_PUBLIC_CLOCK_GRAPHICS].bIsPresent > 0 ) {
      cur_clock = freqs.domain[NVAPI_GPU_PUBLIC_CLOCK_GRAPHICS].frequency;
    }
  }

GPUファン速度の取得
※GPUモデルによっては取得できない場合がある
  // 物理GPUハンドルは取得済みであるものとする
  NvPhysicalGpuHandle hPhysicalGpu = ...
  NvAPI_Status nv_status = NVAPI_OK;
  // 最大回転数の取得
  NvU32 cur_rpm = 0;
  nv_status = NvAPI_GPU_GetTachReading( hPhysicalGpu , &cur_rpm );
  if ( nv_status == NVAPI_OK ) {
    // OK
  }

NVAPIを使ってGPUの使用メモリ量を求める

NVIDIA GPUで現在使用中のメモリ量を求めるにはNVAPIを使う。
搭載メモリ量であればOpenCLやOpenGLからも求めることができるが、メモリ使用量はNVAPIを使う必要がある。

NVAPI SDKを展開した場所をインクルードパスに追加し、nvapi.hをインクルードする。
#include "nvapi.h"
#if defined(_WIN32) && defined(_MSC_VER)
#if _M_AMD64
#pragma comment(lib,"amd64/nvapi64.lib")
#else
#pragma comment(lib,"x86/nvapi.lib")
#endif
#endif

NVAPIの初期化
NVIDIAドライバがインストールされた環境であればnvapi.dll/nvapi64.dllがシステムに存在しているはず。
  bool nvapi_initialized = false;
  NvAPI_Status ret = NVAPI_OK;
  ret = NvAPI_Initialize();
  if ( ret == NVAPI_OK ) {
    // OK
    nvapi_initialized = true;
  } else {
    // NG
  }

物理GPUの列挙
NVAPIには論理GPUと物理GPUという概念があるが、メモリ量などは物理GPUハンドルを用いて取得する。
  NvPhysicalGpuHandle  nvPhysicalGPUHandle[NVAPI_MAX_LOGICAL_GPUS] = {0,};
  NvU32 physicalGpuCount = 0;
  ret = NvAPI_EnumPhysicalGPUs( nvPhysicalGPUHandle, &physicalGpuCount );
  if ( ret == NVAPI_OK && pPhysicalGpuCount > 0 ) {
    // 1つ以上のGPUデバイスが存在する
  }

搭載メモリ量/使用メモリ量の取得
ここでは、1番目のデバイス(nvPhysicalGPUHandle[0])についてのみ取得している。
  // 搭載メモリ量の取得
  NvU32 mem_size = 0;
  ret = NvAPI_GPU_GetPhysicalFrameBufferSize( nvPhysicalGPUHandle[0], &mem_size );
  // 使用中メモリ量の取得
  Nv_U32 mem_usage = 0;
  // NVAPIには構造体で情報を受け取る関数がいくつか存在するが、だいたいこのように
  // あらかじめversionフィールドを初期化しておく必要がある。
  NV_DISPLAY_DRIVER_MEMORY_INFO info = {0,};
  info.version = NV_DISPLAY_DRIVER_MEMORY_INFO_VER;
  ret = NvAPI_GPU_GetMemoryInfo( nvPhysicalGPUHandle[0], &info );
  if ( nv_status == NVAPI_OK ) {
    mem_usage = info.dedicatedVideoMemory - info.curAvailableDedicatedVideoMemory;
  }

最後にNVAPIをUnloadする。
  if ( nvapi_initialized ) {
    NvAPI_Unload();
  }

2015年5月23日土曜日

NVIDIA GeForceドライバのOpenCL対応

350.05からOpenCL1.2に対応したが、GPU Caps ViewerのJulia Setデモの表示がおかしい。
350.12/352.86でも状況は変わらず。
- GPU 1
  - Name: NVIDIA GeForce GT 640 (rev2)
  - GPU codename: GK208
  - Device ID: 10DE-1284
  - Subdevice ID: 1569-1284
  - Driver: 14.301.1001.0 (R352.86)
  - Branch: r352_72-7
  - Bus Id: 2
  - Shader cores: 384
  - Texture units: 16
  - ROP units: 4
  - TDP: 49W
  - BIOS version: 80.28.3f.00.09
  - Memory size: 1023MB
  - Memory type: DDR3
  - Memory bus width: 64-bit
  - PState 0 - GPU clock: 901 MHz, memory: 900 MHz, VDDC: 0.913V
  - PState 8 - GPU clock: 405 MHz, memory: 405 MHz, VDDC: 0.875V

- CL_PLATFORM_NAME: NVIDIA CUDA
- CL_PLATFORM_VENDOR: NVIDIA Corporation
- CL_PLATFORM_VERSION: OpenCL 1.2 CUDA 7.5.8
- CL_PLATFORM_PROFILE: FULL_PROFILE
- Num devices: 1

 - CL_DEVICE_NAME: GeForce GT 630
 - CL_DEVICE_VENDOR: NVIDIA Corporation
 - CL_DRIVER_VERSION: 352.86
 - CL_DEVICE_PROFILE: FULL_PROFILE
 - CL_DEVICE_VERSION: OpenCL 1.2 CUDA
 - CL_DEVICE_TYPE: GPU
 - CL_DEVICE_VENDOR_ID: 0x10DE
 - CL_DEVICE_MAX_COMPUTE_UNITS: 2
 - CL_DEVICE_MAX_CLOCK_FREQUENCY: 901MHz
 - CL_NV_DEVICE_COMPUTE_CAPABILITY_MAJOR: 3
 - CL_NV_DEVICE_COMPUTE_CAPABILITY_MINOR: 5
 - CL_NV_DEVICE_REGISTERS_PER_BLOCK: 65536
 - CL_NV_DEVICE_WARP_SIZE: 32
 - CL_NV_DEVICE_GPU_OVERLAP: 1
 - CL_NV_DEVICE_KERNEL_EXEC_TIMEOUT: 1
 - CL_NV_DEVICE_INTEGRATED_MEMORY: 0
 - CL_DEVICE_ADDRESS_BITS: 32
 - CL_DEVICE_MAX_MEM_ALLOC_SIZE: 262144KB
 - CL_DEVICE_GLOBAL_MEM_SIZE: 1024MB
 - CL_DEVICE_MAX_PARAMETER_SIZE: 4352
 - CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: 128 Bytes
 - CL_DEVICE_GLOBAL_MEM_CACHE_SIZE: 32KB
 - CL_DEVICE_ERROR_CORRECTION_SUPPORT: NO
 - CL_DEVICE_LOCAL_MEM_TYPE: Local (scratchpad)
 - CL_DEVICE_LOCAL_MEM_SIZE: 48KB
 - CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE: 64KB
 - CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: 3
 - CL_DEVICE_MAX_WORK_ITEM_SIZES: [1024 ; 1024 ; 64]
 - CL_DEVICE_MAX_WORK_GROUP_SIZE: 1024
 - CL_EXEC_NATIVE_KERNEL: 4377024
 - CL_DEVICE_IMAGE_SUPPORT: YES
 - CL_DEVICE_MAX_READ_IMAGE_ARGS: 256
 - CL_DEVICE_MAX_WRITE_IMAGE_ARGS: 16
 - CL_DEVICE_IMAGE2D_MAX_WIDTH: 16384
 - CL_DEVICE_IMAGE2D_MAX_HEIGHT: 16384
 - CL_DEVICE_IMAGE3D_MAX_WIDTH: 4096
 - CL_DEVICE_IMAGE3D_MAX_HEIGHT: 4096
 - CL_DEVICE_IMAGE3D_MAX_DEPTH: 4096
 - CL_DEVICE_MAX_SAMPLERS: 32
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR: 1
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT: 1
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT: 1
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG: 1
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT: 1
 - CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE: 1
 - CL_DEVICE_EXTENSIONS: 16
 - Extensions:
  - cl_khr_byte_addressable_store
  - cl_khr_icd
  - cl_khr_gl_sharing
  - cl_nv_compiler_options
  - cl_nv_device_attribute_query
  - cl_nv_pragma_unroll
  - cl_nv_d3d9_sharing
  - cl_nv_d3d10_sharing
  - cl_khr_d3d10_sharing
  - cl_nv_d3d11_sharing
  - cl_nv_copy_opts
  - cl_khr_global_int32_base_atomics
  - cl_khr_global_int32_extended_atomics
  - cl_khr_local_int32_base_atomics
  - cl_khr_local_int32_extended_atomics
  - cl_khr_fp64