Everything starts from the will of running a super awesome C library from within Torch. So, let's start step by step, with the writing of the C source code.
So, for this super mega project we would like to fill an int
array of length size
with the index of each of its elements starting from 1
.
void dummy(int* ptrFromLua, int size) {
for (int i = 0; i < size; i++)
ptrFromLua[i] = i + 1;
return;
}
What this code does is simply defining a routine (or void
function) called dummy
, which expects a pointer to a int
array of size
elements. Once it is called, it iterates over all the array by filling its elements with their C index + 1
. Then it return
a void
.
The source code is available at src/asdf.c
.
Now we need to build our shared library from the C source file. As for convention, the library name will be lib
+ name + .so
, so in this case we'll end up with a file called libasdf.so
.
For compiling the source code shown above into a shared library we need to use gcc with several arguments.
In order to simplify this procedure, a src/Makefile
can be created instead, where all this information can be directly executed by typing the command make
from within the src
directory.
For the more curious of the readers, I'm going to briefly explain how the src/Makefile
has been written.
LIBOPTS = -shared
CFLAGS = -fPIC -std=gnu99
CC = gcc
libasdf.so : asdf.c
$(CC) $< $(LIBOPTS) $(CFLAGS) -o $@
clean :
rm *.so
CC
specifies the compiler that is going to be used; -shared
means we are going to create a shared library from a -fPIC
Position Independent Code (there is no main()
function) which has a variable definition within a for
definition, and therefore, requires -std=gnu99
.
Hence, typing make
will build an -o
output libasdf.so
from the source code asdf.c
.
If we would like to remove every file generated by make, we can simply issue make clean
and it will do the trick.
And now we are almost at the end! From Lua we need to expose the pointer of a IntTensor
of dimension length
and send both pointer and size to the C function that is ready to be used in our shared library libasdf.so
.
-- FFI stuff -------------------------------------------------------------------
-- Require ffi
ffi = require("ffi")
-- Load myLib
myLib = ffi.load(paths.cwd() .. '/libasdf.so')
-- Function prototypes definition
ffi.cdef [[
void dummy(int* ptr_form_lua, int size)
]]
-- Main program ----------------------------------------------------------------
length = 5
a = torch.IntTensor(length):random()
myLib.dummy(torch.data(a), length)
Some comments about the "FFI stuff". We need first to require
the ffi
Lua package, then we can ffi.load
the shared library we have built with make and lastly we need to tell LuaJIT what is the prototype of our routine.
In the "Main program", we have simply to create a random IntTensor
, expose its C pointer with torch.data()
and send it, along with its length
to the C dummy()
routine which is now available as a Lua function in the myLib
table.
Why is this stuff cool? Run the source code src/run.lua
and you'll understand it. Bare in mind that you have to actually read the instructions at the begining of the code and experiment with different combination of options by commenting some lines of code.
For the more lazy ones, I'll write here some conclusions myself.
By setting length
to 1e7
, i.e. 10 millions, and disabling the print()
of the array on screen, I get the following output:
C function computation time 31.98 ms
Lua loop computation time 2890.92 ms
Which means, C completed the task in 1/30
of a second, whereas Lua takes almost 3
seconds for finishing its for
loop. We can make Lua perform better, and we'll do it in the next tutorial session.
Another reason about why you are loving this tutorial is that now you can use whatever compiled library — like some cool stuff from OpenCV, FFmpeg or even from your own shared library you just compiled with a Makefile
— and interface it with Torch. This means your horizons have never been so wide! Go, and start interfacing a new library!