{"id":4705,"date":"2014-03-30T14:47:18","date_gmt":"2014-03-30T14:47:18","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2014\/03\/30\/how-to-dynamically-load-often-re-generated-c-code-quickly-collection-of-common-programming-errors\/"},"modified":"2014-03-30T14:47:18","modified_gmt":"2014-03-30T14:47:18","slug":"how-to-dynamically-load-often-re-generated-c-code-quickly-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2014\/03\/30\/how-to-dynamically-load-often-re-generated-c-code-quickly-collection-of-common-programming-errors\/","title":{"rendered":"How to dynamically load often re-generated c code quickly?-Collection of common programming errors"},"content":{"rendered":"<p>What you want to do is reasonable, and I am doing exactly that in MELT (a high level domain specific language to extend GCC; MELT is compiled to C, thru a translator itself written in MELT).<\/p>\n<p>First, when generating C code (or many other source languages), a good advice is to keep some sort of abstract syntax tree (AST) in memory. So build first the entire AST of the generated C code, then emit it as C syntax. Don&#8217;t think of your code generation framework without an explicit AST (in other words, generation of C code with a bunch of printf is a maintenance nightmare, you want to have some intermediate representation).<\/p>\n<p>Second, the main reason to generate C code is to take advantage of a good optimizing compiler (another reason is the portability and ubiquity of C). If you don&#8217;t care about performance of the generated code (and TCC compiles very quickly C into a very naive and slow machine code) you could use some other approaches, e.g. using some JIT libraries like Gnu lightning (very quick generation of slow machine code), Gnu Libjit (generated machine code is a bit better), LLVM (good machine code generated, but generation time comparable to a compiler).<\/p>\n<p>So if you generate C code and want it to run quickly, the compilation time of the C code is not negligible (since you probably would fork a <code>gcc -O -fPIC -shared<\/code> command to make some shared object <code>foo.so<\/code> out of your generated <code>foo.c<\/code>). By experience, generating C code takes much less time than compiling it (with <code>gcc -O<\/code>). In MELT, the generation of C code is more than 10x faster than its compilation by GCC (and usually 30x faster). But the optimizations done by a C compiler are worth it.<\/p>\n<p>Once you emitted your C code, forked its compilation into a <code>.so<\/code> shared object, you can <code>dlopen<\/code> it. Don&#8217;t be shy, my manydl.c example demonstrates that on Linux you can dlopen a big lot of shared objects (many hundreds of thousands). The real bottleneck is the compilation of the generated C code. In practice, you don&#8217;t really need to <code>dlclose<\/code> on Linux (unless you are coding a server program needing to run for months); an unused shared module can stay practically <code>dlopen<\/code>-ed and you mostly are leaking process address space (which is a cheap resource), since most of that unused <code>.so<\/code> would be swapped-out. <code>dlopen<\/code> is done quickly, what takes time is the compilation of a C source, because you really want the optimization to be done by the C compiler.<\/p>\n<p>You coul use many other different approaches, e.g. have a bytecode interpreter and generate for that bytecode, use Common Lisp (e.g. SBCL on Linux which compiles dynamically to machine code), LuaJit, Java, MetaOcaml etc.<\/p>\n<p>As others suggested, you don&#8217;t care much about the time to write a C file, and it will stay in filesystem cache in practice. And writing it is much faster than compiling it, so staying in memory is not worth the trouble. Use some <em>tmpfs<\/em> if you are concerned by I\/O times.<\/p>\n<h2>addenda<\/h2>\n<p>You asked<\/p>\n<blockquote>\n<p>Can a library <code>.so<\/code> file on Linux be re-compiled and <em>re-<\/em> loaded at runtime?<\/p>\n<\/blockquote>\n<p>Of course yes: you should fork a command to build the library from the generated C code (e.g. a <code>gcc -O -fPIC -shared generated.c -o generated.so<\/code>, but you could do it indirectly e.g. by running a <code>make -j<\/code>, especially if the <code>generated.so<\/code> is big enough to make it relevant to split the <code>generated.c<\/code> in several C generated files!) and then you dynamically load your library with dlopen (giving a full path like <code>\/some\/file\/path\/to\/generated.so<\/code>, and probably the <code>RTLD_NOW<\/code> flag, to it) and you have to use <code>dlsym<\/code> to find relevant symbols inside. Don&#8217;t think of <em>re<\/em>-loading (a second time) the same <code>generated.so<\/code>, better to emit a unique <code>generated1.c<\/code> (then <code>generated2.c<\/code> etc&#8230;) C file, then to compile it to a <em>unique<\/em> <code>generated1.so<\/code> (the second time to <code>generated2.so<\/code>, etc&#8230;) then to <code>dlopen<\/code> it (and this can be done many hundred thousands of times). You may want to have, in the emitted <code>generated*.c<\/code> files, some constructor functions which would be executed at <code>dlopen<\/code> time of the <code>generated*.so<\/code><\/p>\n<p>Your base application program should have defined a convention about the set of dlsym-ed names (usually functions) and how they are called. It should only directly call functions in your <code>generated*.so<\/code> thru <code>dlsym<\/code>-ed function pointers. In practice you would decide for example that each <code>generated*.c<\/code> defines a function <code>void dynfoo(int)<\/code> and <code>int dynbar(int,int)<\/code> and use <code>dlsym<\/code> with <code>\"dynfoo\"<\/code> and <code>\"dynbar\"<\/code> and call these thru function pointers (returned by <code>dlsym<\/code>). You should also define conventions of how and when these <code>dynfoo<\/code> and <code>dynbar<\/code> would be called. You&#8217;ll better link your base application with <code>-rdynamic<\/code> so that your <code>generated*.c<\/code> files could call your application functions.<\/p>\n<p>You <strong>don&#8217;t<\/strong> want your <code>generated*.so<\/code> to <strong>re-define<\/strong> <em>existing<\/em> names. For instance, you don&#8217;t want to redefine <code>malloc<\/code> in your <code>generated*.c<\/code> and expect all heap allocation functions to magically use your new variant (that probably won&#8217;t work, and if even if it did, it would be dangerous).<\/p>\n<p>You probably won&#8217;t bother to <code>dlclose<\/code> a dynamically loaded shared object, except at application clean-up and exit time (but I don&#8217;t bother at all to <code>dlclose<\/code>). If you do <code>dlclose<\/code> some dynamically loaded <code>generated*.so<\/code> file, be sure that nothing is used in it: no pointers, not even return addresses in call frames, are existing to it.<\/p>\n<p>P.S. the MELT translator is currently 57KLOC of MELT code translated to nearly 1770KLOC of C code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What you want to do is reasonable, and I am doing exactly that in MELT (a high level domain specific language to extend GCC; MELT is compiled to C, thru a translator itself written in MELT). First, when generating C code (or many other source languages), a good advice is to keep some sort of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4705","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/4705","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=4705"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/4705\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=4705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=4705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=4705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}