2013년 9월 5일 목요일

매스매티카 9.0.1에서 NVIDIA 그래픽카드 설정하기

CUDA 지원


우분투 13.04에 NVIDIA의 그래픽칩을 사용하는 그래픽카드가 설치되어 있고, NVIDIA의 독점 그래픽 드라이버가 설치 되어 있다고 가정한다. 매스매티카의 도움말을 따르면 이 단계에서 다음의 테스트를 통과해야 한다.
In[1]:= << CUDALink`
In[2]:= CUDAQ[]
Out[2]= False
하지만 실패! 인터넷을 뒤져보니, 이건 매스매티카가 CUDA 라이브러리를 찾지 못해서 발생하는 문제로 환경 변수 설정이 필요한 걸 알 수 있다. 내 경우엔 NVIDIA 그래픽 드라이버 버전이 319.37이므로 이에 해당하는 라이브러리를 찾아 환경변수에 등록해준다.
--- /etc/profile.d/Mathematica.sh.orig 2013-10-09 00:19:53.671638435 +0900
+++ /etc/profile.d/Mathematica.sh 2013-10-09 00:20:15.543638995 +0900
@@ -1,2 +1,4 @@
 #!/bin/sh
 export MATHEMATICA_HOME=/opt/Wolfram/Mathematica/9.0
+export NVIDIA_DRIVER_LIBRARY_PATH=/usr/lib/nvidia-current/libnvidia-tls.so.319.37
+export CUDA_LIBRARY_PATH=/usr/lib/nvidia-current/libcuda.so
이제 다시 로그인 한 후에 매스매티카를 실행해보면 위의 테스트를 깔끔히 통과한다.

OpenCL 지원


자신의 NVIDIA 칩이 OpenCL을 지원한다면, 매스매티카의 OpenCL 인터페이스도 사용할 수 있다. 지원 여부는 위키피디아에서 확인할 수 있다.
In[1]:= << OpenCLLink`
In[2]:= OpenCLQ[]
Out[2]= False
역시 실패! 여기에 따르면, 매스매티카가 엉뚱한 위치에서 OpenCL 라이브러리 찾기 때문이다. 더욱이 이 위치는 고정되어 바꿀 수 없다. 따라서 매스매티카가 찾는 위치에 맞추어서 링크를 만들어줘야 한다.
$ sudo mkdir -p /usr/lib64
$ sudo ln -s /usr/lib/nvidia-current/libOpenCL.so /usr/lib64/
이제 매스매티카 커널을 재시작하고 위 테스트를 실행해보면, 문제없이 통과!

CUDA 개발환경(Toolkit) 지원 컴파일러


매스매티카에서 제공하는 내장 CUDA 함수는 잘 동작한다. 하지만 외부 CUDA 함수를 매스매티카로 불러오면 gcc 4.7 이상은 지원하지 않는다는 에러가 발생한다.
In[2]:= code = "__global__ void addTwo(mint * in, mint * out, mint length) {
int index = threadIdx.x + blockIdx.x*blockDim.x;
if (index < length)          out[index] = in[index] + 2;  }";


In[3]:= cudaFun = CUDAFunctionLoad[code, "addTwo", {{_Integer, _, "Input"}, {_Integer, _,     "Output"}, _Integer}, 256]
During evaluation of In[3]:= CUDAFunctionLoad::cmperr: -- Message text not found -- (/home/kwchun/.Mathematica/Paclets/Repository/CUDAResources-Lin64-9.0.0.0/CUDAToolkit/bin/../include/host_config.h:82:2: error: #error -- unsupported GNU version! gcc 4.7 and up are not supported!) >>


Out[3]= CUDAFunctionLoad["__global__ void addTwo(mint * in, mint * out, mint length) {int index = threadIdx.x + blockIdx.x*blockDim.x;
if (index < length) out[index] = in[index] + 2;}", "addTwo", {{_Integer, _, "Input"}, {_Integer, _,    "Output"}, _Integer}, 256]
에러에 나와 있듯이 2013년 9월 5일 현재 매스매티카의 CUDA 개발환경(Toolkit) 버전은 9.0.0.0이고, 이 버전은 gcc 4.7 이상을 지원하지 않는다. 이 문제는 "XCompilerInstallation" 옵션으로 CUDA 개발환경이 지원하는 C 컴파일러의 경로를 지정하면 해결된다.
In[4]:= cudaFun = CUDAFunctionLoad[code, "addTwo", {{_Integer, _, "Input"}, {_Integer, _, "Output"}, _Integer}, 256, "XCompilerInstallation" -> "/usr/bin/gcc-4.6"]
Out[4]= CUDAFunction["<>", "addTwo", {{_Integer, _,    "Input"}, {_Integer, _, "Output"}, "Integer64"}]

OpenCL에서 배정도 부동소수점 사용


(아마도) 64비트 환경에서 OpenCL 외장 함수 내에 배정도 부동소수점을 사용하면 아래와 같이 함수를 불러드릴 수 없다.

code = "
  __kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
        int index =  get_global_id(0);
        if (index < length)
            out[index] = in[index] + 2;
    }";
openclFun = 
 OpenCLFunctionLoad[code, 
  "addTwo", {{_Real, _, "Input"}, {_Real, _, "Output"}, _Real}, 256, 
  "ShellOutputFunction" -> Print ]

OpenCLFunctionLoad::cmpf: The kernel compilation failed. Consider setting the option "ShellOutputFunction"->Print to display the compiler error message. >>

:2:31: error: must specify '#pragma OPENCL EXTENSION cl_khr_fp64: enable' before using 'double'
__kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
                              ^
In file included from <built-in>:17306:
<command line>:4:16: note: instantiated from:
#define Real_t double
               ^

이건 에러 메시지에도 나와 있듯이, OpenCL 관련 매크로를 코드의 앞부분에 넣어주면 해결된다.
code = "
  #ifdef USING_DOUBLE_PRECISIONQ
  #ifdef OPENCLLINK_USING_NVIDIA
  #pragma OPENCL EXTENSION cl_khr_fp64: enable
  #else /* OPENCLLINK_USING_NVIDIA */
  #pragma OPENCL EXTENSION cl_amd_fp64: enable
  #endif /* OPENCLLINK_USING_NVIDIA */
  #endif /* USING_DOUBLE_PRECISIONQ */

  __kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
    int index =  get_global_id(0);
    if (index < length)
      out[index] = in[index] + 2;
}";