Hi Gambit enthusiasts,
I'm curious if anyone has played with generating CUDA kernels or CUDA from Gambit scheme code.
I've learned some Lisp and some Scheme, but I'm still unclear how the "C embedding" in Gambit works.
For example, if one needs to call nVidia's nvcc compiler to handle their dialect of C (CUDA-C == C with GPU extensions) instead of gcc (for example), will the Gambit compiler pass through the embedded C-with-extensions to the C compiler of choice? For that matter, the Thrust libraries bring an STL-like interface to creating and using CUDA kernels; is it possible to embed C++ code with templates directly into Gambit Scheme code?
Happy to clarify if need be. Any advice or ideas (or experience) would be helpful.
Thank you.
Best regards,
Jason
Afficher les réponses par date
On 2011-02-13, at 10:35 AM, Jason E. Aten wrote:
Hi Gambit enthusiasts,
I'm curious if anyone has played with generating CUDA kernels or CUDA from Gambit scheme code.
I've learned some Lisp and some Scheme, but I'm still unclear how the "C embedding" in Gambit works.
I haven't tried to interface CUDA code with Gambit, but I don't see much of a problem. If you can do it from C (or C++) then you can do it from Scheme compiled with Gambit-C. The first thing to do is to figure out how you would do it in plain C/C++. Then you put that code in a C function, and you write in Scheme an interface to that C function. For example, if your function is called foo and it takes two "int" parameters and returns a "double", you would write:
(c-declare #<<EOF
double foo(int x, int y) { return x/(1.0+y*y); }
EOF )
(define foo (c-lambda (int int) double "foo"))
(print (foo 11 22)) ;; test it...
Note that you could have put the definition of foo in a .c file that is compiled separately. In that case you will still need a prototype for the foo function. If the prototype is in the header file foo.h, the you could write:
(c-declare "#include "foo.h"")
(define foo (c-lambda (int int) double "foo"))
The last hurdle is to link your Scheme program with the C code. If you are generating an executable program, and prog.scm is the file with your Scheme code and the c-lambda form, and -lCUDA is required to link with the CUDA libraries, then this should do:
% gsc -exe -ld-options "-lCUDA" prog.scm foo.c % ./prog
or
% gcc -c foo.c % gsc -exe -ld-options "-lCUDA" prog.scm foo.o % ./prog
If you are generating a loadable object file, then use this instead (lib.scm is the file with your Scheme code, and the c-lambda form):
% gcc -c foo.c % gsc -ld-options "-lCUDA foo.o" lib.scm
For example, if one needs to call nVidia's nvcc compiler to handle their dialect of C (CUDA-C == C with GPU extensions) instead of gcc (for example), will the Gambit compiler pass through the embedded C-with-extensions to the C compiler of choice?
It might be simpler to compile Gambit with the nvcc compiler. That way, nvcc will be used for compiling all the code and you will avoid possible linking problems. That can be done with:
% ./configure CC=nvcc
For that matter, the Thrust libraries bring an STL-like interface to creating and using CUDA kernels; is it possible to embed C++ code with templates directly into Gambit Scheme code?
The code generated by the Gambit compiler is compatible with C and C++. For example, if you want to compile Gambit with the g++ compiler, just do:
% ./configure --enable-cplusplus
or
% ./configure CC=g++
I'm not sure about the templates though... what use of templates are you considering?
Marc
I think a problem may be the need to use nvcc to compile the CUDA code first.
On Sun, Feb 13, 2011 at 2:13 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
On 2011-02-13, at 10:35 AM, Jason E. Aten wrote:
Hi Gambit enthusiasts,
I'm curious if anyone has played with generating CUDA kernels or CUDA from Gambit scheme code.
I've learned some Lisp and some Scheme, but I'm still unclear how the "C embedding" in Gambit works.
I haven't tried to interface CUDA code with Gambit, but I don't see much of a problem. If you can do it from C (or C++) then you can do it from Scheme compiled with Gambit-C. The first thing to do is to figure out how you would do it in plain C/C++. Then you put that code in a C function, and you write in Scheme an interface to that C function. For example, if your function is called foo and it takes two "int" parameters and returns a "double", you would write:
(c-declare #<<EOF
double foo(int x, int y) { return x/(1.0+y*y); }
EOF )
(define foo (c-lambda (int int) double "foo"))
(print (foo 11 22)) ;; test it...
Note that you could have put the definition of foo in a .c file that is compiled separately. In that case you will still need a prototype for the foo function. If the prototype is in the header file foo.h, the you could write:
(c-declare "#include "foo.h"")
(define foo (c-lambda (int int) double "foo"))
The last hurdle is to link your Scheme program with the C code. If you are generating an executable program, and prog.scm is the file with your Scheme code and the c-lambda form, and -lCUDA is required to link with the CUDA libraries, then this should do:
% gsc -exe -ld-options "-lCUDA" prog.scm foo.c % ./prog
or
% gcc -c foo.c % gsc -exe -ld-options "-lCUDA" prog.scm foo.o % ./prog
If you are generating a loadable object file, then use this instead (lib.scm is the file with your Scheme code, and the c-lambda form):
% gcc -c foo.c % gsc -ld-options "-lCUDA foo.o" lib.scm
For example, if one needs to call nVidia's nvcc compiler to handle their dialect of C (CUDA-C == C with GPU extensions) instead of gcc (for example), will the Gambit compiler pass through the embedded C-with-extensions to the C compiler of choice?
It might be simpler to compile Gambit with the nvcc compiler. That way, nvcc will be used for compiling all the code and you will avoid possible linking problems. That can be done with:
% ./configure CC=nvcc
For that matter, the Thrust libraries bring an STL-like interface to creating and using CUDA kernels; is it possible to embed C++ code with templates directly into Gambit Scheme code?
The code generated by the Gambit compiler is compatible with C and C++. For example, if you want to compile Gambit with the g++ compiler, just do:
% ./configure --enable-cplusplus
or
% ./configure CC=g++
I'm not sure about the templates though... what use of templates are you considering?
Marc
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
On 2011-02-13, at 2:20 PM, David Dreisigmeyer wrote:
I think a problem may be the need to use nvcc to compile the CUDA code first.
Perhaps I don't understand how nvcc and CUDA work. Is it the case that all source code is compiled by nvcc and then linked to create an executable, or is there some other preprocessor/compiler/linker/loader that is involved?
Marc
I misspoke a bit. I had try using OpenCL with Chicken. What's easy to do (obviously) is to write a kernel and C code and call that. What I couldn't figure out how to do is make a kernel and call that directly nor wrap up the individual OpenCL functions. Someone with more programming expertise might be able to do this. I had also looked into using CUDA for the latter two things, but never really got started on it.
On Sun, Feb 13, 2011 at 4:15 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
On 2011-02-13, at 2:20 PM, David Dreisigmeyer wrote:
I think a problem may be the need to use nvcc to compile the CUDA code first.
Perhaps I don't understand how nvcc and CUDA work. Is it the case that all source code is compiled by nvcc and then linked to create an executable, or is there some other preprocessor/compiler/linker/loader that is involved?
Marc
I'll try to answer, but feel free to ask for clarification if I don't illuminate the topic.
nvcc is really just a coordinator for many calls. nvcc makes a series of calls, producing intermediate code (PTX files), that can then be just-in-time compiled into final (.cubin) architecture specific code by the nvida libraries. In a sense, yes, it compiles source code, but then breaks out kernel code to be targeted to the GPU device architecture(s). Later in the compilation sequence they all get merged into one file.
I attach an example of a C++ file that includes at the top in the comments the 15 commands issued by the nvcc nVidia compiler coordinator. The final (15th command) is the link stage, producing a final executable that depends upon several shared libraries. The matrixMul.cu example from the SDK is consolidated into one file, matrixMul.cu.txt, and the commands used to assemble the final binary broken out at the top of the file.
Thanks again.
Best regards, Jason
A quick update and a follow up question. I was able to successfully call into the CUDA code from compiled scheme. (Horray!)
But now I don't seem to be able to load the object file from the interpreter, even when I start it as gsc.
I even tried renaming my shared object (build with -fPIC) to end in .o1. But still no luck using (load "myobject.o") or (load "myobject.o1").
Is there a special call to load a shared library into the interpreter? Alternatively, is there a way to invoke an interpreter REPL from within a compiled executable?
Thanks.
Jason
## demonstration of problem loading .o file (full compilation steps and source attached for the curious)
jaten@afarm:~/NVIDIA_GPU_Computing_SDK/C/src/matrixMul$ gsc Gambit v4.6.0
(load "obj/x86_64/release/matrixMul.cu")
*** ERROR IN (console)@1.1 -- No such file or directory
(load "obj/x86_64/release/matrixMul.cu") 1> (load "obj/x86_64/release/matrixMul.cu.o1") *** WARNING -- Could not find C function: "____20_matrixMul_2e_cu_2e_o1" *** ERROR IN (console)@2.1 -- /home/jaten/NVIDIA_GPU_Computing_SDK/C/src/matrixMul/obj/x86_64/release/matrixMul.cu.o1: only ET_DYN and ET_EXEC can be loaded (load "obj/x86_64/release/matrixMul.cu.o1") 2> 1>
(load "obj/x86_64/release/matrixMul.cu.o")
*** ERROR IN "obj/x86_64/release/matrixMul.cu.o"@1.5 -- Illegal character: #\x02 1>
(load "obj/x86_64/release/matrixMul.cu")
*** ERROR IN (console)@4.1 -- No such file or directory (load "obj/x86_64/release/matrixMul.cu") 1>
(load "prog.scm")
*** ERROR IN "prog.scm"@2.1 -- Interpreter does not support ##c-declare 1>
*** EOF again to exit
jaten@afarm:~/NVIDIA_GPU_Computing_SDK/C/src/matrixMul$ cat prog.scm
(c-declare "extern double foo(int a, int b);") (define foo (c-lambda (int int) double "foo")) (println (foo 11 22)) ;; test it...
jaten@afarm:~/NVIDIA_GPU_Computing_SDK/C/src/matrixMul$ ./prog Device 0: "GeForce GTX 460" with Compute 2.1 capability Error when parsing command line argument string.
Using Matrix Sizes: A(80 x 160), B(80 x 80), C(80 x 160)
Run Kernels...
matrixMul, Throughput = 79.8960 GFlop/s, Time = 0.00003 s, Size = 2048000 Ops, NumDevsUsed = 1, Workgroup = 256
Check against Host computation...
PASSED
foo() called: Test code integrating Gambit Scheme with nVIDIA CUDA-C code complete! Answer to foo(11,22) = .02268041237113402 jaten@afarm:~/NVIDIA_GPU_Computing_SDK/C/src/matrixMul$
On Sun, Feb 13, 2011 at 6:47 PM, Jason E. Aten j.e.aten@gmail.com wrote:
I'll try to answer, but feel free to ask for clarification if I don't illuminate the topic.
nvcc is really just a coordinator for many calls. nvcc makes a series of calls, producing intermediate code (PTX files), that can then be just-in-time compiled into final (.cubin) architecture specific code by the nvida libraries. In a sense, yes, it compiles source code, but then breaks out kernel code to be targeted to the GPU device architecture(s). Later in the compilation sequence they all get merged into one file.
I attach an example of a C++ file that includes at the top in the comments the 15 commands issued by the nvcc nVidia compiler coordinator. The final (15th command) is the link stage, producing a final executable that depends upon several shared libraries. The matrixMul.cu example from the SDK is consolidated into one file, matrixMul.cu.txt, and the commands used to assemble the final binary broken out at the top of the file.
Thanks again.
Best regards, Jason
-- Jason E. Aten, Ph.D.
Perhaps I don't understand how nvcc and CUDA work. Is it the case that all source code is compiled by nvcc and then linked to create an executable, or is there some other preprocessor/compiler/linker/loader that is involved?
Marc
On 2011-02-13, at 9:46 PM, Jason E. Aten wrote:
A quick update and a follow up question. I was able to successfully call into the CUDA code from compiled scheme. (Horray!)
Great!
But now I don't seem to be able to load the object file from the interpreter, even when I start it as gsc.
I even tried renaming my shared object (build with -fPIC) to end in .o1. But still no luck using (load "myobject.o") or (load "myobject.o1").
You cannot rename the .oN files (which are shared object files). The reason is that when you do (load "X.o1") Gambit will call dlopen to dynamically link with X.o1, and then the initialization function "____20_X_2e_o1" (which has been generated by the Gambit-C compiler) will be searched for in that shared object. If you rename foo.o3 to X.o1 the correct initialization function "____20_foo_2e_o3" will not be found because load is looking for "____20_X_2e_o1". The approach of naming the initialization function by mangling the file name is necessary for some operating systems that don't allow, or get confused, when executing dlopen of files containing the same function name.
If you want to give the file a specific name, then use the -o option when creating the link file or dynamically loadable object file. This message may be of help:
https://mercure.iro.umontreal.ca/pipermail/gambit-list/2004-October/000014.h...
Is there a special call to load a shared library into the interpreter?
No. However, you could use the FFI to expose the dlopen function.
Alternatively, is there a way to invoke an interpreter REPL from within a compiled executable?
You can start a repl from a program at any time by calling (##repl-debug) .
Marc