continue example: call cuda toolkit API from python, curand
This time I tried to use SWIG to generate the wrapper for common lisp.
1.create an interface file "curand.i"
%module curand
#define size_t unsigned long long
#define __host__
#define cudaError_t int
#define CUDARTAPI
#define __cudart_builtin__
enum cudaMemcpyKind {
cudaMemcpyHostToHost = 0,
cudaMemcpyHostToDevice = 1,
cudaMemcpyDeviceToHost = 2,
cudaMemcpyDeviceToDevice = 3,
cudaMemcpyDefault = 4
};
%include "/usr/local/cuda/include/curand.h"
%include "/usr/local/cuda/include/cuda_runtime_api.h"
Here we use some macro define, for these macro can be recognized by nvcc only, not SWIG.
2. use swig to generate the wrapper.
swig -cffi curand.i
Then there is a file named curand.lisp, it contains the lisp definitions for the C API in curand & cuda runtime.
3. Use common lisp to call curand, and generate some random numbers. file: cffi-curand.lisp
(require :asdf)
(require :cffi)
(defpackage :curand (:use :common-lisp :cffi))
(in-package :curand)
(define-foreign-library libcurand
(:unix (:or "/usr/local/cuda/lib64/libcurand.so"))
(t (:default "libcurand")))
(use-foreign-library libcurand)
(define-foreign-library libcudart
(:unix (:or "/usr/local/cuda/lib64/libcudart.so"))
(t (:default "libcudart")))
(use-foreign-library libcudart)
(load "curand.lisp")
(defmacro with-curand-generator (gen curand-rng-type &rest body)
(let ((pg (gensym)) (ret (gensym)))
`(with-foreign-object (,pg :unsigned-long-long)
(let ((,ret (curandcreategenerator ,pg ,curand-rng-type)))
(if (equal :CURAND_STATUS_SUCCESS ,ret)
(let ((,gen (make-pointer (mem-ref ,pg :unsigned-long-long))))
(unwind-protect (progn ,@body)
(curanddestroygenerator ,gen)))
(format t "curandcreategenerator failed: ~A" ,ret)
)))))
(defmacro with-cuda-buf (dev-buf host-buf bytes &rest body)
(let ((ptr (gensym)) (ret (gensym)))
`(with-foreign-objects ((,ptr :unsigned-long-long) (,host-buf :char ,bytes))
(let ((,ret (cudamalloc ,ptr ,bytes) ))
(if (equal ,ret 0)
(let ((,dev-buf (make-pointer (mem-ref ,ptr :unsigned-long-long))))
(unwind-protect (progn ,@body)
(cudafree dev-buf)))
(format t "cudamalloc failed: ~A" ,ret))))))
(with-foreign-object (version :int)
(format t "RET: ~A~%Ver: ~D~%" (curandgetversion version) (mem-ref version :int)))
(let* ((size 500) (bytes (* 4 size)))
(with-cuda-buf dev-buf host-buf bytes
(with-curand-generator gen :curand_rng_pseudo_default
(if (equal :CURAND_STATUS_SUCCESS (curandgenerateuniform gen dev-buf size))
(progn
(cudamemcpy host-buf dev-buf bytes :cudamemcpydevicetohost)
(format t "~{~A~^, ~}~%"
(loop for i from 0 below size
collect (mem-aref host-buf :float i)))
t)
(format t "generate failed")))))
4. Run it. (use sbcl)
sbcl --script cffi-curand.lisp
Then you can see some float number has been printed.
I have created a project on Github: