{"id":489,"date":"2017-06-06T17:50:49","date_gmt":"2017-06-06T17:50:49","guid":{"rendered":"http:\/\/www.max-sperling.bplaced.net\/?p=489"},"modified":"2024-10-01T11:13:25","modified_gmt":"2024-10-01T11:13:25","slug":"function-pointer-vs-stdfunction-c","status":"publish","type":"post","link":"http:\/\/www.max-sperling.bplaced.net\/?p=489","title":{"rendered":"Function pointer vs. std::function vs Function template"},"content":{"rendered":"<p><strong>Showcase<\/strong><\/p>\n<details>\n<summary>Code \u25bc<\/summary>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n#include &lt;chrono&gt;\r\n#include &lt;functional&gt;\r\n#include &lt;iostream&gt;\r\n\r\n\/\/ Function pointer\r\nvoid func_1(void (*f)()) { f(); }\r\n\r\n\/\/ std::function\r\nvoid func_2(const std::function&lt;void()&gt;&amp; f) { f(); }\r\n\r\n\/\/ Function template\r\nvoid func_3(std::regular_invocable auto&amp;&amp; f) { f(); }\r\n\/\/ Before C++20 it had to be written like this:\r\n\/\/ template &lt;typename T&gt; concept Callable = requires(T&amp;&amp; t) { { t() }; };\r\n\/\/ template &lt;Callable F&gt; void func_3(F&amp;&amp; f) { f(); }\r\n\r\nint main()\r\n{\r\n    using namespace std::chrono;\r\n\r\n    {\r\n        auto start = high_resolution_clock::now();\r\n        void (*f)() = [](){};\r\n        for (auto i = 0; i &lt; 10000000; ++i) { func_1(f); }\r\n        auto stop = high_resolution_clock::now();\r\n\r\n        auto ms = duration_cast&lt;milliseconds&gt;(stop - start);\r\n        std::cout &lt;&lt; &quot;Function pointer: Time=&quot;\r\n                  &lt;&lt; ms.count() &lt;&lt; &quot;ms&quot; &lt;&lt; std::endl;\r\n    }\r\n    {\r\n        auto start = high_resolution_clock::now();\r\n        std::function&lt;void()&gt; f = [](){};\r\n        for (auto i = 0; i &lt; 10000000; ++i) { func_2(f); }\r\n        auto stop = high_resolution_clock::now();\r\n\r\n        auto ms = duration_cast&lt;milliseconds&gt;(stop - start);\r\n        std::cout &lt;&lt; &quot;std::function: Time=&quot;\r\n                  &lt;&lt; ms.count() &lt;&lt; &quot;ms&quot; &lt;&lt; std::endl;\r\n    }\r\n    {\r\n        auto start = high_resolution_clock::now();\r\n        auto f = [](){};\r\n        for (auto i = 0; i &lt; 10000000; ++i) { func_3(f); }\r\n        auto stop = high_resolution_clock::now();\r\n\r\n        auto ms = duration_cast&lt;milliseconds&gt;(stop - start);\r\n        std::cout &lt;&lt; &quot;Function template: Time=&quot;\r\n                  &lt;&lt; ms.count() &lt;&lt; &quot;ms&quot; &lt;&lt; std::endl;\r\n    }\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<\/details>\n<details>\n<summary>Compiler \u25bc<\/summary>\n<p>&#8211; Tool: godbolt.org<br \/>\n&#8211; Compiler: x86-64 Clang 16.0.0<br \/>\n&#8211; Args: -std=c++20<br \/>\n&#8211; Output: Intel asm syntax and Demangle identifiers<br \/>\n<\/details>\n<details>\n<summary>Result \u25bc<\/summary>\n<pre>\r\nFunction pointer: Time=30ms\r\nstd::function: Time=102ms\r\nFunction template: Time=17ms\r\n<\/pre>\n<\/details>\n<hr>\n<p><strong>Analysis<\/strong><\/p>\n<details>\n<summary>Function Pointer \u25bc<\/summary>\n<pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\r\n        mov     dword ptr [rbp - 36], 0\r\n.LBB4_1:\r\n        cmp     dword ptr [rbp - 36], 10000000\r\n        jge     .LBB4_4\r\n        mov     rdi, qword ptr [rbp - 24]\r\n        call    func_1(void (*)())\r\n        mov     eax, dword ptr [rbp - 36]\r\n        add     eax, 1\r\n        mov     dword ptr [rbp - 36], eax\r\n        jmp     .LBB4_1\r\n\r\nfunc_1(void (*)()):\r\n        push    rbp\r\n        mov     rbp, rsp\r\n        sub     rsp, 16\r\n        mov     qword ptr [rbp - 8], rdi\r\n        call    qword ptr [rbp - 8]\r\n        add     rsp, 16\r\n        pop     rbp\r\n        ret\r\n<\/pre>\n<p>When calling f() we have to go throw one indirection:<br \/>\n&#8211;> &#8220;call qword ptr [rbp &#8211; 8]&#8221; (Dereferencing the function pointer.)<br \/>\n<\/details>\n<details>\n<summary>std::function \u25bc<\/summary>\n<pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\r\n        mov     dword ptr [rbp - 116], 0\r\n.LBB4_5:\r\n        cmp     dword ptr [rbp - 116], 10000000\r\n        jge     .LBB4_10\r\n        lea     rdi, [rbp - 104]\r\n        call    func_2(std::function&lt;void ()&gt; const&amp;)\r\n        jmp     .LBB4_7\r\n.LBB4_7:\r\n        jmp     .LBB4_8\r\n.LBB4_8:\r\n        mov     eax, dword ptr [rbp - 116]\r\n        add     eax, 1\r\n        mov     dword ptr [rbp - 116], eax\r\n        jmp     .LBB4_5\r\n\r\nfunc_2(std::function&lt;void ()&gt; const&amp;):\r\n        push    rbp\r\n        mov     rbp, rsp\r\n        sub     rsp, 16\r\n        mov     qword ptr [rbp - 8], rdi\r\n        mov     rdi, qword ptr [rbp - 8]\r\n        call    std::function&lt;void ()&gt;::operator()() const\r\n        add     rsp, 16\r\n        pop     rbp\r\n        ret\r\n<\/pre>\n<p>When calling f() we have to go throw the std::function call logic:<br \/>\n&#8211;> &#8220;call std::function<void ()>::operator()() const&#8221;<br \/>\n<\/details>\n<details>\n<summary>Function template \u25bc<\/summary>\n<pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\r\n        mov     dword ptr [rbp - 180], 0\r\n.LBB4_18:\r\n        cmp     dword ptr [rbp - 180], 10000000\r\n        jge     .LBB4_21\r\n        lea     rdi, [rbp - 176]\r\n        call    void func_3&lt;main::$_1&amp;&gt;(main::$_1&amp;)\r\n        mov     eax, dword ptr [rbp - 180]\r\n        add     eax, 1\r\n        mov     dword ptr [rbp - 180], eax\r\n        jmp     .LBB4_18\r\n\r\nvoid func_3&lt;main::$_1&amp;&gt;(main::$_1&amp;):\r\n        push    rbp\r\n        mov     rbp, rsp\r\n        sub     rsp, 16\r\n        mov     qword ptr [rbp - 8], rdi\r\n        mov     rdi, qword ptr [rbp - 8]\r\n        call    main::$_1::operator()() const\r\n        add     rsp, 16\r\n        pop     rbp\r\n        ret\r\n<\/pre>\n<p>When calling f() we have to go throw zero indirections:<br \/>\n&#8211;> &#8220;call main::$_1::operator()() const&#8221;<br \/>\n<\/details>\n<hr>\n<p>Disclaimer: This code has been compiled without optimization!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Showcase Code \u25bc Compiler \u25bc &#8211; Tool: godbolt.org &#8211; Compiler: x86-64 Clang 16.0.0 &#8211; Args: -std=c++20 &#8211; Output: Intel asm<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false},"categories":[27],"tags":[],"_links":{"self":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/489"}],"collection":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=489"}],"version-history":[{"count":22,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/489\/revisions"}],"predecessor-version":[{"id":17863,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/489\/revisions\/17863"}],"wp:attachment":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=489"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=489"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}