-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path2016-05-31-Matlab-FFI.html
156 lines (133 loc) · 7.76 KB
/
2016-05-31-Matlab-FFI.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="alternate"
type="application/rss+xml"
href="https://bastibe.de/rss.xml"
title="RSS feed for https://bastibe.de/">
<title>Matlab has an FFI and it is not Mex</title>
<meta name="author" content="Bastian Bechtold">
<meta name="referrer" content="no-referrer">
<link href= "static/style.css" rel="stylesheet" type="text/css" />
<link rel="icon" href="static/favicon.ico">
<link rel="apple-touch-icon-precomposed" href="static/favicon-152.png">
<link rel="msapplication-TitleImage" href="static/favicon-144.png">
<link rel="msapplication-TitleColor" href="#0141ff">
<script src="static/katex.min.js"></script>
<script src="static/auto-render.min.js"></script>
<script src="static/lightbox.js"></script>
<link rel="stylesheet" href="static/katex.min.css">
<script>document.addEventListener("DOMContentLoaded", function() { renderMathInElement(document.body); });</script>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8">
<meta name="viewport" content="initial-scale=1,width=device-width,minimum-scale=1"></head>
<body>
<div id="preamble" class="status"><div class="header">
<a href="https://bastibe.de">Basti's Scratchpad on the Internet</a>
<div class="sitelinks">
<a href="https://github.com/bastibe">Github</a> | <a href="https://bastibe.de/projects.html">Projects</a> | <a href="https://bastibe.de/uses.html">Uses</a> | <a href="https://bastibe.de/reviews.html">Reviews</a> | <a href="https://bastibe.de/about.html">About</a>
</div>
</div></div>
<div id="content">
<div class="post-date">31 May 2016</div><h1 class="post-title"><a href="https://bastibe.de/2016-05-31-Matlab-FFI.html">Matlab has an FFI and it is not Mex</a></h1>
<p>
Sometimes, you just have to use C code. There's no way around it. C is the lingua franca and bedrock of our computational world. Even in Matlab, sometimes, you just have to call into a C library.
</p>
<p>
So, you grab your towel, you bite the bullet, you strap into your K&R, and get down to it: You start writing a Mex file. And you curse, and you cry, because writing C is hard, and Mex doesn't exactly make it any better. But you know what? <i>There is a better way!</i>
</p>
<p>
Because, unbeknownst to many, Matlab includes a Foreign Function Interface. The technique was probably pioneered by <a href="https://common-lisp.net/project/cffi/">Common Lisp</a>, and has since <a href="http://luajit.org/ext_ffi.html">been</a> <a href="http://cffi.readthedocs.io/en/latest/overview.html">widely</a> <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html">adopted</a> <a href="https://en.wikipedia.org/wiki/Foreign_function_interface">everywhere</a>: calling functions in a C library without writing any C code and without invoking a compiler!
</p>
<p>
Mind you, there remains a large and essential impedance mismatch between C's statically typed calling conventions and the vagaries of a dynamically typed language such as Matlab, and even the nicest FFI can't completely hide that fact. But anything is better than the abomination that is Mex.
</p>
<p>
So here goes, a very simple C library that adds two arrays:
</p>
<div class="org-src-container">
<pre class="src src-C">// test.c:
#include test.h
void add_arrays(float *out, float* in, size_t length) {
for (size_t n=0; n<length; n++) {
out[n] += in[n];
}
}
// test.h:
#include <stddef.h>
void add_arrays(float *out, float *in, size_t length);
</pre>
</div>
<p>
Let's compile it! <code>gcc -shared -std=c99 -o test.so test.c</code> will do the trick.
</p>
<p>
Now, let's load that library into Matlab with <code>loadlibrary</code>:
</p>
<div class="org-src-container">
<pre class="src src-octave">if not(libisloaded('test'))
[notfound, warnings] = loadlibrary('test.so', 'test.h');
assert(isempty(notfound), 'could not load test library')
end
</pre>
</div>
<p>
Note that <code>loadlibrary</code> can't parse many things you would commonly find in header files, so you will likely have to strip them down to the bare essentials. Additionally, <code>loadlibrary</code> doesn't throw errors if it can't load a library, so we always have to check the <code>notfound</code> output argument to see if the library was actually loaded successfully.
</p>
<p>
With that, we can call functions in that library using <code>calllib</code>. But we can't just pass in Matlab vectors, that would be too easy. We first have to convert them to something C can understand: Pointers
</p>
<div class="org-src-container">
<pre class="src src-octave">vector1 = [1 2 3 4 5];
vector2 = [9 8 7 6 5];
vector1ptr = libpointer('singlePointer', vector1);
vector2ptr = libpointer('singlePointer', vector2);
</pre>
</div>
<p>
What is nice about this is that this automatically converts the vectors from <code>double</code> to <code>float</code>. What is less nice is that it uses its weird <code>singlePtr</code> notation instead of the more canonical <code>float*</code> that you would expect from a self-respecting C header.
</p>
<p>
Then, finally, let's call our function:
</p>
<div class="org-src-container">
<pre class="src src-octave">calllib('test', 'add_arrays', vector1ptr, vector2ptr, length(vector1));
</pre>
</div>
<p>
If you see no errors, everything went smoothly, and you will now have changed the content of vector1ptr, which we can have a look at like this:
</p>
<div class="org-src-container">
<pre class="src src-octave">added_vectors = vector1ptr.Value;
</pre>
</div>
<p>
Note that this didn't change the contents of <code>vector1</code>, only of the newly created pointer. So there will always be some memory overhead to this technique in comparison to Mex files. However, runtime overhead seems pretty fine:
</p>
<div class="org-src-container">
<pre class="src src-octave">timeit(@() calllib('test', 'add_arrays', vector1ptr, vector2ptr, length(vector1)))
% ans = 1.9155e-05
timeit(@() the_same_thing_but_as_a_mex_file(single(vector1), single(vector2)))
% ans = 4.6262e-05
timeit(@() the_same_thing_plus_argument_conversion(vector1, vector2))
% ans = 1.2326e-04
</pre>
</div>
<p>
So as you can see, the <code>calllib</code> is plenty fast. However, if you add the Matlab code for converting the double arrays to pointers and extracting the summed data afterwards, the FFI is noticeably slower than a Mex file.
</p>
<p>
However, If I ask myself whether I would sacrifice 0.00007 seconds of computational overhead for hours of my life <i>not</i> spent with Mex, there really is no competition. I will choose Matlab's FFI over writing Mex files every time.
</p>
<div class="taglist"><a href="https://bastibe.de/tags.html">Tags</a>: <a href="https://bastibe.de/tag-matlab.html">matlab</a> </div>
<div id="comments"><script async src="https://talk.hyvor.com/embed/embed.js" type="module"></script>
<hyvor-talk-comments id="hyvorcomments" website-id="3390" page-id=""></hyvor-talk-comments>
<script type="text/javascript">
document.getElementById("hyvorcomments").setAttribute("page-id", location.pathname);
</script></div></div>
<div id="postamble" class="status"><div id="archive">
<a href="https://bastibe.de/archive.html">Other posts</a>
</div>
<center><a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/3.0/88x31.png" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">bastibe.de</span> by <a xmlns:cc="https://creativecommons.org/ns#" href="https://bastibe.de" property="cc:attributionName" rel="cc:attributionURL">Bastian Bechtold</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</center></div>
</body>
</html>