<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Momtchil Momtchev on Medium]]></title>
        <description><![CDATA[Stories by Momtchil Momtchev on Medium]]></description>
        <link>https://medium.com/@mmomtchev?source=rss-fba99286a61------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*ehwD6KZUdaTQAbm2D8GJyg.jpeg</url>
            <title>Stories by Momtchil Momtchev on Medium</title>
            <link>https://medium.com/@mmomtchev?source=rss-fba99286a61------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 15 Jul 2026 09:18:34 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@mmomtchev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The true cost of C++ exceptions]]></title>
            <link>https://mmomtchev.medium.com/the-true-cost-of-c-exceptions-7be7614b5d84?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/7be7614b5d84</guid>
            <category><![CDATA[exception-handling]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[compilation]]></category>
            <category><![CDATA[cplusplus]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sun, 23 Jun 2024 12:52:17 GMT</pubDate>
            <atom:updated>2024-06-23T12:52:17.146Z</atom:updated>
            <content:encoded><![CDATA[<p>A rant about -fno-exceptions as an optimization technique</p><p>Not so long ago I got burned for a second time during the last 6 months by a horrible MSVC pitfall called the _HAS_EXCEPTIONS macro. The second time it took me about an hour to realize the problem — the first time it took me almost three days — including the time the write my previous story about using the Google address sanitizer in a Node.js addon on Windows [<a href="https://mmomtchev.medium.com/debugging-random-memory-corruption-with-asan-in-a-node-js-addon-on-windows-with-msvc-6246af0c22c7">#7</a>]. Should you be interested in the particular problem of exceptions on Windows for Node.js addons, I have included some links at the very end of the story.</p><p>This story focuses on whether disabling C++ exceptions when building is actually a valid optimization.</p><h3>There is no such thing as C++ without exceptions</h3><p>No more than there is C++ without const variables or C++ without switch statements. This horrible compiler option has been born out of necessity and inherited from the early days when the compilers had very poor exceptions implementations. A 2006 industry study, without any factual benchmarks, but with some very big names on its committee, including Bjarne Stroustrup, came to the conclusion that thanks to advances in compilation, the early performance problems of using C++ exceptions were more or less completely solved [<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf">#5</a>].</p><p>I will cover the few still valid reasons not to use them in a later section.</p><h4>Exceptions are one of the greatest advances in computer languages</h4><p>This is not an overstatement. Exceptions enable structured error handling that greatly simplifies the code. Every single modern high level language has them. <strong>And C++, being this unique language that is both high level and low level at the same time, is not an exception (</strong><em>pun intended</em><strong>).</strong></p><p>However, exceptions in C++ have <em>bad reputation</em>. There is a very famous Google coding style document from the 2000s that forbids using them in Google code [<a href="https://google.github.io/styleguide/cppguide.html#Exceptions">#1</a>]. There is also the browser mafia, very inclined to cut corners during the second browser wars, that famously decided to skip them. C++ exceptions are also <em>controversial</em>. A C++ compiler engineer, when asked about exceptions, is wary of starting a flame war [<a href="https://discourse.llvm.org/t/exceptions-and-performance/56185">#2</a>]. It seems that the LLVM team itself, the C++ compiler engineers, avoid using them! Novice C++ programmers are told that C++ exceptions are dangerous and slow. C++ exceptions are <em>scary</em>.</p><p>If you are new to C++ memory management — learn one simple rule — always catch exceptions by const reference — and throw only std::exception objects. You will never ever worry about memory.</p><p>Yeah, but exceptions are <em>slow</em>, right?</p><p><em>Are they?</em></p><h3>Show me the code and the benchmarks</h3><p>There are already quite a lot of benchmarks that deal with throwing exceptions [<a href="https://pspdfkit.com/blog/2020/performance-overhead-of-exceptions-in-cpp/">#3</a>] [<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2544r0.html">#4</a>]. And in case you didn’t know it — <strong>throwing an exception is expensive</strong> — it is orders of magnitude more expensive that returning an error code.</p><p>So if you plan to have a 10% exception rate, as it is the case in the second benchmark from the two cited above, then exceptions are not for you. Exceptions are about <em>exceptional</em> situations. When you have a 10% rate, this is not an error, this is part of the nominal mode and you need another error-handling mechanism.</p><p>This story is especially about the compiler option -fno-exceptions and its benefits.</p><p>So, before diving in the numbers, lets clarify what does -fno-exceptions do? When a C++ function gets compiled, the compiler must produce the so-called <em>stack unwinding code </em>— code that gets called to destroy all the local objects. As an exception must propagate through a certain number of function calls, it must follow this stack unwinding chain in order to destroy all the local objects. Code compiled with -fno-exceptions lacks part of this information, which means that if an exception reaches it, it is transformed into a std::terminate and terminates the program.</p><p>In order to stress the function calling, we will be using the following horrible program which does nothing but function calls:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5b5f98f47dfcdb020890540b42fdf8f9/href">https://medium.com/media/5b5f98f47dfcdb020890540b42fdf8f9/href</a></iframe><p>Note the branch prediction optimization. The differences will be measuring are so small, that unless you get this correctly, you will be measuring mostly the branch prediction performance. It outweighs any impact from stack unwinding by a very wide margin.</p><p>We want to measure the impact of compiling with exceptions enabled. Also note that we are not actually throwing any exceptions — we consider throwing to be a very rare event — we want to measure the impact of simply <em>using</em> exceptions.</p><p>We will be compiling in a few different modes (you can find a link to the repository with the full code with all #ifdef at the end of story):</p><ul><li>no_unwind : without exceptions at all, throwing manually replaced by a std::abort through a compile time macro, compiling with:</li></ul><pre>-fno-exceptions -fno-rtti -fno-unwind-tables -fomit-frame-pointer</pre><ul><li>unwind_dontcare : without exceptions at all, throwing manually replaced by a std::abort through a compile time macro but exceptions enabled in the compiler — a very important test for everyone who is disabling exceptions for performance benefits in code that does not use them</li><li>unwind_noexcept : without exceptions at all, throwing manually replaced by a std::abort through a compile time macro but exceptions enabled in the compiler and the fibonacci function marked as noexcept</li><li>unwind_throwing : with throwing exceptions but without a catch block in fibonacci — only a single catch block in main</li><li>unwind_catching : the full deal as you see it</li><li>and a plain old C test — for everyone who firmly believes that the world lost a little bit of performance when we started switching to C++ compilers</li></ul><p>I did all of the tests using gcc , clang and Intel OneAPI — without any significant differences — except for the -funroll-loops option which is not enabled by default in clang . We will be discussing mainly the gcc results on x86–64. Keep in mind that x86-64 has very good hardware support for exceptions and stack unwinding and the results might be slightly different on other architectures.</p><p>Let’s try to analyze those results:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*TTh8Yeht2CA0MMzbUlMJqg.png" /></figure><h3>No optimization (-O0)</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9367b8d3b01e547a87422f55038bf48e/href">https://medium.com/media/9367b8d3b01e547a87422f55038bf48e/href</a></iframe><p><strong>If you do not throw and do not catch, having or not having exceptions enabled comes down to nothing.</strong> Throwing has a small cost, catching at each recursion level has a more significant cost. In this case, there is real work to do — saving a handler before each call.</p><p>When it comes to the plain old C — the answer is — no, most of the time, compiling C in C++ mode does not lead to a performance loss.</p><p>What is absolutely remarkable is that the -fomit-frame-pointer binary is very slightly larger. <em>How can omitting the frame pointer lead to a larger binary??</em></p><h4><em>Well, this is lesson number 1</em></h4><p>-fomit-frame-pointer, another not-so-great “<em>optimization</em>” idea.</p><p>Omitting the frame pointer saves one instruction in the beginning. However this also forces the compiler to access all the local variables using the slightly larger instruction based on the stack pointer %rsp instead of the more efficient %rbp instruction:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*MwGBj9dJVVHKk3nrLsIKZA.png" /><figcaption>-fomit-frame-pointer -fno-exceptions</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/570/1*3hN8McAVo9m1kmvXZoLQjg.png" /><figcaption>don’t care (ie compiler knows best)</figcaption></figure><h3>Normal optimization (-O2)</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4953513f42ea2ff533ad63efe67af419/href">https://medium.com/media/4953513f42ea2ff533ad63efe67af419/href</a></iframe><p>Recursion unrolling, an optimization quite similar to loop unrolling, has kicked in. The functions are much bigger and run slightly faster. However, the above cited LLVM compiler engineer was right — throwing and catching gets in the way of compiler optimization and renders unrolling the recursion impossible.</p><h4>Lesson number 2</h4><p><strong>A very important thing to note: our puny efforts at saving several dozen bytes from the function size have been completely dwarfed by the 500 byte increase from the recursion unrolling and now look totally ridiculous.</strong> Something to consider in the future.</p><h3>Aggressive optimization (-O3)</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/218576ef3dada10a6aa55551dfe2e6e9/href">https://medium.com/media/218576ef3dada10a6aa55551dfe2e6e9/href</a></iframe><p>Well, with -O3 gcc has now the upper hand versus the exception handling. Recursion unrolling is possible for all cases. Not only throwing and catching are now completely free — by pure chance — because of the better utilization of the CPU pipelining, <strong>catching at each recursion level is actually faster then compiling without exceptions.</strong> Just look at the stalled-cycles-backend counter to see the problem:</p><pre> Performance counter stats for &#39;./test-cc-O3-unwind_catching&#39;:<br><br>         19 565,33 msec task-clock                       #    1,000 CPUs utilized             <br>               184      context-switches                 #    9,404 /sec                      <br>                 0      cpu-migrations                   #    0,000 /sec                      <br>               110      page-faults                      #    5,622 /sec                      <br>    66 614 649 139      cycles                           #    3,405 GHz                         (49,99%)<br>     6 001 820 007      stalled-cycles-frontend          #    9,01% frontend cycles idle        (50,01%)<br>       862 867 946      stalled-cycles-backend           #    1,30% backend cycles idle         (50,02%)<br>   146 312 773 990      instructions                     #    2,20  insn per cycle            <br>                                                  #    0,04  stalled cycles per insn     (50,01%)<br>    26 776 756 708      branches                         #    1,369 G/sec                       (49,99%)<br>       414 037 604      branch-misses                    #    1,55% of all branches             (49,98%)<br><br>      19,568759571 seconds time elapsed<br><br>      19,566978000 seconds user<br>       0,000000000 seconds sys</pre><pre> Performance counter stats for &#39;./test-cc-O3-no_unwind&#39;:<br><br>         21 431,10 msec task-clock                       #    1,000 CPUs utilized             <br>               157      context-switches                 #    7,326 /sec                      <br>                 0      cpu-migrations                   #    0,000 /sec                      <br>                52      page-faults                      #    2,426 /sec                      <br>    72 985 918 854      cycles                           #    3,406 GHz                         (50,00%)<br>     6 651 978 854      stalled-cycles-frontend          #    9,11% frontend cycles idle        (50,00%)<br>     9 548 404 958      stalled-cycles-backend           #   13,08% backend cycles idle         (50,00%)<br>   134 993 845 095      instructions                     #    1,85  insn per cycle            <br>                                                  #    0,07  stalled cycles per insn     (50,00%)<br>    19 862 285 441      branches                         #  926,797 M/sec                       (50,00%)<br>       788 489 544      branch-misses                    #    3,97% of all branches             (50,00%)<br><br>      21,433689694 seconds time elapsed<br><br>      21,428887000 seconds user<br>       0,003999000 seconds sys</pre><p>I don’t want to dive into this 700 bytes function to see why and the results — which once again are the product of pure coincidence — might be different on a different CPU — as this is on an older AMD CPU for which the optimization might not be perfect. But still, the lesson stands and it is a very important one:</p><h4>Lesson number 3</h4><p><strong>Simply avoid fiddling with compiler options. The compiler authors usually know what they are doing and it is unlikely they left out any hidden magical performance increasing options.</strong></p><h3>Adding code to the stack unwinding</h3><p>As we already said, the job of the stack unwinding code is to destroy the local variables. However, in this first test, the function did not contain any local variables with destructors. We will see that adding even a single variable will make any execution time difference impossible to measure — as this difference is so tiny. However this will allow to see the code size difference. Here is the code for the second test:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6fe4743dab8fb9182f428338ed3f3747/href">https://medium.com/media/6fe4743dab8fb9182f428338ed3f3747/href</a></iframe><p>We have added an artificial object construction that cannot be optimized away and has a non-trivial destructor that must be called when the exception travels up the stack. Although I have been very careful to avoid any memory allocation, this was more than enough to become the dominant part of the execution — now the execution time difference is (<em>almost</em>) impossible to measure.</p><p>However the code size experiment results remain the same — slight code size increase in the unoptimized case, that is completely dwarfed by the various loop/recursion unrolling optimizations:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e60b3ceb451386330de9e476b4e0b140/href">https://medium.com/media/e60b3ceb451386330de9e476b4e0b140/href</a></iframe><figure><img alt="" src="https://cdn-images-1.medium.com/max/911/1*PwZC2QZtvMUHmd8hMgrIpg.png" /></figure><h4>Stack unwinding under the hood</h4><p>We can use the last example to see the additional code needed for stack unwinding:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MfF6b47TNrx0gCtzD5o6sg.png" /><figcaption><a href="https://github.com/mmomtchev/cpp-exceptions-cost/blob/main/img/test-vector-cc-O0-unwind_dontcare-annotated.png">Actual unwinding code (https://github.com/mmomtchev/cpp-exceptions-cost/blob/main/img/test-vector-cc-O0-unwind_dontcare-annotated.png)</a></figcaption></figure><p>This is the raw unoptimized version. The unwinding code is a small subroutine at the end of the function that calls the destructors of the local objects. The new x86-64 endbr64 instruction — a security feature called <em>Indirect Branch Tracking </em>— marks valid jump locations and makes it particularly easy to spot the beginning of the function and the beginning of the stack unwinding routine. When the function exits normally, a jmp allows to bypass the unwinding. try / catch statements save its entry points in a special memory section which works as a second parallel stack. Sometimes — especially for the standard library — static tables allow for greater efficiency. You can find a more detailed description in [<a href="https://www.zyma.me/post/stack-unwind-intro/">#6</a>].</p><h3>Alternatives to exceptions</h3><p>There are few cases where not using exceptions is probably the right answer. For example, in the world of embedded software — where code size may be very important— this allows to skip parts of the compiler runtime — <em>if</em> the compiler supports it.</p><p>Do not forget that most normal C++ compilers will always use the same precompiled C++ runtime which uses exceptions.</p><p>Also, in very low-level routines, especially when very high error rates are expected, alternative constructs such as Result in Rust or the new std::expected in C++ can offer a better alternative.</p><p>It is worth noting that Rust — which does not have exceptions, but relies only on the higher performance Result — is trying to be a successor of C and not C++.</p><p>Because of its special status as both high- and low-level language, C++ offers both mechanisms.</p><h3>Conclusions</h3><ol><li>If you don’t use exceptions, and you decide to disable them at the compilation level, you can expect some very small execution times gains in some cases, but generally they tend to completely disappear in an optimized build</li><li>The code size gains from reducing the stack unwinding code are on the order of 5% to 10% but these are completely dwarfed by loop unrolling when using optimizations</li><li>If you decide to actually use exceptions — without throwing that much — every try and catch block has a small cost, that tends to disappear in an optimized build</li><li>A throw statement might get in the way of compiler optimization, don’t use it in tight loops</li><li>If you throw a lot, you should try one of the alternative error handling mechanisms</li></ol><h4>If you feel tempted to disable exceptions when building your C++ project, think again. Take the time to actually test it. Check if actually improves anything, because it definitely breaks things.</h4><p>If it is a standalone application, then this will probably affect only yourself — when you start debugging it. But if you are building a target that people will be linking with, I very strongly recommend you to avoid doing this. Otherwise, sooner or later, you will have an user that will spend countless hours debugging a very bizarre problem.</p><p>You can find all the code used for this story at:</p><p><a href="https://github.com/mmomtchev/cpp-exceptions-cost">GitHub - mmomtchev/cpp-exceptions-cost: The true cost of C++ exceptions</a></p><p>References:</p><ol><li>Google C++ Style Guide <a href="https://google.github.io/styleguide/cppguide.html#Exceptions">https://google.github.io/styleguide/cppguide.html#Exceptions</a></li><li>Exceptions and performance, LLVM discussion <a href="https://discourse.llvm.org/t/exceptions-and-performance/56185">https://discourse.llvm.org/t/exceptions-and-performance/56185</a></li><li>Investigating the Performance Overhead of C++ Exceptions: <a href="https://pspdfkit.com/blog/2020/performance-overhead-of-exceptions-in-cpp/">https://pspdfkit.com/blog/2020/performance-overhead-of-exceptions-in-cpp/</a></li><li>P2544R0 C++ exceptions are becoming more and more problematic <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2544r0.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2544r0.html</a></li><li>Technical Report on C++ Performance<br><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf">https://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf</a></li><li>An Introduction to Stack Unwinding and Exception Handling <a href="https://www.zyma.me/post/stack-unwind-intro/">https://www.zyma.me/post/stack-unwind-intro/</a></li><li>Debugging random memory corruption with ASAN in a Node.js addon on Windows with MSVC<a href="https://mmomtchev.medium.com/debugging-random-memory-corruption-with-asan-in-a-node-js-addon-on-windows-with-msvc-6246af0c22c7"><br>https://mmomtchev.medium.com/debugging-random-memory-corruption-with-asan-in-a-node-js-addon-on-windows-with-msvc-6246af0c22c7</a></li></ol><blockquote>The problem that inspired this story:</blockquote><h3>Using C++ exceptions in Node.js addons on Windows</h3><p>Or the horrible _HAS_EXCEPTIONS pitfall in Node.js addons on Windows</p><p>If you are building Node.js addons, Microsoft and the Node.js core team have conspired to create a truly horrible landmine waiting for you. MSVC carries two distinct std::exception implementations that differ in memory size and behavior. If part of your code is compiled without exceptions enabled, while another part has them, you will get an almost working binary with some very subtle memory alignment errors. This is the case of Node.js itself and node-gyp which builds by default with exceptions disabled. Node.js does not use C++ exceptions and cannot trigger the problem — however any Node.js addon that uses them is affected when running on Windows.</p><p>It is because of this problem that I decided to investigate the benefits of those two dreaded compiler options: -fno-exceptions and /EH* that caused me so much pain. MSVC is a particularly vicious offender since the default behavior deviates from the C++ standard — which means that almost every single project overrides it.</p><p>Here is more information on this subject.</p><ul><li>A note in the SWIG JavaScript Evolution manual:</li></ul><p><a href="https://htmlpreview.github.io/?https://github.com/mmomtchev/swig/blob/main/Doc/Manual/Javascript.html#Javascript_napi_exceptions">GitHub &amp; BitBucket HTML Preview</a></p><ul><li>A note in gdal-async :</li></ul><p><a href="https://github.com/mmomtchev/node-gdal-async/blob/ad9eea1f8482bd590c9ad77ea0ecab819aad6053/common.gypi#L74">node-gdal-async/common.gypi at ad9eea1f8482bd590c9ad77ea0ecab819aad6053 · mmomtchev/node-gdal-async</a></p><ul><li>A gist with another example of different behavior of std::exception in MSVC depending on the _HAS_EXCEPTIONS macro:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f0bd706500fd2d4c8c300c5100254a08/href">https://medium.com/media/f0bd706500fd2d4c8c300c5100254a08/href</a></iframe><p>I am an unemployed engineer living on social welfare in France because of a huge judicial scandal that involves a sexually-motivated extortion with the French police and some of the largest IT companies in the world.</p><p>My particular area of expertise is linking C++ and JavaScript.</p><p>I am the author of SWIG JavaScript Evolution, the hadron build system and I have authored and maintain a large number of bindings of C++ libraries for JavaScript.</p><p><a href="https://github.com/mmomtchev/">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7be7614b5d84" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging random memory corruption with ASAN in a Node.js addon on Windows with MSVC]]></title>
            <link>https://mmomtchev.medium.com/debugging-random-memory-corruption-with-asan-in-a-node-js-addon-on-windows-with-msvc-6246af0c22c7?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/6246af0c22c7</guid>
            <category><![CDATA[asan]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[msvc]]></category>
            <category><![CDATA[node-addon-api]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sun, 10 Sep 2023 14:16:51 GMT</pubDate>
            <atom:updated>2024-06-07T16:25:32.744Z</atom:updated>
            <content:encoded><![CDATA[<p>Since recently, MSVC has added built-in support for ASAN — a high-performance memory safety debugger developed by Google that is already included in clang (its original host) and g++.</p><p>Using it is as simple as adding the /fsanitize=address option when compiling. Since we are targetting a Node.js addon, this option would go in binding.gyp :</p><pre>&#39;msvs_settings&#39;: {<br>  &#39;VCCLCompilerTool&#39;: {<br>    &#39;AdditionalOptions&#39;: [<br>      &#39;/fsanitize=address&#39;<br>    ],<br>  }<br>}</pre><p>Then we have to rebuild everything. Like, literally, <em>everything</em>. On Windows, it is not possible to link release and debug versions. This means that if your addon requires any libraries, you will have to rebuild them with the exactly same options.</p><p>Once you have your new magic self-debugging binary addon, you will be up for a very unpleasant surprise. Node.js refuses to load it with the absolutely awesome error message:</p><pre>File not found</pre><p>You can double- even triple-check all your path names — everything will be in order. Alas, this is the only excuse Node.js knows for not loading your library — whatever the error is — you will always get this message. Which will probably be the correct one in 99% of the cases.</p><p>The problem is that, just like on Linux and macOS, ASAN does not accept not being the very first DLL to be loaded. But unlike on Linux and macOS, it does not get the chance to tell you about it.</p><p>Head to <a href="https://github.com/microsoft/Detours">https://github.com/microsoft/Detours</a> — a free library for implementing interceptors for Windows by Microsoft Research. Clone the repository, then launch a Visual Studio Code shell and build it by simply launching in its root:</p><pre>git clone <a href="https://github.com/microsoft/Detours">https://github.com/microsoft/Detours</a><br>cd Detours<br>nmake</pre><p>You need one of its samples, samples\setdll — you will find it in bin.X64\setdll.exe after building the library.</p><p>Now find this library:</p><pre>clang_rt.asan_dbg_dynamic-x86_64.dll</pre><p>It is usually located in some variation (depending on the exact version) of this path:</p><pre>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64\clang_rt.asan_dbg_dynamic-x86_64.dll</pre><p>Make a local copy of node.exe — it is usually located in Program Files :</p><pre>copy &quot;C:\Program Files\nodejs\node.exe&quot; .</pre><p>Then manually inject the ASAN DLL as a dependency:</p><pre>setdll /d:clang_rt.asan_dbg_dynamic-x86_64.dll node.exe</pre><p>Voilà, this <em>custom </em>Node.js will be able to load ASAN-enabled addons.</p><p>Or, alternatively, use withdll to simply launch node.exe without modifying it:</p><pre>withdll /d:clang_rt.asan_dbg_dynamic-x86_64.dll node.exe</pre><p>If this does not work — which might be the case if you haven’t compiled in debug mode — try with the release version:</p><pre>setdll /d:clang_rt.asan_dynamic-x86_64.dll node.exe</pre><p>I am an unemployed IT engineer with a story to tell. I work on open-source software with a particular interest in Node.js addons and JavaScript / C++ interaction. You can find me on Github:</p><p><a href="https://github.com/mmomtchev">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6246af0c22c7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Effortlessly porting a major C++ library to Node.js with SWIG Node-API]]></title>
            <link>https://mmomtchev.medium.com/effortlessly-porting-a-major-c-library-to-node-js-with-swig-napi-3c1a5c4a233f?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/3c1a5c4a233f</guid>
            <category><![CDATA[swig]]></category>
            <category><![CDATA[cplusplus]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[node-addon-api]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Tue, 09 May 2023 16:48:23 GMT</pubDate>
            <atom:updated>2023-09-09T10:24:54.058Z</atom:updated>
            <content:encoded><![CDATA[<p>An introduction for C++ engineers with previous binary Node.js addon experience to SWIG Node-API</p><p><em>(this tutorial will continuously evolve to match SWIG Node-API, the current version is from </em><strong><em>September 9th 2023</em></strong><em> and includes asynchronous execution and TypeScript which still haven’t been merged to the main trunk)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*djWXACYq3M0ry5B2" /><figcaption>Photo by <a href="https://unsplash.com/@jonasrhyner?utm_source=medium&amp;utm_medium=referral">Jonas Rhyner</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Not so long ago, while working on a Node.js project which generated map tiles, I was very surprised to find out that ImageMagick — the quintessential image processing library, initially released in 1990 — was surprisingly absent from its ecosystem. Sure, there were some wrappers around the CLI tools — but they didn’t even come close to the full scale of features available in it.</p><p>Being someone with lots of spare time on my hands — you can check my Github profile for my story — I decided that it was time to right this wrong and do so by working smart instead of working hard.</p><p>Now, if you are part of the younger generation of Noders, you probably have never heard of SWIG before. And it is a pity, because this amazing piece of software can easily crash the job market for high-level language bindings writers — by fully automating their jobs.</p><p>So I found myself looking at SWIG for Node.js for the second time during the last few years — the first time being when I started maintaining gdal-async — the asynchronous GDAL bindings for Node.js. And once again, I came to the conclusion that the Node.js support was barely usable — it hadn’t been updated for the last 10 years — which in the Node.js world was comparable to the evolution from feudalism to industrial society.</p><p>Now, ImageMagick is an absolutely huge C++ project — the SWIG-generated Node.js bindings are about 400,000 lines of code. Hand-writing these would probably have costed upwards of 1 man-year.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VfFeVIn0zksj4UeLOsaPpw.png" /><figcaption>SWIG offers the best of both worlds — at the cost of a relatively steep learning curve for the bindings author</figcaption></figure><p>With SWIG, it took me two weeks to implement a modern NAPI-based backend — and then one more week to create the ImageMagick bindings. And only because this was the SWIG NAPI testing project and there were some rough edges to polish.</p><p>This was the perfect opportunity to kill three birds with one stone:</p><ul><li>Publish full ImageMagick bindings for<em> </em>Node.js</li><li>Write a modern, NAPI-based, Node.js backend for SWIG</li><li>Write a simple tutorial to unleash a torrent of C++ libraries for Node.js</li></ul><h3>Enter SWIG</h3><p>SWIG is a C++ header compiler that can produce bindings for interpreted and dynamically typed languages that cannot link directly with C/C++ shared libraries. It supports a large number of languages, including JavaScript.</p><p>Now, SWIG is a complicated piece of engineering and no matter what, you won’t be able to avoid delving deep into its huge documentation.</p><p>Still, this tutorial’s goal is to allow you to quickly understand some basic concepts and bootstrap your next Node.js addon project.</p><p><strong>Learning and using SWIG is definitely not easy. It is an entire computer language with its own C++ transpiler. It eliminates the work that is necessary to write a huge native C++ addon. However, it does not eliminate the requirement of having the necessary skills to do so.</strong></p><p>When using SWIG, you will be expected to be able to look and understand the generated code — especially if you will be writing advanced typemaps.</p><p><strong>So, if you have never worked on a Node.js binary addon — expect a very steep learning curve.</strong></p><h3>As of September 9th 2023, the bulk of the Node-API support in SWIG has been merged in the main trunk. It will be available in SWIG 4.2.0. The asynchronous execution and the TypeScript support is still being reviewed and undergoing polishing.</h3><p>The full SWIG Node-API including asynchronous execution and TypeScript support can be used by checking out my development branch at</p><p><a href="https://github.com/mmomtchev/swig#mmom">https://github.com/mmomtchev/swig#mmom</a></p><h3><strong>node-magickwand</strong></h3><p>You can find the resulting project, which is a real-world Node.js binary addon based on a very complex C++ library with multi-OS support here:</p><p><a href="https://github.com/mmomtchev/node-magickwand/">GitHub - mmomtchev/node-magickwand: Territorialing the map</a></p><h3>The scaffolding</h3><p>Create a node-addon-api skeleton:</p><pre>sudo npm install -g generator-napi-module<br>mkdir node-magickwand &amp;&amp; cd node-magickwand &amp;&amp; yo napi-module</pre><p>SWIG can compile C++ headers, but it usually needs some help with deducing how things work. Its most basic block is the interface .i file. Create its skeleton:</p><pre>%module magickwand<br><br>%{<br>#include &lt;Magick++.h&gt;<br>#include &lt;MagickWand/MagickWand.h&gt;<br>#include &lt;iostream&gt;<br><br>using namespace Magick;<br>%}<br><br>%include &quot;cpointer.i&quot;<br>%include &quot;std_string.i&quot;<br>%include &quot;typemaps.i&quot;</pre><p>The very first thing we start with is to tell SWIG how it is supposed to access the C++ library we will be working with. These are the #include statements necessary to bring the ImageMagick definitions into a C++ program. The %{ %} block — called a <em>verbatim </em>block — is a block that SWIG will simply include verbatim in the generated wrappers.</p><p>Then, we have the standard JavaScript typemaps — we won’t be writing code that converts v8::String (wrapped in a Napi::String) arguments to std::string arguments — SWIG NAPI comes with the basic types already supported.</p><pre>%rename(call) operator();<br>%rename(clone) operator=;<br>%rename(equal) operator==;<br>%rename(notEqual) operator!=;<br>%rename(gt) operator&gt;;<br>%rename(lt) operator&lt;;<br>%rename(gte) operator&gt;=;<br>%rename(lte) operator&lt;=;</pre><p>This part is almost mandatory in any JavaScript project — JS does not have operator overloading, so we have to rename those methods. The first line means that when dealing with the callable object transparentImage we won’t be calling transparentImage(im) — we will be calling transparentImage.call(im).</p><h3>The main dish</h3><pre>%nspace;<br>namespace MagickCore {<br>  %include &quot;../swig/magickcore.i&quot;<br>  %include &quot;../swig/magickwand.i&quot;<br>}<br>%include &quot;../swig/magick++.i&quot;</pre><p>Finally, we bring in all the definitions that we will be compiling. This is different than the verbatim block — the verbatim block is a piece of code that will be directly included in the generated code. Here, we are actually compiling in SWIG. SWIG has two ways of absorbing C++ headers — %include which means include and generated wrappers for everything you see — and %import — which means acquaint yourself with the types mentioned there — but do not generate wrappers for them. The second form is typically used for low-level stuff that SWIG needs to be aware of.</p><p>For example, ImageMagick has both an old plain C API — called MagickCore and a newer higher-level C++ API called Magick++. As many methods share the same name, in order to avoid collisions, we have to enable the namespaces support — %nspace — and to include the old C API in a separate namespace.</p><p>Typically, MagickCore is precisely the type of header files that you might consider using an %import for. They contain some type definitions that are used by the other API, but one will probably never need these from JavaScript.</p><p>Normally, here you would have to %include or %import every single header file of your target project. SWIG does not follow #include statements unless specifically told to — and in most normal projects you won’t need this very unusual and hard to use feature. You won’t need the standard class libraries either — SWIG already includes its own definitions for classes such as std::vector or std::string which you have to use. You just have to list all the header files in the exact order of their dependencies. For ImageMagick, with its 120 header files, this is a daunting task and I had to write a dependency analyser and generator — you can check it in src/deps.js— as well as its output in swig/magickcore.i. You won’t need this in most normal projects and I won’t cover it here.</p><h3>Sweeping the unusable methods under the carpet</h3><p>Every C++ project has methods that won’t be needed or won’t even be usable from Node.js — and ImageMagick is not an exception.</p><p>We start by ignoring the methods that depend on a va_list:</p><pre>%ignore LogMagickEventList;<br>%ignore ThrowMagickExceptionList;</pre><p>We also ignore everything that has custom or special memory allocation — these are spread around the various classes — so we take advantage of the regular expressions support:</p><pre>%rename(&quot;$ignore&quot;, regextarget=1) &quot;NoCopy$&quot;;<br>%rename(&quot;$ignore&quot;, regextarget=1) &quot;Allocator&quot;;</pre><h3>The hard methods — returning a container from std::vector</h3><p>Most complex C++ projects will inevitably include some methods that SWIG will be unable to handle without some help. Take a look at this one:</p><pre>template &lt;class Container &gt; <br>  void coderInfoList( Container *container_, <br>                 CoderInfo::MatchType isReadable_   = CoderInfo::AnyMatch, <br>                 CoderInfo::MatchType isWritable_   = CoderInfo::AnyMatch, <br>                 CoderInfo::MatchType isMultiFrame_ = CoderInfo::AnyMatch <br>);</pre><p>This method allows the user to retrieve the descriptors of all image file formats that have been compiled in. You have to pass a pointer to an std compatible container in which you will receive the resulting list. Here is how you are supposed to use it:</p><pre>list&lt;CoderInfo&gt; coderList; <br>  coderInfoList( &amp;coderList,        // Reference to output list <br>              CoderInfo::TrueMatch, // Match readable formats <br>              CoderInfo::AnyMatch,  // Don&#39;t care about writable formats <br>              CoderInfo::AnyMatch); // Don&#39;t care about multi-frame support</pre><p>First of all, C++ templates must be instantiated when compiling. JavaScript won’t be able to call this method unless it has been pre-instantiated.</p><p>So this is the very first thing we will need to tell SWIG:</p><pre>%template(coderInfoArray)<br>   std::vector&lt;Magick::CoderInfo&gt;;<br>%template(coderInfoList) <br>   Magick::coderInfoList&lt;std::vector&lt;Magick::CoderInfo&gt;&gt;;</pre><p>Then, we will have to include some hand-written code to transform these input arguments. This is called a SWIG <em>typemap</em>:</p><pre>%include &quot;std_vector.i&quot;<br><br>%typemap(in, numinputs=0)<br>    std::vector&lt;Magick::CoderInfo&gt; *container_<br>{<br>  $1 = new std::vector&lt;Magick::CoderInfo&gt;;<br>}<br><br>%typemap(argout)<br>    std::vector&lt;Magick::CoderInfo&gt; *container_<br>{<br>  $result = SWIG_NAPI_NewPointerObj(env,<br>                          $1, $1_descriptor, SWIG_POINTER_OWN);<br>}<br><br>%typemap(tsout)<br>  std::vector&lt;Magick::CoderInfo&gt; *container_<br>&quot;std.coderInfoArray&quot;;</pre><p>We create three typemaps here.</p><p>First, an in typemap, that handles arguments of type std::vector&lt;Magick::CoderInfo&gt; *. We can apply it to all such arguments — or only to arguments named container_ — which is probably safer. This typemap works without expecting any arguments coming from JavaScript — it effectively eliminates this argument for the JavaScript caller — this is what numinputs=0 does. JavaScript callers will call this method without its first argument. Then, we tell SWIG to initialize this first argument — which we use $1 to refer to — with a new statement. We create a an empty std::vector for it.</p><p>The second typemap is an argout typemap. SWIG applies it when exiting the method. We use the same type specification as the first one — to make sure that it is applied whenever the first one is applied. Here we assign the method result $result a new wrapped JS object by transferring ownership — because we used new — with the SWIG_POINTER_OWN flag. $1 is the method argument that we assigned in the input typemap. $1_descriptor is its SWIG type descriptor.</p><p>The third typemap — tsout— is required only if you enable the TypeScript support — it specifies the TypeScript return type of the method.</p><p>If you have never worked on a binary Node.js addon this might not be immediately clear. Every C++ object that will be visible to JavaScript must have a JS wrapper that implements JS-callable methods. Luckily, SWIG already includes a JS-compatible std::vector that we can access by including std_vector.i. SWIG_NAPI_NewPointerObj creates this wrapper.</p><p>Voilà, we have effectively transformed this C++ invocation:</p><pre>list&lt;CoderInfo&gt; coderList; <br>coderInfoList( &amp;coderList,        // Reference to output list <br>            CoderInfo::TrueMatch, // Match readable formats <br>            CoderInfo::AnyMatch,  // Don&#39;t care about writable formats <br>            CoderInfo::AnyMatch); // Don&#39;t care about multi-frame support</pre><p>to this JS invocation:</p><pre>const list = coderInfoList(Magick.TrueMatch, Magick.AnyMatch,<br>    Magick.AnyMatch);</pre><p>Instead of expecting a pointer to a container, the JS version of this method will simply return a wrapped std::vector that we can access from JavaScript. This SWIG-provided object implements the size() and get() methods.</p><h3>The hard methods — a void pointer from a Buffer</h3><p>There are a number of methods in Magick::Blob such as Magick::Blob::update or one of the constructors Magick::Blob::Blob that expect arguments of the form const void *data_, size_t length_. We want to the JavaScript caller to be able to invoke these with a single Buffer.</p><p>If we include , SWIG NAPI already provides these typemaps out of the box for arguments named const void *buffer_data, const size_t buffer_len if we include node_buffer.i. We can use an assignment to add a new case:</p><pre>%include &quot;nodejs_buffer.i&quot;<br><br>%typemap(in)        (const void *data_,const size_t length_) =<br>              (const void *buffer_data, const size_t buffer_len);<br>%typemap(typecheck) (const void *data_,const size_t length_) =<br>              (const void *buffer_data, const size_t buffer_len);<br>%typemap(ts)        (const void *data_,const size_t length_) =<br>              (const void *buffer_data, const size_t buffer_len);</pre><p>This kind of typemap is called a multi-argument typemap. It will be applied when a method expects two consecutive arguments of the given type. The name is optional — we can apply it to all pairs of arguments of this type — or only to those named data_ and length_ — which is the safer choice. We haven’t provided a numinputs=1 — this is the default value. This means that those two arguments will be represented by a single JavaScript argument.</p><h3><strong>The hard methods — a void pointer from a TypedArray</strong></h3><p>Let’s take a look at another twisted example, one of the Image class constructors:</p><pre>Image::Image(const size_t width_, <br>  const size_t height_,<br>  std::string map_,<br>  const StorageType type_,<br>  const void *pixels_);</pre><p>It expects a raw pointer to the image data, dimensions and a data type.</p><p>We want to make it work with a TypedArray.</p><p>First of all, we use a verbatim block to embed a function that converts the NAPI type to Magick::StorageType directly into the final code:</p><pre>%{<br>inline Magick::StorageType GetMagickStorageType(<br>    Napi::Env env, const Napi::TypedArray &amp;array) {<br><br>  switch (array.TypedArrayType()) {<br>    case napi_int8_array:<br>    case napi_uint8_array:<br>    case napi_uint8_clamped_array:<br>      return MagickCore::CharPixel;<br>    case napi_int16_array:<br>    case napi_uint16_array:<br>      return MagickCore::ShortPixel;<br>    case napi_int32_array:<br>    case napi_uint32_array:<br>      return MagickCore::LongPixel;<br>    case napi_float32_array:<br>      return MagickCore::FloatPixel;<br>    case napi_float64_array:<br>      return MagickCore::DoublePixel;<br>#if (NAPI_VERSION &gt; 5)<br>    case napi_bigint64_array:<br>    case napi_biguint64_array:<br>#endif  // (NAPI_VERSION &gt; 5)<br>      return MagickCore::LongLongPixel;<br>  }<br>  SWIG_Error(SWIG_ERROR, &quot;Invalid type&quot;);<br>  // Avoid a warning<br>  return MagickCore::CharPixel;<br>}<br>%}</pre><p>Nothing too complicated, nothing unusual.</p><p>Then we create a multi-argument input typemap:</p><pre>%typemap(in)<br>  (const Magick::StorageType type_, void *pixels_)<br>  (Napi::TypedArray _global_typed_array)<br>{<br>  if ($input.IsTypedArray()) {<br>    _global_typed_array = $input.As&lt;Napi::TypedArray&gt;();<br>    $1 = GetMagickStorageType(env, _global_typed_array);<br>    $2 = reinterpret_cast&lt;void*&gt;(<br>      reinterpret_cast&lt;uint8_t *&gt;(<br>        _global_typed_array.ArrayBuffer().Data()) +<br>        _global_typed_array.ByteOffset());<br>  } else {<br>    SWIG_exception_fail(SWIG_TypeError,<br>      &quot;in method &#39;$symname&#39;, argument $argnum is not a TypedArray&quot;);<br>  }<br>}</pre><p>The single input argument coming from JavaScript is referred as $input. It is of type Napi::Value.</p><p>Note, that this typemap also creates a local variable in the wrapper method — this is the second expression in parentheses.</p><p>First thing first, we check if this argument is a JavaScript TypedArray. If it is not, we throw a TypeError. It is important to throw a TypeError — since this is what the overloading dispatcher uses to determine if it should try another overloaded method signature.</p><p>Then we use the previously embedded function to get an ImageMagick-compatible StorageType. Then we proceed to extract the raw pointer to the underlying data of the TypedArray. This is the only correct way to do so. If you have previously used a TypedArray and you got its pointer by simply calling array.ArrayBuffer().Data() — then immediately go back and fix your code before anyone has noticed what a lousy engineer you are. Unless you apply the ByteOffset(), your code will cause terrible destruction when the user passes an argument produced by the subarray() method in JavaScript. Such errors are known to have caused spectacular space launch failures in the past.</p><p>And in order to avoid such regrettable disasters, a JavaScript engineer cannot be allowed to access data beyond the end of its allocated array under any circumstances, no matter how hard he tries to do so. Thus, we will also use a check typemap:</p><pre>%typemap(check)<br>(const size_t width_, const size_t height_,<br>    const std::string &amp;map_, const Magick::StorageType type_,<br>    const void *pixels_),<br>(const size_t columns_, const size_t rows_,<br>    const std::string &amp;map_,<br>    const Magick::StorageType type_, void *pixels_)<br>{<br>  if ($1 * $2 * $3-&gt;size() != _global_typed_array.ElementLength()) {<br>    SWIG_exception_fail(SWIG_IndexError,<br>      &quot;The number of elements in the TypedArray does not match &quot;<br>      &quot;the number of pixels in the image&quot;);<br>  }<br>}</pre><p>This check typemap will be applied to any method having those five arguments. It uses the previously created variable in the wrapper method and its function is to prevent size mismatches.</p><p>Note that this typemap is applied to two different cases — one where the image width is called width_ and another one where it is called columns_ — as these are the two argument names found througout ImageMagick. This is different from the previous declaration where the second expression defined a local variable — note the comma between the two expressions.</p><p>Finally, if generating TypeScript bindings, we also have to specify the new TypeScript type that the wrapper will require:</p><pre>%typemap(in)<br>  (const Magick::StorageType type_, void *pixels_)<br>&quot;Uint8Array | Uint8ClampedArray | Uint16Array | Uint32Array | &quot;<br>&quot;Float32Array | Float64Array&quot;;</pre><h3>The very hard methods — a method that returns a void pointer</h3><p>Take a look at Magick::Blob::data():</p><pre>const void *data(void);</pre><p>The ImageMagick creators definitely didn’t think in advance about JavaScript. What can we do about this nightmare?</p><p>No amount of argument massaging can produce a safe JavaScript wrapper out of this method.</p><p>The only solution is to ignore this method and to replace it with a JavaScript-friendly one:</p><pre>%ignore Magick::Blob::data() const;<br>%extend Magick::Blob {<br>  void data(void **buffer_data, size_t *buffer_len) const {<br>    *buffer_data = const_cast&lt;void *&gt;(self-&gt;data());<br>    *buffer_len = self-&gt;length();<br>  }<br>}</pre><p>We have already seen %ignore. In the case of overloaded methods, you can use it to ignore but one of the signatures — by specifying its arguments. %extend allows you to add methods to an existing C++ class. In this case we simply add a new method that uses the argout version of the nodejs_buffer.i Buffer-compatible signature that we saw earlier. This means that the JavaScript wrapper won’t take any arguments and it will instead return a Buffer. You can check nodejs_buffer.i to see how this works under the hood.</p><p>This new method calls the old one through overloading. The old one is still there — it simply won’t be exposed to JavaScript.</p><h3>Asynchronous execution</h3><p>One of the most peculiar aspects of the JavaScript language is its way of handling I/O and concurrency by using asynchronous execution. Although this is not by design — it is a reminder of its origin as a language dedicated to UI scripting — it is considered to be a very successful paradigm that allows for very easy and high performance access to parallel programming. This model of concurrency is usually called <em>Green Threads</em> or <em>CoRoutines</em>. It moves all the complexity of handling the parallel execution to the C++ engine.</p><p>SWIG Node-API allows the automatic generation of asynchronous wrappers that return a Promise and do the heavy lifting in invisible background threads managed by libuv — the Node.js low-level system library.</p><p>Particular care must be taken when enabling asynchronous execution as it bestows to the JavaScript end-user the capability of launching multiple operations — on the same object and by the same method — in parallel. It is up to the binary addon author to implement the exclusion rules governing parallel execution.</p><p>SWIG Node-API can implement a default locking, that works for most use cases. It implements mutexes that ensure that every underlying C++ object is used only by a single C++ method at a time. It is not without caveats — about which you should read in the documentation — but it works well enough for ImageMagick.</p><p>Enabling the generation of asynchronous wrappers for all classes is as simple as using:</p><pre>%feature(&quot;async:locking&quot;, &quot;1&quot;);<br>%feature(&quot;async&quot;, &quot;Async&quot;);<br>%apply SWIGTYPE  LOCK {SWIGTYPE};<br>%apply SWIGTYPE *LOCK {SWIGTYPE *};<br>%apply SWIGTYPE &amp;LOCK {SWIGTYPE &amp;};</pre><p>The first two lines enable locking and asynchronous execution globally. All asynchronous wrappers will have the same name as their synchronous counterparts with an Async suffix. Then we bring in the default locking typemaps for all types. This means that every method will try to lock all of its SWIG-exported C++ objects before proceeding with the operation.</p><p>Alas, in the case of ImageMagick this brings the generated C++ code from about 350k lines to about 700k. Having such an absolutely huge wrapper is not only impractical — its compilation also exceeds the free allotment of RAM by Github Actions. Besides, do we really need an async version of this:</p><pre>const gm = new Geometry(100, 80);<br>assert.equal(gm.width(), 100);<br>assert.equal(gm.height(), 80);</pre><p>Creating a Geometry and retrieving its dimensions are instantaneous operations. It is a helper class. Adding asynchronous handling and locking will only get in our way. A much more economical approach will be to limit the asynchronous execution and the locking to the heavy-weight classes — essentially Image and some global methods that apply processing and do I/O. These can take anywhere from a few milliseconds to several seconds and must be done without blocking the event loop. I won’t go through the exact selection of asynchronous classes in ImageMagick, but I have grouped them in AsyncClasses.i and then I have defined a macro that allows me to enable async support selectively:</p><pre>%feature(&quot;async:locking&quot;, &quot;1&quot;);<br>%define LOCKED_ASYNC(TYPE)<br>%apply SWIGTYPE  LOCK {TYPE};<br>%apply SWIGTYPE *LOCK {TYPE *};<br>%apply SWIGTYPE &amp;LOCK {TYPE &amp;};<br>%feature(&quot;async&quot;, &quot;Async&quot;) TYPE;<br>%enddef<br>%include &quot;AsyncClasses.i&quot;</pre><p>This allows me to bring them one by one by using a single statement:</p><pre>LOCKED_ASYNC(Magick::adaptiveBlurImage);<br>LOCKED_ASYNC(Magick::adaptiveThresholdImage);<br>LOCKED_ASYNC(Magick::addNoiseImage);<br>LOCKED_ASYNC(Magick::adjoinImage);<br>...</pre><p>This way the size of the final generated code is kept to reasonable levels.</p><h3>Putting it all together</h3><p>Invoke SWIG on your .i file to generate the C++ wrappers:</p><pre>swig -javascript -napi -typescript -c++ \<br>  -Ideps/ImageMagick/Magick++/lib -Ideps/ImageMagick \<br>  -DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16 \<br>  -o swig/Magick++.cxx -outdir swig src/Magick++.i</pre><p>As you can see, it has a compiler-like command-line interface. You can even pass include paths and add macros. This will produce the actual code for your addon.</p><p>This tutorial is provided in the hope that it will pave the road for porting many existing excellent C++ libraries to Node.js.</p><p>Previously, this has been a very labour-intensive process that had greatly limited the number of available options for Node.js.</p><p>Then simply add the resulting file to your binding.gyp in the sources section:</p><pre>&#39;sources&#39;: [ &#39;swig/Magick++.cxx&#39; ]</pre><p>Now you are ready to launch the build through node-gyp:</p><pre>node-gyp configure<br>node-gyp build</pre><p>I am an unemployed engineer that is currently being extorted with the French IT recruitment companies and the French judiciary about a huge sexually-motivated judicial affair involving high-level corruption in the French administration.</p><p>I use my free time to create and maintain open-source software. My main areas of interest are Node.js/V8 internals, linking C++ and JavaScript, geospatial software. I am also a paragliding pilot with a keen interest in numerical weather prediction.</p><p><a href="https://github.com/mmomtchev">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3c1a5c4a233f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Are Deep Neural Networks our first true breakthrough at reverse-engineering the human intelligence?]]></title>
            <link>https://mmomtchev.medium.com/are-deep-neural-networks-our-first-true-breakthrough-at-reverse-engineering-the-human-intelligence-3fbfe96471ce?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/3fbfe96471ce</guid>
            <category><![CDATA[philosophy-of-mind]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[philosophy]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Mon, 03 Apr 2023 15:33:00 GMT</pubDate>
            <atom:updated>2023-04-03T15:33:00.204Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aHEms7Lpg5fGkaMU" /><figcaption>Photo by <a href="https://unsplash.com/ko/@averey?utm_source=medium&amp;utm_medium=referral">Robina Weermeijer</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><em>(part of a series of a slightly philosophical stories by an AI dilettante)</em></p><p>Deep neural networks seem to have revolutionized AI. For now, I would avoid delving too much in ChatGPT — as it merits its own story — but rather at the general adoption of DNNs — which seem to have become an ubiquitous miracle cure in AI. So have we finally started gaining any real insights in how our brain works? Are DNNs our first real success at reverse-engineering ourselves?</p><p>Before continuing, let me tell you a personal story. 5 years ago I discovered electric monowheels. I live in downtown Paris and commuting has always been a huge problem — so I instantly fell in love with the concept of the monowheel. The only problem was that I had to learn to ride it. I was in my late 30s, had never skateboarded or rollerskated during my teens, and I haven’t been skiing since my early childhood. I knew this one was going to be difficult. I bought and old second-hand wheel and decided to simply give it a try on my own. The very first time I spent an hour without ever doing more than 50 cm. The next evening, after lots of failing at about 1 m, I was suddenly able to keep myself standing on it for 20 m. Half an hour later, I was riding it without falling too much. On the third day, I used it to get to work. I was stunned, because, initially, it seemed like an absolutely impossible task — than in a few hours it became a breeze. Just like that. Baffled by the experience, my next stop was Wikipedia where I started searching for motor-skills and muscle memory. It is an absolutely fascinating subject. You should probably try learning about it too. Maybe you remember how you learned to ride a bike or drive a car. In the beginning, it was a very tedious conscious process, where you were thinking hard about every step. Then — you simply stopped thinking about it and started doing it. <em>It became embedded in the hardware</em>. Often, just like for DNNs, it is a very sudden process.</p><p>So, to answer that question in the beginning, definitely yes, we do have started reverse engineering parts of our brain. It is just not the really important part — the neocortex. We have started cracking the “<em>peripherals</em>”: visual recognition, motor skills, language skills and even gaming strategy — which are mostly intuitive. Now, language skills and gaming strategy are particularly interesting since they have both a “<em>hardware</em>” first-order intuitive part — as well as a higher-order “<em>software</em>” part — and the truth is that this second part is still somewhat lacking in the current generation AI.</p><p>When 20 years ago, Deep Blue won its first victory against Kasparov, many noted that humans were still unequaled at extreme complexity — and the one example they frequently gave was Go — a game with a much higher order of complexity compared to chess — in which humans were still impossible to beat. Then there was StarCraft 2 and DOTA 2. Both video games were supposedly fine examples of solution-spaces that were impossible to explore using classical branch-and-bound exhaustive approaches. Only “<em>human creativity</em>”, and human capacity of inventing strategies could secure a win. Personally, for having played — competitively — both of these — even if I never came out of the bottom of the ranking ladders — I consider both of them to be very bad examples. True — these games are not accessible to classical algorithms that simply explore the solutions space. Predicting even 2 or 3 seconds in the future would require considering gazillions of possibilities — and one needs to think minutes in advance to have any chance of winning.</p><p>DNNs are however extremely good at precisely this kind of tasks. In fact, this is probably the type of game where a DNN is the absolutely best possible player. They simply have to learn various — not so complex — strategies and to apply them perfectly in what is a very fast-paced game where no human is ever perfect — and where one of the very characteristic differences between the entry-level amateur and the top-level professional player is his APM — actions per minute. Obviously, in these games, DNNs easily gained the upper hand without requiring the huge R&amp;D investment that IBM made for Deep Blue.</p><p>I even think that if there was a large corporation with deep pockets and a project to create an entirely mechanic top soccer player, its most difficult problem would probably be the power source — as power storage is a technology in which we are still far behind our own bodies. And of course the gargantuan task of having to analyze thousands of hours of soccer matches that are available only as analog video. DOTA 2 has a Linux API — which meant that the AI creators could focus entirely on the AI itself.</p><p>In fact, we have started understanding — and successfully copying — all <strong>those basic skills that we are able to learn to perform without thinking</strong>. We are still decades away from understanding the higher functions of the brain. Heck, it might even be centuries away. Or maybe there are no other mechanisms in the brain besides what we already know. Maybe it is simply a matter of adding even more neurons. Which is easier said than done, because ChatGPT has been trained on a — very expensive — supercomputer. From what we know, the human brain is not similar to a CPU — but rather to an FPGA. Its most distinctive advantage over DNNs is that it is fully dynamic, capable of adding neurons to existing structures and interconnecting stages on the go. <strong>But the hard truth is that we simply do not even know what there is to know for the next step.</strong></p><p>Because let’s be honest. We, the computer engineers, didn’t invent anything. Neural networks, reinforcement learning, genetic algorithms — we didn’t come up with those. Evolution produced them. Then they were discovered by the scientists. Only then, we started adapting them, filling the voids — and even — improving them — since nature has never been a perfect designer. So for there to be a significant AI breakthrough — there must be a significant neurology breakthrough first. This is not coming out of a computer lab for sure — it simply has to come from medical science.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3fbfe96471ce" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using numpy and pandas in Node.js]]></title>
            <link>https://mmomtchev.medium.com/using-numpy-and-pandas-in-node-js-88fe30080938?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/88fe30080938</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[pandas]]></category>
            <category><![CDATA[scipy]]></category>
            <category><![CDATA[numpy]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sun, 02 Apr 2023 13:44:38 GMT</pubDate>
            <atom:updated>2023-04-02T13:44:38.931Z</atom:updated>
            <content:encoded><![CDATA[<p><em>If you are an exclusively-Node.js shop and you suddenly need that Python library which has no Node.js equivalent — then I have the solution for you</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LgqMiVqeQtMqd7q1" /><figcaption>Photo by <a href="https://unsplash.com/es/@sebastiandumitru?utm_source=medium&amp;utm_medium=referral">Sebastian Dumitru</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>About 4 years ago, while working on a typical CPU-heavy geometry optimization problem in Python, I decided to give Node.js a try. Until that moment, I was subscribing to the commonly held opinion that Node.js excelled at I/O, while serious number crunching was firmly inside Python’s territory. No matter how hard I tried, I was never able to beat the Node.js implementation in Python. Both CPython and pypy failed behind and failed hard. Alas, the simplicity and elegance of the Python’s interpretators could not compete with the JIT juggernaut that V8 has become.</p><p>Now, don’t get me wrong — Python is still the better language. In the short history of technology, JavaScript is but one of the so many examples of an inferior technology winning over a superior one — VHS, x86, MS-DOS — you don’t have to look hard to find dozens of others. Momentum and installed base are very important when it comes to core technologies — and the Allmighty Web has spoken — JavaScript will be the new general-purpose language for the masses. Like it or not.</p><p>However, despite Node.js gaining a huge amount of traction during the last few years, there is still one field where it is lagging probably a whole decade behind Python —and that is precisely number crunching. Even if you have never used Python, you have probably heard about scipy, numpy and pandas and you know at least someone who regularly uses them. They are that good and that widely used.</p><p>In the Node.js world we have scijs and now @stdlib — however scijs covers about 5% of the numpy features and @stdlib is a gargantuan task that is still a work in progress. Even if it is growing fast, the community behind it is still orders of magnitude smaller than the scipy community.</p><p><strong>Now, if you want to do serious number crunching in the browser — there are still no alternatives. </strong>@stdlib is the only way to go. However, if you are targeting only Node.js — then Python has a solution for you — its embedded version can be included anywhere. Python can be integrated with any other language and its integration with Node.js — another somewhat similar interpreted language — can be absolutely flawless.</p><h3>Enough talk, let’s get to the point</h3><pre>import { pymport } from &#39;pymport/profixied&#39;;<br>const np = pymport(&#39;numpy&#39;);<br><br>const a = np.arange(15).reshape(3, 5);<br>const b = np.ones([2, 3], { dtype: np.int16 });</pre><p>Eh? What is the catch? Too good to be true? What about performance? How does it work? Is this a transpiler? What about the binary code? Does it support <em>all</em> Python?</p><p>Yes, it supports all Python and there is no catch. And in fact, the explanation is very mundane. Node.js runs the JS code, Python runs the Python code. Python can be loaded as a shared library and has a very good and mature embedding API. And Node.js has a very good addon API. Both languages being somewhat similar, the integration is nearly perfect. It is also remarkably simple: each Python object is proxied in JS — ie there is a corresponding JS Proxy object that holds a reference to it.</p><p><strong>pymport simply implements Python references in Node.js.</strong> Nothing more, nothing less. JS objects are managed by the V8 GC. Python objects are managed by the Python GC — but are locked in place while JS is using them.</p><p>In this example a is a JS object of PyObject type that holds a numpy array. np is a PyObject of a Python module type. Call toJS() on a Python reference, and you will obtain a JS copy of the underlying object:</p><pre>import { pymport } from &#39;./proxified/index.js&#39;;<br>const np = pymport(&#39;numpy&#39;);<br>const list = np.arange(15).reshape(3, 5).tolist();<br>// list is a reference to a Python list<br>// it can be converted to a JS array<br>console.log(list.toJS());<br>// or you can use Python&#39;s own str() built-in<br>// .toString() is wired to it<br>console.log(list.toString());</pre><p>Performance is as good as the slowest part — Node.js won’t run slower because you loaded Python — and Python code won’t run slower because it is embedded in Node.js.</p><p>There is a certain memory price to pay though. You load and run both interpreters. As Python is numbers of magnitude lighter the Node.js, it is not that much of a problem. The only thing one must be careful with are the proxy objects:</p><pre>// This creates a single JS proxy object<br>// a is a reference to a Python numpy object (overhead: 1 reference)<br>const a = np.arange(65536);<br>// list is a reference to a Python list object (overhead: 1 reference)<br>const list = a.tolist();<br>// refArray is a JavaScript array of references to Python numbers<br>// The memory overhead is huge - 65536 separate references<br>const refArray = a.map((x) =&gt; x);<br>// jsArray is a JavaScript array of numbers, we are now fully in JS land<br>const jsArray = a.toJS();</pre><p><em>But </em><em>numpy makes heavy use of operator overloading that does not exist in JavaScript?</em></p><p>Python has the solution for you, every overloaded function can be called explicitly:</p><pre>// living simply<br>const sum = a.__add__(b);<br>// or a slightly more snobbish approach<br>const op = pymport(&#39;operator&#39;);<br>const sum = op.add(a, b);</pre><p><em>But what about functions and exceptions?</em></p><p>Absolutely natural:</p><pre>const fn = (x, y) =&gt; {<br>  return x.__add__(y);<br>};<br>try {<br>  const a = np.fromfunction(fn, [2, 3]);<br>} catch (e) {<br>  console.error(e);<br>}</pre><p><em>And if I already have an existing </em><em>scijs Node.js application and I want just one feature of </em><em>numpy that does not exist in </em><em>scijs?</em></p><p>You are welcome, numpy arrays can be converted to scijs without even copying the backing-store:</p><pre>// b is a scijs ndarray<br>const ref = np.frombuffer(PyObject.memoryview(b.data),<br>  {dtype: np.int32}).reshape(b.shape);</pre><p>And pip3? Where does numpy come from in Node.js?</p><p>It comes from pip3 of course. When installing pymport you have two choices: use the built-in Python interpreter that comes with it — if you simply type npm install pymport it will quietly sneak itself into node_modules/pymport/lib/binding. You will have to call it npx pympip3, but it will be otherwise indistinguishable from a normal pip3. I have tried as hard as I could to make this work on all OSes and all environments.</p><p>Or — you can opt for the advanced installation. Install Python yourself, then rebuild the pymport C++ code to use your own Python installation. npm install pymport --build-from-source will take you on this route. In this case, it will be up to you to manage this installation and add whatever modules you need to it.</p><p>For a more detailed documentation and more examples — including expressing some extreme pandas statements, your starting point should be <a href="https://github.com/mmomtchev/pymport/wiki">https://github.com/mmomtchev/pymport/wiki</a> — the pymport wiki.</p><p><a href="https://github.com/mmomtchev/pymport">GitHub - mmomtchev/pymport: Use Python libraries from Node.js</a></p><p>If you are interested in its internals — or should I dare to ask — want to even contribute — you can find a brief introduction in the wiki.</p><p>I am the world’s first unemployed IT engineer — as I am currently embroiled in an absolutely huge judicial/sex scandal that is being kept under wraps because of its political implications in France. I use this time to work on various open-source projects, related to Node.js/V8 internals, geospatial software as well as everything else that comes my way.</p><p><a href="https://github.com/mmomtchev">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=88fe30080938" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Are we on the verge of a major social and technological change driven by AI?]]></title>
            <link>https://mmomtchev.medium.com/are-we-on-the-verge-of-a-major-social-and-technological-change-driven-by-ai-f5af0e28ce3e?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/f5af0e28ce3e</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[philosophy]]></category>
            <category><![CDATA[chatgpt]]></category>
            <category><![CDATA[stable-diffusion]]></category>
            <category><![CDATA[philosophy-of-mind]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sun, 02 Apr 2023 13:40:38 GMT</pubDate>
            <atom:updated>2023-04-02T13:40:38.508Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R4b0wdgDFVuilXvzcCGFSQ.jpeg" /></figure><p>Even if I have been living under a rock for quite some time now, it has been absolutely impossible to not be aware that a new version of the ChatGPT bot has been recently released.</p><p>In between all the hype, the raving reviews, there has also been a steady influx of stories by various bloggers and pundits about “<em>coding coming to an end, sometime in the next 5 years</em>”.</p><p>If you are currently finishing high-school and having second thoughts about choosing IT, be assured, coding is not coming to an end, at least not before the current generation of students retire. It <em>might </em>come an end one day, but we won’t be here to see it for sure.</p><p>I have 30 years of software development experience and I admit that I do not know anything about AI. It is one of those technologies that intrigues me — as much as everyone else in fact — but at the end, it never really captured my attention span long enough for something meaningful to come out of it.</p><p><em>In fact, I simply don’t like it.</em></p><p>First of all, progress in AI has always been a very bumpy road. Software engineering is a field that has experienced a tremendous growth and progress in the last 50 years. And every decade has built upon the achievements of the previous one. When it comes to AI, however, every decade has brought a new fashion — but not necessarily progress — I mean linear progress of the type that builds upon everything that came before it.</p><p>Once every 10 years, there will be that new spectacular AI technology that will make me rethink my choice and start reading about its inner workings. (<em>I would like to specially thank the </em><a href="https://stability.ai/blog/stable-diffusion-public-release"><em>Stable Diffusion</em></a><em> team for publishing their amazing work and making it accessible to the average layman)</em>. Every 10 years, there will be some new AI breakthrough — that will make me reconsider this stance — until I sink a few days into it. And invariably, every 10 years I will discover that what I am reading about is an <em>absolute revolution in making it appear</em> that it was AI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RbwqrFICo5DatCr7F4UmWA.png" /><figcaption>ChatGPT trying to assemble an URL out of <a href="https://github.com/huggingface">https://github.com/huggingface</a> — the ChatGPT repository — and node-fetch— a very commonly requested package for Node.js</figcaption></figure><p>The previous time I did this was during the mass hysteria surrounding Deep Blue. Since, computers have finally mostly surpassed humans when it comes to chess, but there remain many other games in which humans are simply unbeatable.</p><p>AI is a very difficult technology. While many scientific discoveries were made by chance, no engineer ever designed a product that he did not understand. Well, Evolution, Our Creator (<em>the author subscribes to a modern, evolution-based religion</em>) did so — but it took hundreds of millions of years. You know, if you give an infinite number of monkeys and infinite number of typewriters, given an infinite time, one of them will eventually produce the complete works of Shakespeare. We, simple mortal engineers however, we don’t have infinite time or infinite resources. We are limited to designing the systems <em>we understand</em>. And when it comes to human intelligence, it is remarkable how little we currently understand. Especially when compared to the rest of our bodies.</p><p>Now repeat after me: <em>we won’t design an AI before understanding the inner workings of the human brain</em>. Not before neurology and psychiatry, the two scientific fields tackling this problem — from the two opposing sides — have advanced to the point they can merge completely into one full and complete understanding of ourselves.</p><p>Now, many AI technologies have been based on our limited understanding of the human brain: neural networks, reinforcement learning, fuzzy logic, even the genetic algorithms come from what we have seen from nature. However our current understanding can probably be compared to the first European explorers touring the Chinese coast without disembarking from their ship, then coming back to Europe trying to recreate Chinese cuisine from a smell they picked up along a way. True, it starts <em>smelling </em>like an AI. But the similarities stop here. Because you don’t even know what exactly they were cooking.</p><p>If you have been following chess-playing computers, you will know how different the computer playing style is. It is largely an exercise in raw computing power and optimization techniques. Same goes for Alpha Star — if you are a StarCraft fan you should definitely watch some of its games. While very good, it is definitely not human-like. The same applies to the current generation of text to image algorithms — a field that has progressed tremendously in the last five years — go read about them and you will understand why they tend to produce humans with three arms or birds with two heads and why these problems will likely persist for some time.</p><p>So, don’t hold your breath for the end of coding. At some point, ChatGPT, or some other related technology, could probably replace Google, but do not expect much more to come out of it. And coding is not coming to an end anytime soon.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f5af0e28ce3e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bundling React Native for the Web with Webpack and TypeScript]]></title>
            <link>https://mmomtchev.medium.com/bundling-react-native-for-the-web-with-webpack-and-typescript-ba42db25584?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/ba42db25584</guid>
            <category><![CDATA[webpack-5]]></category>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sun, 02 Apr 2023 13:29:36 GMT</pubDate>
            <atom:updated>2023-04-02T13:29:36.364Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently I decided to make a web demo for my React Native component for creating preferences screens:</p><p><a href="https://github.com/mmomtchev/react-native-settings-screen">GitHub - mmomtchev/react-native-settings-screen: React Native Universal Settings Screen</a></p><p>I, like many other people, was under the impression that React Native applications could easily be bundled for the web. After all, react-native-web is now an official part of React Native.</p><p>Turns out, this is easier said then done. There is of course expo, which can make it seem easy, but if you need something less standard, then things can get very rough very quickly.</p><p>There is of course the documentation by the original author of <em>react-native-web</em> — but if you browse his issues — and you inevitably will — looking for clues about arcane <em>webpack </em>error messages — you will find tons of issues and you can even trace the growing annoyance of its developer, forced to provide technical support for <em>webpack </em>— over those issues for the last two years — by now he is simply immediately closing all issues about <em>webpack </em>bundling problems. His message is very clear: raw bundling of React Native apps for the Web is not for the faint hearted and if you do not feel like browsing through tons of source code and eventually writing a <em>webpack </em>loader of your own — then better go for expo. It is there for a reason.</p><p>Still, if you need some special features, like a web demo of a component in my case, raw <em>webpack </em>bundling is the only way to go.</p><p>As I feel that I have accumulated some precious experience in this matter, I decided to share it with you.</p><p>The rest of this story will assume you that you have read the original guide at <a href="https://necolas.github.io/react-native-web/docs/">https://necolas.github.io/react-native-web/docs/</a>, <strong><em>then you did exactly how its author said</em></strong>, and nevertheless you got some weird errors.</p><h3>Here be dragons: <em>webpack </em>error messages</h3><p><em>webpack </em>error message when bundling React Native come essentially in two flavors.</p><h3>Errors about missing components</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f8bdae4edfeb7fd069d6b29476409407/href">https://medium.com/media/f8bdae4edfeb7fd069d6b29476409407/href</a></iframe><p>What is key to understand here is how <em>react-native-web</em> works — it replaces the <em>react-native</em> module with a <em>react-native-web</em> module through an alias — that can be resolved by <em>webpack </em>itself, its <em>babel </em>plugin or the TypeScript loader if you need it. The original configuration relies on <em>webpack </em>— better stick to it. Avoid <em>ts-loader</em>, it is far too complex and will require separate configurations for your code and for the React Native modules— <em>babel </em>is the way to go if you use TypeScript.</p><p>So far so good. The only thing <strong>necolas </strong>didn’t tell you is that his setup requires an ideal world and we do not live in an ideal world. <strong><em>Many 3rd party react-native modules do not play by the rules.</em></strong> Instead of including the react-native libraries through their official entry points, they deep link inside the source. This way of linking is not, and cannot, be supported by <em>react-native-web</em>.</p><p>So the key thing that you should notice in this error output is react-native in the call stack. This error message contains a call stack — at the bottom is your code, then it calls @react-navigation, then you enter react-native. This is exactly what should not happen. <strong>First clue: If you have react-native in your stack, this is not good — you should have react-native-web</strong>. <em>@react-navigation</em> is deep-linking into <em>react-native</em> and you must break this chain somewhere.</p><p>Usually, in this case, there will be a component somewhere in this stack that has an alternative implementation in <em>react-native-web</em> — you will have to find which.</p><p>A web alternative of Utilities/Platform is provided by <em>react-native-web</em> but <em>webpack </em>cannot find it because of the deep link.</p><p>An even better alternative is to simply replace SafeAreaView from <em>react-native-safe-area-context</em> with SafeAreaView from <em>react-native-web</em>, breaking the chain in the middle. As always, the problem lies in a 3rd party module.</p><p>A <em>webpack </em>alias can solve this problem:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/065facb56daab52c7c693eb584962e43/href">https://medium.com/media/065facb56daab52c7c693eb584962e43/href</a></iframe><h3>Errors about invalid syntax</h3><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ef4130b6e740dc21ebdf8027c37e4dc8/href">https://medium.com/media/ef4130b6e740dc21ebdf8027c37e4dc8/href</a></iframe><p><em>react-native</em> uses some non-standard JavaScript features and one of them is Flow annotations which is the culprit here. You must make sure that all of these files are transpiled by <em>babel</em>. As you followed the original guide exactly how its author said, you already have module:metro-react-native-babel-preset. Now you just have to be absolutely sure that it applies to all those 3rd party modules:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9e2e1461aef5b703a4b31d745245f765/href">https://medium.com/media/9e2e1461aef5b703a4b31d745245f765/href</a></iframe><p>Notice all the additional includes. These are 3rd party modules not covered by the configuration if simply copied and pasted it from the original guide.</p><h3>The big roadblock</h3><p>Ok, by now you have a perfectly working webpack.config.js, but guess what, it is still not enough. You have hit the big road block — SafeAreaCompat.</p><p>The implementation in <em>react-native-web</em> is not complete and does not support Navigators.</p><p>Luckily the folks who made expo for us (frankly they deserve paying them a subscription) have been kind enough to open-source their implementation and we simply borrow it:</p><pre>npm install — save-dev expo-dev-menu</pre><p>And then one final webpack alias:</p><pre>&#39;react-native-safe-area-context&#39;: &#39;expo-dev-menu/vendored/react-native-safe-area-context/src&#39;</pre><p>If you have reached so far, then you probably have it very close to working. I have tried to provide you not only the ready to use answers but also the methodology I used to get so far. Once again, manually bundling React Native for the Web is not an easy task and, depending on your app, may or may not work.</p><p>I am an unemployed engineer with a strong interest in Node.js, React and GIS who is in the middle of a huge sex scandal with political implications in France. Should you require help in setting up React Native projects for the web, I will be more than willing to help you.</p><p><a href="https://github.com/mmomtchev">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ba42db25584" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solve the missing .mvt/.pbf files problem when serving vector tiles from a standard HTTP server]]></title>
            <link>https://mmomtchev.medium.com/solve-the-missing-mvt-pbf-files-problem-when-serving-vector-tiles-from-a-standard-http-server-c542c85e5d45?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/c542c85e5d45</guid>
            <category><![CDATA[gdal]]></category>
            <category><![CDATA[mvt]]></category>
            <category><![CDATA[pbf]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Tue, 21 Feb 2023 16:48:55 GMT</pubDate>
            <atom:updated>2023-02-21T16:49:39.302Z</atom:updated>
            <content:encoded><![CDATA[<h3>Solve the missing .mvt/.pbf files problem when serving vector tiles from a standard HTTP server</h3><h4>Create a small empty .pbf or empty .mvt file to avoid 404 client errors</h4><p>Many GIS tools, including GDAL, can produce MVT tiles from vector data. As the MVT specification does not cover the distribution of the tiles, it is not clear what should be the correct behavior when a given tile at a given resolution does not contain any features.</p><p>Currently, GDAL will skip those tiles:</p><pre>mmom@mmom-workstation:~/velivole/dev/www/tiles/place$ ls 0/10/525<br>106.pbf  107.pbf  109.pbf  111.pbf  112.pbf  125.pbf<br>130.pbf  131.pbf  153.pbf</pre><p>When those tiles are served to an Openlayers client, it will request them and it will receive a 404. If you have an error handler, it will get triggered, otherwise it will more or less ignore it. Other clients may react in a different way.</p><p>As the concept of normal errors bothers me, I decided to find a cleaner solution.</p><p>Alas, there is no way to modify GDAL to create empty tiles without rewriting major parts of the MVT code. Besides, this behavior is not that bad since it allows to avoid having gazillions of empty files — especially when using very high zoom levels.</p><p>The solution that I have found to is to generate a single .pbf file with no features and to use it in the 404 handler of the web server — by configuring it as a custom error page. It is a simple solution that combines all advantages at once.</p><p>Alas, GDAL can not generate an empty .pbffile because of its optimization, but you are free to use mine:</p><p><a href="https://velivole.b-cdn.net/tiles/empty.pbf">https://velivole.b-cdn.net/tiles/empty.pbf</a></p><p>for the gzip precompressed version, or:</p><p><a href="https://velivole.b-cdn.net/tiles/uncompressed_empty.pbf">https://velivole.b-cdn.net/tiles/uncompressed_empty.pb</a>f</p><p>for the raw version.</p><p>It is a completely universal file that does not depend on the projection or the tilegrid.</p><p>The file being very small, the gzipped version is actually larger than the raw one, but it won’t interfere with your custom HTTP headers governing precompressed .pbf files.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c542c85e5d45" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[C++ Class Inheritance with Node-API (and node-addon-api)]]></title>
            <link>https://mmomtchev.medium.com/c-class-inheritance-with-node-api-and-node-addon-api-c180334d9902?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/c180334d9902</guid>
            <category><![CDATA[cpp]]></category>
            <category><![CDATA[inheritance]]></category>
            <category><![CDATA[node]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Sat, 14 Jan 2023 13:20:22 GMT</pubDate>
            <atom:updated>2023-01-14T13:20:22.783Z</atom:updated>
            <content:encoded><![CDATA[<p>Including deriving a C++ class from EventEmitter</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4QpdBM516FvHCha1B67-Yw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@alinnnaaaa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Alina Grubnyak</a> on <a href="https://unsplash.com/s/photos/interconnections?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>All the example code can be found in this repository:</p><p><a href="https://github.com/mmomtchev/napi-inherit">GitHub - mmomtchev/napi-inherit: An example of shared C++/JS inheritance with N-API</a></p><p>During the last few years most major Node.js addons switched from the older NAN (Native Abstractions for Node.js) to the newer Node-API. It offers numerous advantages: fully compatible binary ABI across different Node.js versions, native C++ class and object semantics — including constructors and destructors — and full compatibility between C++ and JS exceptions. During those years, only one major problem has remained unsolved and that is the question of C++ class inheritance — which used to work very well in NAN — and it is still not supported in Node-API. For example, the largest addon that I maintain — the GDAL bindings for Node.js — makes extensive use of C++ class inheritance — and until recently I have been unable to come up with a satisfying solution.</p><p>I will very briefly sum up the reasons why this problem has remained stalled for the last 5 years since it was first discussed in an issue:</p><p><a href="https://github.com/nodejs/node-addon-api/issues/229">Substitute for Inherit · Issue #229 · nodejs/node-addon-api</a></p><p>I won’t go through all the details of why this can’t be done in Node-API — you can read the rest — but it all comes down to V8 being unable to retrieve the v8::FunctionTemplate used for creating a v8::Function. As Node-API aims to be an universal and future-proof API for interfacing with any JS engine — even if V8 is still the only one for which there is full support, its project team refuse to extend it to provide access to a V8-specific feature. NAN, which was tied to the V8 engine did not have this problem as it simply allowed access to <a href="https://v8docs.nodesource.com/node-18.2/d8/d83/classv8_1_1_function_template.html#ac54d88e6fdc00872babe5c91515b9dce">v8::FunctionTemplate::Inherit</a> . However, even with NAN, extending from EventEmitter was not possible — as there was no way to retrieve its v8::FunctionTemplate .</p><p>There are various approaches to solving the problem — none of them are perfect — and you can read about some of them in the issue. Throughout this story, I will show you mine which I think is the least bad.</p><h3>The requirement</h3><ul><li>Use only Node-API, no direct V8 calls which will result in loss of binary compatibility</li><li>In JS, the classes must appear inherited for the end-user when using instanceof</li><li>The C++ implementation can share method code in a base class</li><li>Support inheriting from EventEmitter (ie inheriting from a JS class)</li></ul><h3>Case 1: Inheriting a C++ class from another C++ class and exporting the class hierarchy to JavaScript</h3><p>As you know, in Node-API all C++ classes that will be exported to JS must inherit from ObjectWrap&lt;T&gt; which is a <a href="https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern">CRTP</a> class.</p><p>If we want to avoid the dreaded Illegal invocation V8 error, we will have to have different template parameters in the superclass and in the subclass. There is only way to make it and this is to declare another CRTP class:</p><pre>template &lt;class T&gt; class Base: public Napi::ObjectWrap&lt;T&gt; {<br>public:<br>  Base(const Napi::CallbackInfo &amp;);<br>  virtual ~Base(){};<br>  <br>  static Napi::Function GetClass(Napi::Env);<br><br>  // shared methods go here<br>  virtual Napi::Value Do(const Napi::CallbackInfo &amp;);<br>  Napi::Value Tell(const Napi::CallbackInfo &amp;);<br>};</pre><p>This will be our Base class. It will hold all the members that will be shared between the subclasses. Now, the problem is that this class cannot be instantiated, so will have to create a special leaf class that we will be using when instantiating Base :</p><pre>class BaseInstance: public Base&lt;BaseInstance&gt; {<br>public:<br>  using Base::Base;<br>  virtual ~BaseInstance(){};<br>};</pre><p>We can still export this class to JavaScript under the name Base if we desire so. When we want to extend Base we will be using Base , and when we want to instantiate it — we will be using BaseInstance . Exporting the class to JS is straight-forward:</p><pre>template &lt;class T&gt; Napi::Function Base&lt;T&gt;::GetClass(Napi::Env env) {<br>  return DefineClass(env, &quot;Base&quot;,<br>                     {<br>                         InstanceMethod(&quot;do&quot;, &amp;Base::Do),<br>                         InstanceMethod(&quot;tell&quot;, &amp;Base::Tell)<br>                     });<br>}</pre><p>And then when registering the module we can simply do:</p><pre>exports.Set(&quot;Base&quot;, BaseInstance::GetClass(env));</pre><p><em>Note that we name it </em><em>Base but we actually call </em><em>BaseInstance .</em></p><p>Let’s now extend this class. Create another leaf class that will hold the extended methods, nothing too fancy here:</p><pre>class Extended : public Base&lt;Extended&gt; {<br>public:<br>  Extended(const Napi::CallbackInfo &amp;);<br>  virtual ~Extended(){};<br><br>  static Napi::Function GetClass(Napi::Env);<br>  <br>  // This method overrides the one in the base class<br>  virtual Napi::Value Do(const Napi::CallbackInfo &amp;);<br>};</pre><p>The secret of this recipe is the way we register the subclass in V8:</p><pre>Napi::Function Extended::GetClass(Napi::Env env) {<br>  return ObjectWrap&lt;Extended&gt;::DefineClass(<br>      env, &quot;Extended&quot;,<br>      {<br>          // Override this method<br>          ObjectWrap&lt;Extended&gt;::InstanceMethod(&quot;do&quot;, &amp;Extended::Do),<br>          // Inherit this method<br>          ObjectWrap&lt;Extended&gt;::InstanceMethod(&quot;tell&quot;, &amp;Base::Tell),<br>      });<br>}</pre><p>The C++ inheritance is now set up. We need just one last step to make instanceof in JavaScript work properly and this is accomplished by using Object.setPrototypeOf :</p><pre>Napi::Function ClassBase = BaseInstance::GetClass(env);<br>Napi::Function ClassExtended = Extended::GetClass(env);<br>exports.Set(&quot;Base&quot;, ClassBase);<br>exports.Set(&quot;Extended&quot;, ClassExtended);<br>Napi::Function setProto = env.Global()<br>                              .Get(&quot;Object&quot;)<br>                              .ToObject()<br>                              .Get(&quot;setPrototypeOf&quot;)<br>                              .As&lt;Napi::Function&gt;();<br>setProto.Call({ClassExtended, ClassBase});<br>setProto.Call({ClassExtended.Get(&quot;prototype&quot;),<br>                ClassBase.Get(&quot;prototype&quot;)});</pre><p><em>In this example, as through-out the rest of the tutorial I won’t be making any error-checking — it is highly recommended that you enable C++ exceptions if you intend to do this too.</em></p><p>We retrieve the V8 method Object.setPrototypeOf and then we setup the JS prototype chain to make the two classes appear inherited. Voila, we are done.</p><p>These two classes are almost indistinguishable from two JS classes inherited by using extends in JavaScript. The only — very minor — difference is that when calling extended.tell() we will invoke Extended.prototype.tell() and not Extended.prototype.__proto__.tell()which is the same as Base.prototype.tell() . In our case the subclass will appear to have its own tell() method and the redirection to the base class method will happen at the C++ level.</p><h3>Case 2: Extending EventEmitter</h3><p>The other frequently encountered pattern is extending Node.js’ EventEmitter so that the C++ object can be compatible with the event API.</p><p>This one is a little bit uglier since we will have to call require from C++ —as EventEmitter is not defined unless one imports its definition.</p><p>There is nothing special about the declaration of the class itself, except that it will have to manually hold a reference to its JS parent:</p><pre>class MyEmitter : public Napi::ObjectWrap&lt;MyEmitter&gt; {<br>public:<br>  MyEmitter(const Napi::CallbackInfo &amp;);<br><br>  void Ping(const Napi::CallbackInfo &amp;);<br><br>  static Napi::Function GetClass(Napi::Env);<br><br>private:<br>  // This is the quirk<br>  static Napi::FunctionReference *EventEmitter;<br>};</pre><p>When initially setting up the class, this reference must be initialized:</p><pre>Napi::Function MyEmitter::GetClass(Napi::Env env) {<br>  Napi::Function self =<br>      DefineClass(env, &quot;MyEmitter&quot;,<br>                  {<br>                      InstanceMethod(&quot;ping&quot;, &amp;MyEmitter::Ping),<br>                  });<br><br>  // require from C++ needs some acrobatics<br>  // (the ugly part is in the JS file)<br>  Napi::Function require = env.Global()<br>                          .Get(&quot;require&quot;).As&lt;Napi::Function&gt;();<br>  Napi::Function ee = require.Call({Napi::String::New(env, &quot;events&quot;)})<br>                          .ToObject()<br>                          .Get(&quot;EventEmitter&quot;)<br>                          .As&lt;Napi::Function&gt;();<br><br>  // This is the visible inheritance<br>  Napi::Function setProto = env.Global()<br>                                .Get(&quot;Object&quot;)<br>                                .ToObject()<br>                                .Get(&quot;setPrototypeOf&quot;)<br>                                .As&lt;Napi::Function&gt;();<br>  // Same approach as the C++/C+ inheritance<br>  // This makes instanceof work<br>  setProto.Call({self, ee});<br>  setProto.Call({self.Get(&quot;prototype&quot;), ee.Get(&quot;prototype&quot;)});<br><br>  // Keep a static reference to the constructor<br>  MyEmitter::EventEmitter = new Napi::FunctionReference();<br>  *MyEmitter::EventEmitter = Napi::Persistent(ee);<br>  return self;<br>}</pre><p>Now when creating object of this class, there will be no one to call the superclass constructor for you — remember that you are extending across the JS/C++ boundary — and neither the C++ compiler neither V8 know anything about this. You will have to do this yourself:</p><pre>MyEmitter::MyEmitter(const Napi::CallbackInfo &amp;info) : ObjectWrap(info) {<br>  // Call the super class constructor<br>  MyEmitter::EventEmitter-&gt;Call(this-&gt;Value(), {});<br>}</pre><p>Your JS to C++ inheritance is now set up. When accessed from JS, MyEmitter will be next to indistinguishable from a JS class extended from EventEmitter . There is only one very ugly last caveat, you will have to add this line in the beginning of your JS code:</p><pre>globalThis.require = require;</pre><p>This will allow you to easily retrieve require from C++.</p><p>Calling your JS superclass methods from C++ will also be manual and it will need to go through V8. Here is an example of calling super.emit(&#39;data&#39;, &#39;pong&#39;) :</p><pre>void MyEmitter::Ping(const Napi::CallbackInfo &amp;info) {<br>  Napi::Env env = info.Env();<br>  Napi::Object self = this-&gt;Value().ToObject();<br>  Napi::Function emit = self.Get(&quot;emit&quot;).As&lt;Napi::Function&gt;();<br>  emit.Call(this-&gt;Value(),<br>            {Napi::String::New(env, &quot;data&quot;),<br>            Napi::String::New(env, &quot;pong&quot;)});<br>}</pre><p>And of course, you could cache the first three lines in order to do this initialization only once.</p><p>My name is Momtchil Momtchev and I am an unemployed engineer with over 20 years of experience in the IT industry — mostly networking and low-level system engineering. For the last 6 years I have focused mostly on Node.js internals. I develop and maintain a number of binary Node.js addons.</p><p>I am at the center of a huge judicial scandal after an employer tried intervening in my sex life with the help of the French police, then tried extorting me to back off from suing him because it would expose his own problem. The affair has continued for many years now and has affected more than one open-source project.</p><p><a href="https://github.com/mmomtchev/">mmomtchev - Overview</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c180334d9902" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Performance considerations when using and abusing functions in JavaScript/V8]]></title>
            <link>https://mmomtchev.medium.com/performance-considerations-when-using-and-abusing-functions-in-javascript-v8-8be824f858cd?source=rss-fba99286a61------2</link>
            <guid isPermaLink="false">https://medium.com/p/8be824f858cd</guid>
            <category><![CDATA[v8-engine]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[typedarray]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Momtchil Momtchev]]></dc:creator>
            <pubDate>Thu, 31 Mar 2022 15:06:57 GMT</pubDate>
            <atom:updated>2022-03-31T15:06:57.580Z</atom:updated>
            <content:encoded><![CDATA[<p><em>(even if there is no such thing as a free lunch, some meals are definitely cheaper than others)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1EDmqWs4Ctb7wB03xwR4ew.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vnHbc6g9GiwXhP40PCYU_A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@rollelflex_graphy726?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">sk</a> / <a href="https://unsplash.com/@jackywatt?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jacky Watt</a> on <a href="https://unsplash.com/s/photos/burger-king?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>This article is the sequel of:</p><p><a href="https://mmomtchev.medium.com/in-2021-is-there-still-a-huge-performance-difference-between-javascript-and-c-for-cpu-bound-8ff798d999d6">In 2021, is there still a huge performance difference between JavaScript and C++ for CPU-bound…</a></p><p>If you have never read it, you should probably start with it.</p><p>Recently, while working on <strong><em>ExprTk.js</em></strong> I found that in some cases, especially when using floating point numbers, <em>scijs/cwise</em> made V8 produced a remarkably fast code from one particular function:</p><p>const fn = (x) =&gt; x*x + 2*x + 1</p><p>I couldn’t resist, so I disassembled its output and I was, once again, amazed by its remarkable efficiency. Since I already had a previous story where I had clearly underestimated its performance, I decided to right that wrong and publish a sequel.</p><p>While I was at it, I also decided to examine one very common dilemma every JavaScript developer has faced, namely the eternal question of is using aforEach loop slower than a normal one and if so — why.</p><h3>Momtchil Momtchev on Twitter: &quot;forEach was the JS way for quite some time, then they told us that the extra function call was expensive, then they told us that the new V8 was very good at inlining, then they started making us vote... 😀I guess it comes down to the language you were using the most before JS / Twitter&quot;</h3><p>forEach was the JS way for quite some time, then they told us that the extra function call was expensive, then they told us that the new V8 was very good at inlining, then they started making us vote...</p><p>So, first things first, the code.</p><p>Let’s examine the various functions:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/681115d5272762100307776d9ccd905f/href">https://medium.com/media/681115d5272762100307776d9ccd905f/href</a></iframe><p>Let’s examine the various functions.</p><p>Old developers with lots of experience with compiled languages will immediately tell you that <strong>#1</strong>, the direct approach is surely the best one. The <strong>#2</strong> help isn’t really needed — the compiler will optimize away the multiple memory accesses for sure. And calling a function, <strong>#3</strong>, is a good test if your compiler has an excellent inline optimizer. If you are not aware of the subtle differences in <strong>#4/#5</strong>, you should probably think that these are equivalent to <strong>#3</strong>. What about <strong>#6</strong> and <strong>#7</strong>? Every time we ask the V8 gurus, we get a different answer, so probably it is time to finally check this for ourselves.</p><p>Ok, lets see the results now.</p><p>….ta-da…</p><p>…and the winner is…</p><pre>#1 direct: 6.396ms<br>#2 direct with help: 4.409ms<br>#3 with function: 5.076ms<br>#4 with function and variable creation: 235.577ms<br>#5 with function and variable creation: 15.278ms<br>#6 with map: 12.399ms<br>#7 with forEach: 10.519ms<br>#8 two slices: 6.998ms<br>#9 two slices with cheese: 7.856ms</pre><p>Well, turns out V8 is remarkably good at some tasks, but still needs some help for other tasks. This one should definitely end on a post-it on one of the V8 developers’ screen. <strong>#2</strong> vs <strong>#1</strong> should be automatic. It will be even faster as it will avoid the creation of a dynamic variable on the stack.</p><p><strong>#3</strong> is where we discover the (almost) free lunch in JS — functions in JS are a dime a dozen. You can safely use and abuse them.</p><p>If no one ever told you that you should never use for..inwith an <em>Array </em>— after looking at <strong>#4</strong>, now you will remember it. It was made for <em>Objects </em>and it has many problems, including not guaranteeing order, and also creating temporary <em>Arrays</em>.</p><p>And of course, <strong>#5</strong>, <strong>#6</strong> and <strong>#7</strong>. Now it is official — forEach<em> </em>should stop blaming the function call for what is clearly its own fault. All these methods of <em>Array </em>traversal are suboptimal and should be avoided when possible. In V8, a simple for loop will be directly compiled, while these rely on more complex builtin implementations written in <em>Torque</em>. for..offor example supports using the <em>Iterator </em>protocol of the underlying object and can be used with every <em>Object </em>that implements it. These methods do not have any added value when used with an <em>Array</em>.</p><p><strong>#8</strong> and <strong>#9</strong> show the dynamic compilation cost — which is less than I previously thought. The role of the cheese in <strong>#9</strong> is to break the compiler optimization — because it initially compiled the function to accept only floating point numbers. Calling it with a different argument forces a recompilation. That one millionth and one call costs as much as hundred thousand compiled iterations. Still, this is clearly not as expensive as you might have believed — V8 recompilation is very fast.</p><p>Now to go back to my initial observation.</p><p>In fact, unlike <em>scijs/ndarray-ops</em>, which uses the slightly slower <strong>#1</strong> form, <em>scijs/cwise</em> uses the most efficient form possible — <strong>#2</strong>.</p><p>In fact, when using <strong>#2</strong> on x86–64 — as is the case of <em>scijs/cwise</em>, V8 produces this absolutely amazing machine translation which has only 2 memory accesses per iteration and keeps everything in a register:</p><pre>movapd  %xmm1,%xmm2        # x has been previously loaded in xmm1<br>addsd   %xmm1,%xmm2        # x+x = 2*x in xmm1<br>movapd  %xmm1,%xmm3        # the previous add is still running<br>mulsd   %xmm1,%xmm3        # x*x in xmm3<br>addsd   %xmm2,%xmm3        # x*x+2*x in xmm3<br>lea     (%r11,%rax,1),%rdx # calculate output offset<br>addsd   %xmm0,%xmm3        # constant xmm0=1, x*x+2*x+1 in xmm3<br>movsd   %xmm3,(%rdx,%r15,8) # store the result</pre><p>+2*x has been transformed to +x+x which is slightly faster and avoids using a constant that must be loaded into an FPU register. Instructions are correctly interleaved to take advantage of the super-scalar architecture. And — something I previously believed was absent from V8 — instead of being loaded at each iteration, the constant <em>1</em> is being held throughout the loop in a static register — xmm0. This is clearly indicative of a very good register optimization — unlike the example from my previous story.</p><p>In fact, this piece of machine code is equivalent to what <em>gcc </em>or <em>clang </em>would produce with -O3 maximum optimization.<strong> Yes, on this particular example, when using floating point numbers, V8 runs with the same speed as highly optimized C++ code.</strong> Integer would have added a few more JOinstructions for exception handling.</p><p>Remember to tell this to everyone who still intends to rewrite <em>scijs </em>in C++.</p><p>Or to whoever told you that JavaScript was ill-suited for data-mining.</p><p><a href="https://github.com/mmomtchev">mmomtchev - Overview</a></p><p><em>I am a hungry engineer who is currently unemployed and turned open source developer because my ex-employers are extorting me to cover up a remarkably ugly sex scandal. I do mostly Node.js and V8 stuff.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8be824f858cd" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>