<p dir="auto">I find constructors taking keyword arguments are a lot slower than constructors that don't.  For example,</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="julia&gt; immutable PositionalArg
           x::Int
           PositionalArg(x::Int=1) = new(x)
       end

julia&gt; immutable KeywordArg
           x::Int
           KeywordArg(;x::Int=1) = new(x)
       end

julia&gt; using BenchmarkTools

julia&gt; @benchmark PositionalArg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.087 ns (0.00% GC)
  median time:      2.092 ns (0.00% GC)
  mean time:        2.381 ns (0.00% GC)
  maximum time:     386.548 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia&gt; @benchmark KeywordArg()
BenchmarkTools.Trial:
  memory estimate:  16.00 bytes
  allocs estimate:  1
  --------------
  minimum time:     75.253 ns (0.00% GC)
  median time:      78.912 ns (0.00% GC)
  mean time:        101.540 ns (1.62% GC)
  maximum time:     26.617 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     970
  time tolerance:   5.00%
  memory tolerance: 1.00%"><pre class="notranslate"><code class="notranslate">julia&gt; immutable PositionalArg
           x::Int
           PositionalArg(x::Int=1) = new(x)
       end

julia&gt; immutable KeywordArg
           x::Int
           KeywordArg(;x::Int=1) = new(x)
       end

julia&gt; using BenchmarkTools

julia&gt; @benchmark PositionalArg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     2.087 ns (0.00% GC)
  median time:      2.092 ns (0.00% GC)
  mean time:        2.381 ns (0.00% GC)
  maximum time:     386.548 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia&gt; @benchmark KeywordArg()
BenchmarkTools.Trial:
  memory estimate:  16.00 bytes
  allocs estimate:  1
  --------------
  minimum time:     75.253 ns (0.00% GC)
  median time:      78.912 ns (0.00% GC)
  mean time:        101.540 ns (1.62% GC)
  maximum time:     26.617 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     970
  time tolerance:   5.00%
  memory tolerance: 1.00%
</code></pre></div>
<p dir="auto">Such a huge difference seems to be caused by the additional allocations that constructors with keyword arguments use.  However, I'm not sure if that has to be the case, because ordinary functions don't seem to be using additional allocations when taking keyword arguments:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="julia&gt; posarg(x::Int=1) = x
posarg (generic function with 2 methods)

julia&gt; kwdarg(;x::Int=1) = x
kwdarg (generic function with 1 method)

julia&gt; @benchmark posarg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.997 ns (0.00% GC)
  median time:      1.998 ns (0.00% GC)
  mean time:        2.282 ns (0.00% GC)
  maximum time:     95.239 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia&gt; @benchmark kwdarg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.997 ns (0.00% GC)
  median time:      1.998 ns (0.00% GC)
  mean time:        2.042 ns (0.00% GC)
  maximum time:     273.882 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%"><pre class="notranslate"><code class="notranslate">julia&gt; posarg(x::Int=1) = x
posarg (generic function with 2 methods)

julia&gt; kwdarg(;x::Int=1) = x
kwdarg (generic function with 1 method)

julia&gt; @benchmark posarg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.997 ns (0.00% GC)
  median time:      1.998 ns (0.00% GC)
  mean time:        2.282 ns (0.00% GC)
  maximum time:     95.239 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia&gt; @benchmark kwdarg()
BenchmarkTools.Trial:
  memory estimate:  0.00 bytes
  allocs estimate:  0
  --------------
  minimum time:     1.997 ns (0.00% GC)
  median time:      1.998 ns (0.00% GC)
  mean time:        2.042 ns (0.00% GC)
  maximum time:     273.882 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
  time tolerance:   5.00%
  memory tolerance: 1.00%
</code></pre></div>
<p dir="auto">Is this a matter that can be fixed easily, or I need to refrain from using keyword arguments in constructors?</p>
<p dir="auto">Here is the version info:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="julia&gt; versioninfo()
Julia Version 0.6.0-dev.1636
Commit 99b6a8c* (2016-12-20 13:46 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin16.0.0)
  CPU: Intel(R) Core(TM)2 Duo CPU     T9900  @ 3.06GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Penryn)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.7.1 (ORCJIT, penryn)"><pre class="notranslate"><code class="notranslate">julia&gt; versioninfo()
Julia Version 0.6.0-dev.1636
Commit 99b6a8c* (2016-12-20 13:46 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin16.0.0)
  CPU: Intel(R) Core(TM)2 Duo CPU     T9900  @ 3.06GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Penryn)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.7.1 (ORCJIT, penryn)
</code></pre></div>
<p dir="auto">The script that demonstrate this can be found <a href="https://github.com/yuyichao/explore/blob/master/julia/kw_forward/kw_forward.jl">here</a>. Pasted below</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#!/usr/bin/julia -f

f() = nothing

forward_nothing(func) = func()
forward_pos_only(func, args...) = func(args...)
forward_kw(func, args...; kws...) = func(args...; kws...)

do_sth(a, b, c) = begin
    global g = a * b * c
end

macro time_forward(ex)
    quote
        println($(Expr(:quote, ex)))
        gc()
        @time for i in 1:10000000
            $(esc(ex))
        end
    end
end

@time_forward forward_nothing(f)
@time_forward forward_pos_only(f)
@time_forward forward_kw(f)
@time_forward do_sth(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
@time_forward do_sth(1, 2, 3)
@time_forward do_sth(1.2, 2.3, 3.4)
@time_forward (do_sth(1, 2, 3); do_sth(1.2, 2.3, 3.4))
"><pre class="notranslate"><span class="pl-c"><span class="pl-c">#</span>!/usr/bin/julia -f</span>

<span class="pl-en">f</span>() <span class="pl-k">=</span> <span class="pl-c1">nothing</span>

<span class="pl-en">forward_nothing</span>(func) <span class="pl-k">=</span> <span class="pl-c1">func</span>()
<span class="pl-en">forward_pos_only</span>(func, args<span class="pl-k">...</span>) <span class="pl-k">=</span> <span class="pl-c1">func</span>(args<span class="pl-k">...</span>)
<span class="pl-en">forward_kw</span>(func, args<span class="pl-k">...</span>; kws<span class="pl-k">...</span>) <span class="pl-k">=</span> <span class="pl-c1">func</span>(args<span class="pl-k">...</span>; kws<span class="pl-k">...</span>)

<span class="pl-en">do_sth</span>(a, b, c) <span class="pl-k">=</span> <span class="pl-k">begin</span>
    <span class="pl-k">global</span> g <span class="pl-k">=</span> a <span class="pl-k">*</span> b <span class="pl-k">*</span> c
<span class="pl-k">end</span>

<span class="pl-k">macro</span> <span class="pl-en">time_forward</span>(ex)
    <span class="pl-k">quote</span>
        <span class="pl-c1">println</span>(<span class="pl-k">$</span>(<span class="pl-c1">Expr</span>(<span class="pl-c1">:quote</span>, ex)))
        <span class="pl-c1">gc</span>()
        <span class="pl-c1">@time</span> <span class="pl-k">for</span> i <span class="pl-k">in</span> <span class="pl-c1">1</span><span class="pl-k">:</span><span class="pl-c1">10000000</span>
            <span class="pl-k">$</span>(<span class="pl-c1">esc</span>(ex))
        <span class="pl-k">end</span>
    <span class="pl-k">end</span>
<span class="pl-k">end</span>

<span class="pl-c1">@time_forward</span> <span class="pl-c1">forward_nothing</span>(f)
<span class="pl-c1">@time_forward</span> <span class="pl-c1">forward_pos_only</span>(f)
<span class="pl-c1">@time_forward</span> <span class="pl-c1">forward_kw</span>(f)
<span class="pl-c1">@time_forward</span> <span class="pl-c1">do_sth</span>(<span class="pl-s"><span class="pl-pds">"</span>a<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>b<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>c<span class="pl-pds">"</span></span>)
<span class="pl-c1">@time_forward</span> <span class="pl-c1">do_sth</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>)
<span class="pl-c1">@time_forward</span> <span class="pl-c1">do_sth</span>(<span class="pl-c1">1.2</span>, <span class="pl-c1">2.3</span>, <span class="pl-c1">3.4</span>)
<span class="pl-c1">@time_forward</span> (<span class="pl-c1">do_sth</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>); <span class="pl-c1">do_sth</span>(<span class="pl-c1">1.2</span>, <span class="pl-c1">2.3</span>, <span class="pl-c1">3.4</span>))
</pre></div>
<p dir="auto">The output of the script on my laptop</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="forward_nothing(f)
elapsed time: 0.019369954 seconds (0 bytes allocated)
forward_pos_only(f)
elapsed time: 6.2e-8 seconds (0 bytes allocated)
forward_kw(f)
elapsed time: 0.95345393 seconds (960000000 bytes allocated, 33.34% gc time)
do_sth(&quot;a&quot;,&quot;b&quot;,&quot;c&quot;)
elapsed time: 1.014413397 seconds (800000000 bytes allocated, 28.80% gc time)
do_sth(1,2,3)
elapsed time: 0.04512597 seconds (0 bytes allocated)
do_sth(1.2,2.3,3.4)
elapsed time: 0.125168819 seconds (160000000 bytes allocated, 37.53% gc time)
begin 
    do_sth(1,2,3)
    do_sth(1.2,2.3,3.4)
end
elapsed time: 0.157331348 seconds (160000000 bytes allocated, 30.24% gc time)"><pre class="notranslate"><span class="pl-c1">forward_nothing</span>(f)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">0.019369954</span> seconds (<span class="pl-c1">0</span> bytes allocated)
<span class="pl-c1">forward_pos_only</span>(f)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">6.2e-8</span> seconds (<span class="pl-c1">0</span> bytes allocated)
<span class="pl-c1">forward_kw</span>(f)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">0.95345393</span> seconds (<span class="pl-c1">960000000</span> bytes allocated, <span class="pl-c1">33.34</span><span class="pl-k">%</span> gc time)
<span class="pl-c1">do_sth</span>(<span class="pl-s"><span class="pl-pds">"</span>a<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>b<span class="pl-pds">"</span></span>,<span class="pl-s"><span class="pl-pds">"</span>c<span class="pl-pds">"</span></span>)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">1.014413397</span> seconds (<span class="pl-c1">800000000</span> bytes allocated, <span class="pl-c1">28.80</span><span class="pl-k">%</span> gc time)
<span class="pl-c1">do_sth</span>(<span class="pl-c1">1</span>,<span class="pl-c1">2</span>,<span class="pl-c1">3</span>)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">0.04512597</span> seconds (<span class="pl-c1">0</span> bytes allocated)
<span class="pl-c1">do_sth</span>(<span class="pl-c1">1.2</span>,<span class="pl-c1">2.3</span>,<span class="pl-c1">3.4</span>)
elapsed time<span class="pl-k">:</span> <span class="pl-c1">0.125168819</span> seconds (<span class="pl-c1">160000000</span> bytes allocated, <span class="pl-c1">37.53</span><span class="pl-k">%</span> gc time)
<span class="pl-k">begin</span> 
    <span class="pl-c1">do_sth</span>(<span class="pl-c1">1</span>,<span class="pl-c1">2</span>,<span class="pl-c1">3</span>)
    <span class="pl-c1">do_sth</span>(<span class="pl-c1">1.2</span>,<span class="pl-c1">2.3</span>,<span class="pl-c1">3.4</span>)
<span class="pl-k">end</span>
elapsed time<span class="pl-k">:</span> <span class="pl-c1">0.157331348</span> seconds (<span class="pl-c1">160000000</span> bytes allocated, <span class="pl-c1">30.24</span><span class="pl-k">%</span> gc time)</pre></div>
<p dir="auto">There are several interesting things here,</p>
<ol dir="auto">
<li>It seems that the compiler can inline <code class="notranslate">f(args...)</code> and figure out it is useless and eliminate the loop altogether (I don't think my cpu can excute a million loops in tens of nanosecond) but it cannot do it for <code class="notranslate">f()</code>. This particular case isn't very interesting but I'm wondering if there's any missing optimizations here.</li>
<li>Forwarding keyword argument is very costy, even when there's nothing to be forwarded (probably a big part of it is memory allocation?). Although it would be nice to make keyword argument fast in general, the slowness of forwarding empty keyword argument means that it is very costy to support keyword arguments. One example (also how I discover this issue) is a <a href="https://github.com/JuliaLang/julia/issues/7045#issuecomment-68486528" data-hovercard-type="issue" data-hovercard-url="/JuliaLang/julia/issues/7045/hovercard">version of <code class="notranslate">invoke</code> that supports keyword arguments</a>. This version is ~3 times slower than the builtin invoke and a version without keyword argument support and simply forward to the builtin <code class="notranslate">invoke</code> is as fast as the builtin one.</li>
<li>Just for comparison, I've also included some other random functions and it seems that keyword arguments are much more expensive than global variables and is as expensive as concatenating strings. What I don't quite understand either is that why is it necessary to allocate memory when assigning floating point numbers to a global variable but not for integers? (Edit: it seems that the integer version does no allocation because of the cache of boxed value.)</li>
</ol>
<p dir="auto">Anothing thing I've noticed is that the extra keyword argument got by the functions is in a different format (<code class="notranslate">Array{(Symbol, Any), 1}</code>) from the format of argument (<code class="notranslate">Array{Any, 1}</code>) to the <code class="notranslate">.env.kwsorter</code>. Is it necessary to do this conversion? This probably won't affect the performance for empty forwarding but feels inefficient in general.</p>
<p dir="auto">Test done on current master as of a few hours ago.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="julia&gt; versioninfo()
Julia Version 0.4.0-dev+2369
Commit bab9ec5* (2015-01-02 01:47 UTC)
Platform Info:
  System: Linux (x86_64-unknown-linux-gnu)
  CPU: Intel(R) Core(TM) i7-4702HQ CPU @ 2.20GHz
  WORD_SIZE: 64
  BLAS: libblas
  LAPACK: liblapack
  LIBM: libm
  LLVM: libLLVM-3.3"><pre class="notranslate"><code class="notranslate">julia&gt; versioninfo()
Julia Version 0.4.0-dev+2369
Commit bab9ec5* (2015-01-02 01:47 UTC)
Platform Info:
  System: Linux (x86_64-unknown-linux-gnu)
  CPU: Intel(R) Core(TM) i7-4702HQ CPU @ 2.20GHz
  WORD_SIZE: 64
  BLAS: libblas
  LAPACK: liblapack
  LIBM: libm
  LLVM: libLLVM-3.3
</code></pre></div>
<p dir="auto">The llvm version installed is 3.5 and I have no idea why it prints llvm-3.3....</p>