Arkanis Development

Styles

Minimal OpenCL development on Windows

Published

I've been doing some Windows OpenCL stuff recently. One of the things that kind of annoyed me a lot was all the time spend to set things up. Installing Visual Studio (which takes quite some time), finding the proper SDK and praying that everything kind of works. Not realy my kind of fun. And all I wanted to do was to create a program that uses the systems opencl.dll.

So how hard can that be? What do I really need for that? Common sense linking would suggest you only need the header files. With that the compiler knows how to generate code to call the DLL functions. So I gave it a try and it actually worked! (Which honestly surprises me most of the time.)

All that remains is to add the OpenCL headers to the compilers include path, compile an OpenCL program and link it against the systems opencl.dll:

gcc -I. main.c C:\Windows\System32\OpenCL.dll -o main.exe

I've put the OpenCL header files into an cl subdirectory. Then added the current directory to the include path. So the compiler will find #include <cl/cl.h>.

And that's it! MinGW-w64, two header files and a recent graphics driver is all you need. :)

I've tested this with OpenCL 1.1 on nVidia (one notebook) and AMD drivers (two different computers). There's so much interesting stuff in OpenCL 1.1. alone (e.g. combining out-of-order GPU and CPU command queues) that I haven't tested newer versions yet.

If you're curious you can find the minimal setup on GitHub. It's a small OpenCL application that takes the first GPU and transforms a hand full of characters on the GPU. Total waste of any processing time but enough to see if it works.

12 comments for this post

leave a new one

#1 by
Joe Dude

Thanks for the post. I was on track to begin the massive amount of learning it would take to use OpenGL compute shaders to do GPGPU computing. That definitely wasn't optimal

I was under the impression that one of Intel, AMD, or NVIDIA's OpenCL SDK was needed. That actually means all 3 are needed if I want to set things up to be as portable as possible for other people.

Finally, I got OpenCL to compile with MinGW-w64 without an SDK, per your instructions. What problems lie ahead, I don't know. Thanks again.

#2 by
Anonymus

was hoping for IDE style compiler instructions :(

#3 by
Stephan

It's meant to be the exact opposite. ;) Just the bare minimum needed. You could tell your IDE to pass those compiler flags to your built tools and it would do the job.

The IDE focused articles for OpenCL actually annoyed me quite a bit because I got the impression that using OpenCL itself is way more complicated than it actually is. Especially considering deployment which really is nothing more than just linking against a DLL.

#4 by
pratikone

Thanks for the solution. I realised most of errors people are getting, are trying to run OpenCL is by including -lOpenCL in their gcc compilation, instead of directly linking OpenCL.dll as you did here.

#5 by
Sony

Hi! Thanks! Do you know how to run an OpenCL program via command line? If I just use main.exe then only the c code is ran.

#6 by
Stephan

Well, I'm not really sure what you mean by running an OpenCL program via the command line. Usually OpenCL programs are just little code snippets that describe one specific processing step done on the GPU (e.g. calculating a sum of an array).

A useful computer program has to combine many such OpenCL programs in a command queue to solve real problems. And that's what the C code does: It tells the computer how all those OpenCL programs have to be glued together to actually work. It also allocates and manages the resources (memory, images, etc.) needed by the OpenCL programs.

So at least as far as I'm aware of it it doesn't make sense to run an OpenCL program by itself. It can't do anything by itself since it describes only a small processing step (and then even without the used resources) not a whole program.

In the minimal example the OpenCL program (or kernel) is embedded into the normal C program as a string: https://github.com/arkanis/minimal-opencl-on-windows/blob/master/main.c#L40 Usually people use extra files for that.

Does that answer your question in any way?

#7 by
Anonymus

This was super helpful, thanks! I was building xmrig-amd for a few desktops, but since I was doing it on my laptop (which has an nVidia GPU), I had to temporarily replace my system's nVidia OpenCL.dll (in system32) with the version from the AMD desktop I was building for to avoid "undefined reference to `clCreateCommandQueueWithProperties'" when linking. After that, your instructions worked perfectly! Thanks again!

#8 by
Stephan

clCreateCommandQueueWithProperties() is an OpenCL 2.0 function and nVidia only supports OpenCL 1.1. That's why nVidias OpenCL.dll doesn't define those symbols. I'm actually quite surprised that you can just take AMDs DLL and use it on a system with nVidia hardware. Thanks for posting that!

#9 by
Zee

Thanks for the very clean instructions!

If I may add a few extra steps, you can create a library from the DLL to avoid static linking.

gendef "c:\Windows\System32\OpenCL.dll"
dlltool --as-flags=--64 -m i386:x86-64 -k -l libOpenCL.a -d OpenCL.def
move libOpenCL.a c:\MinGW\lib\

Then you would compile your code using

gcc -o main.exe main.c -lOpenCL

The most up-to-date gcc I could find for Windows is Nuwen's gcc (8.1 as the time of this writing https://nuwen.net/mingw.html ), but although dlltool.exe is available in the zip provided, gendef.exe isn't. I think it's available in msys2, but their gcc lags a bit behind Nuwen's, so to compile it old school in the spirit of this tutorial, head over to:

https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/msys2-contrib/mingw-w64/tree/master/mingw-w64-tools/gendef/src

This will download just the folder you need. Then compile gendef with

gcc -o gendef.exe gendef.c gendef_def.c fsredir.c
move gendef.exe c:\MinGW\bin

Also to get the latest CL headers from the Khronos Group github's repository:

https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/KhronosGroup/OpenCL-Headers/tree/master/CL

You can then copy them to where gcc can find them, in my case c:\MinGW\include

Then finally, if you clone https://github.com/arkanis/minimal-opencl-on-windows don't forget to delete the CL folder there as you already have the latest installed from the Khronos Group.

I've tested all this on my Lenovo T460 laptop with an Intel HD Graphics 520 GPU and the example code produces the right output. Whether main.exe actually makes use of the GPU, I'll leave that one to the experts to answer :)

#10 by
Stephan

Hi Zee,

thanks for the information! I really like the gendef.c part. Now I know where to get that tool when it's not part of MinGW. Also I didn't know Nuwen's gcc distribution. It contains make by default so that's a bonus for me. :)

What exactly do you mean by "avoid static linking"? It has been a few years since I've done a C project on Windows but back then MinGW did all that automatically. When you "directly" link against a DLL it uses gendef (and probably dlltool) to generate any needed glue-code on the fly. You still dynamically link against that DLL and your binary will only contain calls into the DLL, not the code of the DLL itself.

I'm not sure where I read that back then but it is shortly mentioned here: http://mingw.org/wiki/CreateImportLibraries. The article is actually about creating the glue code library but says that you usually don't need to do that because it happens automatically.

On x86 32 Bit there were cases where gendef could not extract the needed information directly from a DLL. As far as I know this was about the messed up calling conventions of the WinAPI. But on x86 64 Bit there is just one calling convention and the problem shouldn't arise. But I'm not working in that area right now so I might miss something here.

Note that MSVC doesn't extract information out of DLLs (at least as far as I know). For that compiler you always have to do the steps you described albeit with different tools. You have to generate the glue code that loads and calls the DLL functions (the LIB thing). And you have to statically link that glue-code into your binary. That's why MSVC always pairs a DLL with a LIB. But again, I haven't worked with MSVC in quite some time.

#11 by
Zee

Hi Stephan,

Thank you very much for your explanations. I got this wrong and should've read the manual instead of making assumptions about how things work.

This section in https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/win32.html in particular describes direct linking to a dll

The cygwin/mingw ports of ld support the direct linking, including data symbols, to a dll without the usage of any import libraries. This is much faster and uses much less memory than does the traditional import library method, expecially when linking large libraries or applications. When ld creates an import lib, each function or variable exported from the dll is stored in its own bfd, even though a single bfd could contain many exports. The overhead involved in storing, loading, and processing so many bfd's is quite large, and explains the tremendous time, memory, and storage needed to link against particularly large or complex libraries when using import libs.

Linking directly to a dll uses no extra command-line switches other than -L and -l, because ld already searches for a number of names to match each library. All that is needed from the developer's perspective is an understanding of this search, in order to force ld to select the dll instead of an import library.

And actually, looking at file sizes:

gcc -o main.exe main.c -lOpenCL
>>> 93,691 bytes

gcc -o main.exe main.c c:\Windows\System32\OpenCL.DLL
>>> 92,679 bytes

So your method, on top of being simpler to use, also generates a smaller executable. And checking both executables with dependency walker shows that OpenCL.dll is indeed dynamically linked in both cases.

Sorry!

#12 by
Stephan

Hi Zee,

No problem, I'm still learning about that stuff myself and it's hard to find good information. Glad the the post and comment was useful. :)

The linked documentation is pretty interesting. I didn't know that ld has such a good documentation. Especially the BFD library and linker script stuff got me hooked. I always looked at the man page and it doesn't explain that stuff. Thanks for the link!

Happy programming Stephan

Leave a new comment

Having thoughts on your mind about this stuff here? Want to tell me and the rest of the world your opinion? Write and post it right here. Be sure to check out the format help (focus the large text field) and give the preview button a try.

optional

Format help

Please us the following stuff to spice up your comment.

An empty line starts a new paragraph. ---- print "---- lines start/end code" ---- * List items start with a * or -

Just to keep your skill sharp and my comments clean.

or