以下是科研实习第一周的进度

整体思路

识别系统信息

不同的系统给我们提供了一系列API供我们调用来检测系统,在CPP中有以下宏:

_WIN32 //判断是否是windows
__linux__ //判断是否是linux
__APPLE__ //判断是否是MacOS
__unix__ //判断是否是类unix系统,linux和MacOS的此值亦为1

因此我们可以使用下述方式来判断系统:

  #ifdef __linux__

  #elif _WIN32

  #elif __APPLE__

  #elif __unix__

  #endif

检测系统硬件

不同的系统对于系统硬件的检测方式各不相同,下面仅描述Linux和Windows的检测

Linux

Linux 系统自带一系列工具,我们针对的硬件主要为Nvidia GPU,AMD GPU 和 CPU,因此我们主要要成功检测是否存在GPU设备,如果有我们就将运算进行 在GPU上,反之运行在CPU上。

C/C++/Fortran并没有提供一套函数来进行设备检测,因此这一部分需要我们自己来写。

由于在Linux上,安装了Nvidia驱动/AMD驱动都会提供一个CLI来提供显卡信息,我们可以通过检测是否存在驱动来判断是什么设备。

我们可以使用以下函数来检测一个CLI是否存在

bool commandExists(const std::string& command) {
    std::string cmd = "which " + command + " > /dev/null 2>&1";
    return system(cmd.c_str()) == 0;
}

因此我们可以使用:

commandExists("nvidia-smi")
commandExists("rocm-smi")

从而识别电脑的硬件。

Windows

Windows上就比较复杂,我们需要使用DXGI来识别显卡

  IDXGIFactory* pFactory = nullptr;
  IDXGIAdapter* pAdapter = nullptr;
  std::string result = "cpu";

  HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory);
  if (FAILED(hr))
    return result;
  for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i){
    DXGI_ADAPTER_DESC desc;
    pAdapter->GetDesc(&desc);

    if (desc.VendorId == 0x1002) {
      result = "amd";
      break;
    }
    else if (desc.VendorId == 0x10DE){
      result = "nvidia";
      break;
    }

    pAdapter->Release();
  }

  if (pAdapter) pAdapter->Release();
  if (pFactory) pFactory->Release();
  return result;

我们把他们放在同一个文件里,有:

#include <iostream>
#include <cstdlib>

#ifdef _WIN32
#include <windows.h>
#include <dxgi.h>
#pragma comment(lib, "dxgi.lib")
#endif

bool commandExists(const std::string& command) {
    std::string cmd = "which " + command + " > /dev/null 2>&1";
    return system(cmd.c_str()) == 0;
}


std::string judgeDevice(){
  #ifdef __linux__
  if(commandExists("nvidia-smi")){
    return "nvidia";
  }else if(commandExists("rocm-smi")){
    return "amd";
  }else {
    return "cpu";
  }
  #elif _WIN32
  IDXGIFactory* pFactory = nullptr;
  IDXGIAdapter* pAdapter = nullptr;
  std::string result = "cpu";

  HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pFactory);
  if (FAILED(hr))
    return result;
  for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i){
    DXGI_ADAPTER_DESC desc;
    pAdapter->GetDesc(&desc);
    if (desc.VendorId == 0x1002) {
      result = "amd";
      break;
    }
    else if (desc.VendorId == 0x10DE){
      result = "nvidia";
      break;
    }
    pAdapter->Release();
  }
  if (pAdapter) pAdapter->Release();
  if (pFactory) pFactory->Release();
  return result;
  #elif __APPLE__

  #elif __unix__

  #endif
}

识别最优硬件

一般来说我们认为GPU性能优于CPU,因此我们可以认为如果识别到GPU就使用GPU,否则使用CPU运行

但是我们需要注意一点,对于多GPU来说,如果检测到核显,我们需要屏蔽掉来使用独显运算。

一般来说,存在独显的电脑,核显的显存分配不会超过1GB,因此我们可以使用:

if (desc.DedicatedVideoMemory > 512 * 1024 * 1024)  // 大于512MB显存
{
    if (desc.VendorId == 0x1002)  // AMD
    {
        result = "amd";
        break;
    }
    else if (desc.VendorId == 0x10DE)  // NVIDIA
    {
        result = "nvidia";
        break;
    }
}

而在Linux上,ROCm会自动屏蔽核显,因此无须改动

将代码运行在合适的GPU上

不同的设备有不同的加速方案,我们需要做的就是根据不同的设备来运行不同的代码

Nvidia平台可以使用Cuda、OpenACC、DirectX来加速运算

AMD平台以及国产加速硬件可以使用ROCm、OpenACC、DirectX来加速运算

CPU上可以使用MPI、OpenMP来加速运算

目前平台正在搭建中