Skip to content

Commit 1685109

Browse files
committed
Merge pull request #1 from Atcold/FFI-so
FFI and .so library chapter
2 parents dbe517a + 203f4e2 commit 1685109

File tree

6 files changed

+171
-3
lines changed

6 files changed

+171
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Files
22
*.sw*
33
.DS_Store
4+
*.so

FFI-so/README.md

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# *FFI* and `.so` library
2+
3+
Everything starts from the will of running a super awesome *C* library from within *Torch*.
4+
So, let's start step by step, with the writing of the *C* source code.
5+
6+
## *C* source code
7+
8+
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`.
9+
10+
```c
11+
void dummy(int* ptrFromLua, int size) {
12+
13+
for (int i = 0; i < size; i++)
14+
ptrFromLua[i] = i + 1;
15+
16+
return;
17+
18+
}
19+
```
20+
21+
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`.
22+
The source code is available at [`src/asdf.c`](src/asdf.c).
23+
24+
## *make* and `.so` compilation
25+
26+
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`.
27+
28+
For compiling the source code shown above into a *shared library* we need to use *gcc* with several arguments.
29+
In order to simplify this procedure, a [`src/Makefile`](src/Makefile) can be created instead, where all this information can be directly executed by typing the command `make` from within the [`src`](src) directory.
30+
31+
### Compilation details
32+
33+
For the more curious of the readers, I'm going to briefly explain how the [`src/Makefile`](src/Makefile) has been written.
34+
35+
```makefile
36+
LIBOPTS = -shared
37+
CFLAGS = -fPIC -std=gnu99
38+
CC = gcc
39+
40+
libasdf.so : asdf.c
41+
$(CC) $< $(LIBOPTS) $(CFLAGS) -o $@
42+
43+
clean :
44+
rm *.so
45+
```
46+
47+
`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`.
48+
Hence, typing `make` will build an `-o` output `libasdf.so` from the source code `asdf.c`.
49+
If we would like to remove every file generated by *make*, we can simply issue `make clean` and it will do the trick.
50+
51+
## Scripting in *Lua*
52+
53+
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`.
54+
55+
```lua
56+
-- FFI stuff -------------------------------------------------------------------
57+
-- Require ffi
58+
ffi = require("ffi")
59+
-- Load myLib
60+
myLib = ffi.load(paths.cwd() .. '/libasdf.so')
61+
-- Function prototypes definition
62+
ffi.cdef [[
63+
void dummy(int* ptr_form_lua, int size)
64+
]]
65+
66+
-- Main program ----------------------------------------------------------------
67+
length = 5
68+
a = torch.IntTensor(length):random()
69+
myLib.dummy(torch.data(a), length)
70+
```
71+
72+
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*.
73+
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.
74+
75+
## Motivations
76+
77+
Why is this stuff cool? Run the source code [`src/run.lua`](scr/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.
78+
79+
For the more lazy ones, I'll write here some conclusions myself.
80+
By setting `length` to `1e7`, i.e. 10 millions, and disabling the `print()` of the array on screen, I get the following output:
81+
82+
```
83+
C function computation time 31.98 ms
84+
Lua loop computation time 2890.92 ms
85+
```
86+
87+
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.
88+
89+
Another reason about *why* you are loving this tutorial is that now you can use whatever compiled library — like some cool stuff from [*OpenCV*](http://opencv.org/), [*FFmpeg*](https://www.ffmpeg.org/) 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!

FFI-so/src/Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
################################################################################
2+
# Make file for building the shared library
3+
################################################################################
4+
# Alfredo Canziani, Nov 14
5+
################################################################################
6+
7+
LIBOPTS = -shared
8+
CFLAGS = -fPIC -std=gnu99
9+
CC = gcc
10+
11+
libasdf.so : asdf.c
12+
$(CC) $< $(LIBOPTS) $(CFLAGS) -o $@
13+
14+
clean :
15+
rm *.so

FFI-so/src/asdf.c

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*******************************************************************************
2+
Dummy library
3+
********************************************************************************
4+
Alfredo Canziani, Nov 14
5+
*******************************************************************************/
6+
7+
// Dummy function definition
8+
void dummy(int* ptrFromLua, int size) {
9+
10+
for (int i = 0; i < size; i++)
11+
ptrFromLua[i] = i + 1;
12+
13+
return;
14+
15+
}

FFI-so/src/run.lua

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--------------------------------------------------------------------------------
2+
-- FFI and .so library
3+
--------------------------------------------------------------------------------
4+
-- Alfredo Canziani, Nov 14
5+
--------------------------------------------------------------------------------
6+
7+
-- Instructions ----------------------------------------------------------------
8+
-- You can run this code and see the results of the operation (1st option) or
9+
-- you can increase the lenght of the tensor and turn off the printing
10+
11+
-- Choose a combination (comment the other)
12+
--<<<
13+
length = 5; display = true
14+
--<<<>>>
15+
--length = 1e7; display = false
16+
-->>>
17+
18+
-- FFI stuff -------------------------------------------------------------------
19+
-- Require ffi
20+
ffi = require("ffi")
21+
-- Load myLib
22+
myLib = ffi.load(paths.cwd() .. '/libasdf.so')
23+
-- Function prototypes definition
24+
ffi.cdef [[
25+
void dummy(int* ptr_form_lua, int size)
26+
]]
27+
28+
-- Shortcuts -------------------------------------------------------------------
29+
-- printf
30+
pf = function(...) print(string.format(...)) end
31+
32+
-- Main program ----------------------------------------------------------------
33+
a = torch.IntTensor(length):random()
34+
b = a:clone()
35+
if display then
36+
print('Random vector')
37+
print(a)
38+
end
39+
40+
timer = torch.Timer()
41+
myLib.dummy(torch.data(a), length)
42+
pf('C function computation time %.2f ms', timer:time().real*1e3)
43+
if display then print(a) end
44+
45+
timer = torch.Timer()
46+
for i = 1, length do b[i] = i end
47+
pf('Lua loop computation time %.2f ms', timer:time().real*1e3)
48+
if display then print(b) end

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This repository tries to provide some advanced tricks with [Torch7](http://torch
44

55
## *FFI* series
66

7-
[*LuaJIT*](http://luajit.org/) [*FFI library*](http://luajit.org/ext_ffi.html) provides an incredible easy way to call external C functions and use C data structures from pure Lua code.
8-
The *FFI series* will provide several working examples of playing with C from within Torch, taking for granted no prior knowledge of building `.so` libraries, using `.make` files or tackling *Tensors* pointers.
7+
[*LuaJIT*](http://luajit.org/) [*FFI library*](http://luajit.org/ext_ffi.html) provides an incredible easy way to call external *C* functions and use *C* data structures from pure Lua code.
8+
The *FFI series* will provide several working examples of playing with *C* from within *Torch*, taking for granted no prior knowledge of building `.so` libraries, using `Makefile` or tackling *Tensors* pointers.
99

10-
- *FFI* and `.so` library
10+
1. [*FFI* and `.so` library](FFI-so/README.md)

0 commit comments

Comments
 (0)