# Core.tuple() warntype

**URL:** <https://discourse.julialang.org/t/core-tuple-warntype/33375>\
**Category:** Performance\
**Created:** [January 15, 2020, 2:15am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375 "2020-01-15T02:15:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 2:15am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/1 "2020-01-15T02:15:16Z")

</div>

I’m studying `Tuple`s and find that (in `base/tuple.jl`) a lot of methods to create them (e.g. `map()`, `NTuple{}()`, etc.) at the end would return something like `(x..., )`, which calls `Core.tuple()`.

However, I found that this call gives warntype as below:

```julia
julia> a = collect(1:16);

julia> f() = (a..., )
f (generic function with 3 methods)

julia> @code_warntype f()
Variables
  #self#::Core.Compiler.Const(f, false)
Body::Tuple
1 ─ %1 = Core._apply(Core.tuple, Main.a)::Tuple
└── return %1

```

and seems like `Core.tupe()` is so basic that could not be inspected:

```julia
julia> @code_warntype tuple(a)
ERROR: ArgumentError: argument is not a generic function
julia> @edit tuple(a)
ERROR: ArgumentError: argument is not a generic function

```

so, how could we create a tuple without any warntype? Thanks.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [January 15, 2020, 2:37am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/2 "2020-01-15T02:37:45Z")

</div>

`a` is a non-constant global variable, so there’s no way it can be inferred. It’s also a vector, so the length of the resulting tuple isn’t known by the compiler. Fixing those issues solves the problem:

```julia
julia> const b = (1, 2, 3)
(1, 2, 3)

julia> f() = (b...,)
f (generic function with 1 method)

julia> @code_warntype f()
Variables
  #self#::Core.Compiler.Const(f, false)

Body::Tuple{Int64,Int64,Int64}
1 ─ %1 = Core._apply(Core.tuple, Main.b)::Core.Compiler.Const((1, 2, 3), false)
└── return %1

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 4:12am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/3 "2020-01-15T04:12:04Z")

</div>

what about this?

```julia
julia> const nt16 = (1:16..., )
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)

julia> @code_warntype map(x -> x + 1, nt16)
Variables
...
  @_6::Union{Nothing, Tuple{Int64,Int64}}
Body::Tuple
...
4 ┄ %20 = Core._apply(Core.tuple, A)::Tuple
└── return %20

```

now even I define `mymap()`, it still gives warntype:

```julia
function mymap(f, t::NTuple{N, T} ) where {T, N}
    A = Vector{T}(undef, N) # A now is Vector{T} rather than Vector{Any}
    for i=1:N
        A[i] = f(t[i])
    end
    (A..., ) # warntype
    # NTuple{N, T}(A) # also warntype
end

julia> @code_warntype mymap(x -> x + 1, nt16)
Variables
...
  @_5::Union{Nothing, Tuple{Int64,Int64}}
Body::Tuple{Vararg{Int64,N} where N}
...
4 ┄ %19 = Core._apply(Core.tuple, A)::Tuple{Vararg{Int64,N} where N}
└── return %19

```

---

<div class="post-metadata">

**Author:** ![aviatesk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aviatesk/32/7610_2.png) [@aviatesk](https://discourse.julialang.org/u/aviatesk)\
**Post date:** [January 15, 2020, 4:48am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/4 "2020-01-15T04:48:31Z")

</div>

> [@tomtom](#):
>
> seems like `Core.tupe()` is so basic that could not be inspected:

For getting inferred _return type_ of builtin functions, you can check

```julia
Core.Compiler.return_type(Core.tuple, Tuple{Int, Int}) # => Tuple{Int64, Int64}

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 6:13am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/5 "2020-01-15T06:13:21Z")

</div>

the problem now is calling `(a..., )` **inside a function** would give warntype when a is a **`Vector`** (NOT a `Tuple`). It happens inside `map()` (see `base\tuple.jl` line 186).

using a `SizedVector` instead of a `Vector`, and using `StaticArrays.Tuple()` instead of `Core.tuple()` would avoid warntype:

```julia
using StaticArrays
function mymap2(f, t::Base.All16{T, N} ) where {T, N}
    N_ = 16 + N
    A = SizedVector{N_, T}(undef)
    for i=1:N_
        A[i] = f(t[i])
    end
    # (A..., ) # warntype
    Tuple(A) # NO warntype
end

julia> const nt1000 = (1:1000..., );
julia> @code_warntype mymap2(x -> x + 1, nt1000); # NO warntype

```

okay, now I’m thinking ways of avoiding whatever vector: to use recursion of recursions like:

```julia
mymap3(f, t::Tuple{}) = () # same as map()
mymap3(f, t::Tuple{Any,}) = (f(t[1]),) # same as map()
mymap3(f, t::Tuple{Any, Any}) = (f(t[1]), f(t[2])) # same as map()
mymap3(f, t::Tuple{Any, Any, Any}) = (f(t[1]), f(t[2]), f(t[3])) # same as map()
mymap3(f, t::Tuple) = (Base.@_inline_meta; (f(t[1] ), mymap3(f,Base.tail(t) )... ) )

argheadtail(x1, x2, x3, x4,
            x5, x6, x7, x8,
            x9, x10, x11, x12,
            x13, x14, x15,
            rest...) = ((x1, x2, x3, x4,
                         x5, x6, x7, x8,
                         x9, x10, x11, x12,
                         x13, x14, x15),
                         rest)
headtail(x::Tuple) = argheadtail(x...)
mymap3(f, t::Base.All16) = (Base.@_inline_meta; 
                            (head, tail) = headtail(t);
                            (mymap3(f, head)..., mymap3(f, tail)... ) )

julia> map(x->x+1,nt1000)===mymap2(x->x+1,nt1000)===mymap3(x->x+1,nt1000)
true
julia> @code_warntype mymap3(x -> x + 1, nt1000); # NO warntype

```

finally, `mymap3()` avoids any warntype.

now, check the performance:

```julia
julia> @btime map(x -> x + 1, $nt1000); # Vector & Core.tuple()
  25.667 μs (494 allocations: 39.34 KiB)

julia> @btime mymap2(x -> x + 1, $nt1000); # SizedVector & Tuple()
  1.413 μs (1 allocation: 7.94 KiB)

julia> @btime mymap3(x -> x + 1, $nt1000); # recursion of recursions
  2.118 ms (50195 allocations: 2.59 MiB)

```

`mymap2()` has no warntype and is much faster! 😀  
however, even `mymap3()` also avoids warntype, somehow it’s much slower!!!

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 15, 2020, 7:35am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/6 "2020-01-15T07:35:47Z")

</div>

If you’re looking for maximum compilation effort to minimize runtime, then how about:

```julia
mymap4(f, t::Tuple) = ntuple(i -> f(t[i]), Val(length(t)))

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 7:41am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/7 "2020-01-15T07:41:19Z")

</div>

wow! thanks! `mymap4()` has no warntype and is the fastest so far: 😀

```julia
julia> @btime map(x -> x + 1, $nt1000);
  25.183 μs (494 allocations: 39.34 KiB)

julia> @btime mymap2(x -> x + 1, $nt1000);
  1.390 μs (1 allocation: 7.94 KiB)

julia> @btime mymap3(x -> x + 1, $nt1000);
  2.118 ms (50195 allocations: 2.59 MiB)

julia> @btime mymap4(x -> x + 1, $nt1000);
  575.929 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 15, 2020, 7:45am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/8 "2020-01-15T07:45:50Z")

</div>

Yes, but note that it ends up in a generated function, so using it on a length-1000 tuple is probably not a good idea in practice. (You risk filling up your code cache.)

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 7:51am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/9 "2020-01-15T07:51:45Z")

</div>

… I have difficulty in understanding what is a “generated function”?

actually, I came across with `ntuple(, ::Val)` and could not understand. Below is the definition from `base\ntuple.jl` line 45:

```julia
@inline function ntuple(f::F, ::Val{N}) where {F,N}
    N::Int
    (N >= 0) || throw(ArgumentError(string("tuple length should be ≥ 0, got ", N)))
    if @generated
        quote
            @nexprs $N i -> t_i = f(i)
            @ncall $N tuple t
        end
    else
        Tuple(f(i) for i = 1:N)
    end
end

```

could you be nice enough to teach me:

1. what is `if @generated` ??? how could a macro used after `if`, also, nothing after `@generated`?
2. what do the codes mean inside `quote` … `end` ???

thanks!

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 15, 2020, 8:22am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/10 "2020-01-15T08:22:11Z")

</div>

That’s an “optionally generated function.” It’s explained here: [Metaprogramming · The Julia Language](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1) (last section)

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [January 15, 2020, 9:18am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/11 "2020-01-15T09:18:17Z")

</div>

Because of compiler specialization, tuples aren’t really recommended for long “vectors.” StaticArrays.jl explicitly warns you against using it for large arrays, and Base uses the `Any16` mechanism to explicitly “punt” on inferrable tuple manipulations for long tuples. It can be fun to play around with this stuff but in real life I would recommend against going beyond the `Any16` limit unless you really know what you are doing. (For example, if very much Julia code did such manipulations on very long tuples, you could easily increase our current “time to first plot” by orders of magnitude.)

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 1:26pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/12 "2020-01-15T13:26:16Z")

</div>

> [@tim.holy](#):
>
> Base uses the `Any16` mechanism to explicitly “punt” on inferrable tuple manipulations for long tuples.

(what does “punt” actually mean?)  
In the context of `map()` at least, I don’t think that the introduction of `Any16` was intended to “discourage” the use of long tuples. Rather, it’s **essential to avoid stack overflow** caused by deep recursions.

That said, if we omit the method `function map(f, t::Any16) ... end` and rely solely on the recursion `map(f, t::Tuple) = (@_inline_meta; (f(t[1]), map(f,tail(t))...))`, stack overflow will throw when a long tuple (say, 2000 elements) is used.

That’s why I had an attempt `mymap3()` that breaks a single deep recursions into multiple shallow recursions. It’s slow though.

> [@tim.holy](#):
>
> Because of compiler specialization, tuples aren’t really recommended for long “vectors.”

why? and how? could I know more in depth about this issue? thanks.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 15, 2020, 1:48pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/13 "2020-01-15T13:48:25Z")

</div>

> [@tomtom](#):
>
> (what does “punt” actually mean?)

[http://www.catb.org/~esr/jargon/html/P/punt.html](http://www.catb.org/~esr/jargon/html/P/punt.html)

> [@tomtom](#):
>
> why? and how? could I know more in depth about this issue? thanks.

Mostly because a specialized version would be compiled for every length, which is usually not what you want.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 15, 2020, 2:15pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/14 "2020-01-15T14:15:37Z")

</div>

> [@Tamas\_Papp](#):
>
> Mostly because a specialized version would be compiled for every length, which is usually not what you want.

… I don’t understand… actually Julia is always compiling a specialized version for each (combination) of type variables … now, if we treat the length as a (static) type variable also, it’s just the usual case… what’s exceptional then?

of course, if the program is manipulating a lot of tuples with all different lengths, that may be a problem. But then it’s a problem of “number of different lengths”, not the problem of “long length”?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [January 15, 2020, 2:41pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/15 "2020-01-15T14:41:20Z")

</div>

The point being that with the constructs you’re playing with here, “long length” leads directly to “many different lengths.” Most tuple algorithms are written recursively.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 16, 2020, 3:10pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/16 "2020-01-16T15:10:48Z")

</div>

> help?\> @nospecialize  
> @nospecialize  
> Applied to a function argument name, hints to the compiler that the method should not be specialized for different types of that argument, but instead to use precisely the declared type for each argument.

please help… I could not understand what the above says… could anyone help to give some examples? thanks.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 16, 2020, 5:01pm UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/17 "2020-01-16T17:01:41Z")

</div>

See

[https://docs.julialang.org/en/v1/devdocs/functions/#Compiler-efficiency-issues-1](https://docs.julialang.org/en/v1/devdocs/functions/#Compiler-efficiency-issues-1)

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 17, 2020, 3:24am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/18 "2020-01-17T03:24:57Z")

</div>

I had read the doc but could not understand… 😢 😫

to be precise, what the **difference** if the original definition:

```julia
length(@nospecialize t::Tuple) = nfields(t)

```

being replaced by:

```julia
length(t::Tuple) = nfields(t)

```

?

thanks.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 17, 2020, 6:29am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/19 "2020-01-17T06:29:46Z")

</div>

Generally, `@nospecialize` will prevent producing a compiled version for each tuple type. Though for this particular function, I believe it is mostly inlined anyway in practice.

The discussion is getting away from your original question. While knowing about `@nospecialize` is useful for advanced users of Julia, I am not sure that getting deep into the internals is useful, unless you work on the low-level parts of `Base` or the compiler.

IMO 99% of Julia users can make productive use of the language without every using `@nospecialize`.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [January 17, 2020, 6:46am UTC](https://discourse.julialang.org/t/core-tuple-warntype/33375/20 "2020-01-17T06:46:14Z")

</div>

> [@Tamas\_Papp](#):
>
> The discussion is getting away from your original question.

Not at all, as we’ve discussed that using long tuples would introduce a lot of methods each for a different length of tuple.

> [@Tamas\_Papp](#):
>
> unless you work on the low-level parts of `Base` or the compiler.

That’s what I’m trying hard to do. So I went into `base\tuple.jl` and found a lot of `@nospecialize`, which seems to be related to the generation of the method table.

so… could you be nice enough to give an **example** that show the **difference** between using vs. not using `@nospecialize`? many thanks 😵

[Next page](https://discourse.julialang.org/t/core-tuple-warntype/33375.md?page=2)
